Main.elm 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. module Main exposing (..)
  2. import Browser exposing (Document)
  3. import Card exposing (CardData)
  4. import Database
  5. import Draft exposing (Draft)
  6. import Html exposing (Html, button, div, img, span, text)
  7. import Html.Attributes exposing (alt, class, classList, disabled, src)
  8. import Html.Events as Events exposing (onClick)
  9. import Html.Keyed as Keyed
  10. import Round
  11. import Zipper
  12. main =
  13. Browser.document
  14. { init = init
  15. , update = update
  16. , view = view
  17. , subscriptions = \_ -> Sub.none
  18. }
  19. type Model
  20. = Ready ReadyModel
  21. | Error ErrorModel
  22. type FocusStat
  23. = FocusALSA
  24. | FocusALPA
  25. | FocusGIHWR
  26. | FocusPickRate
  27. type alias ReadyModel =
  28. { draft : Draft
  29. , database : Database.Database
  30. , highlighted : Maybe Draft.PickCard
  31. , flipHighlighted : Bool
  32. , focusStat : FocusStat
  33. }
  34. type alias ErrorModel =
  35. { error : String
  36. }
  37. init : { setData : String, draftData : String } -> ( Model, Cmd Msg )
  38. init flags =
  39. case ( Draft.decode flags.draftData, Database.decode flags.setData flags.draftData ) of
  40. ( Ok draftData, Ok database ) ->
  41. ( Ready
  42. { draft = draftData
  43. , database = database
  44. , highlighted = Nothing
  45. , flipHighlighted = False
  46. , focusStat = FocusPickRate
  47. }
  48. , Cmd.none
  49. )
  50. ( Err err, Ok _ ) ->
  51. ( Error { error = "Error decoding draft data: " ++ err }, Cmd.none )
  52. ( Ok _, Err err ) ->
  53. ( Error { error = "Error decoding set data: " ++ err }, Cmd.none )
  54. ( Err draftError, Err databaseError ) ->
  55. ( Error { error = "Error decoding draft data: " ++ draftError ++ ", set data: " ++ databaseError }, Cmd.none )
  56. type Msg
  57. = Increment
  58. | Decrement
  59. | Highlight Draft.PickCard
  60. | FlipHighlightedCard
  61. | SetFocusStat FocusStat
  62. update : Msg -> Model -> ( Model, Cmd Msg )
  63. update msg model =
  64. case model of
  65. Ready mdl ->
  66. case msg of
  67. Increment ->
  68. ( Ready { mdl | draft = Zipper.moveRight mdl.draft }, Cmd.none )
  69. Decrement ->
  70. ( Ready { mdl | draft = Zipper.moveLeft mdl.draft }, Cmd.none )
  71. Highlight card ->
  72. ( Ready
  73. { mdl
  74. | highlighted = Just card
  75. , flipHighlighted = False
  76. }
  77. , Cmd.none
  78. )
  79. FlipHighlightedCard ->
  80. ( Ready { mdl | flipHighlighted = not mdl.flipHighlighted }, Cmd.none )
  81. SetFocusStat stat ->
  82. ( Ready { mdl | focusStat = stat }, Cmd.none )
  83. Error mdl ->
  84. ( Error mdl, Cmd.none )
  85. view : Model -> Document Msg
  86. view model =
  87. { title = "Drafter"
  88. , body =
  89. [ case model of
  90. Ready mdl ->
  91. viewReady mdl
  92. Error mdl ->
  93. viewError mdl
  94. ]
  95. }
  96. viewReady : ReadyModel -> Html Msg
  97. viewReady model =
  98. div [ class "grid grid-cols-12 gap-6 h-full bg-slate-100" ]
  99. [ viewSidebar model
  100. , viewDraft model
  101. , viewHighlightedCard model
  102. ]
  103. viewSidebar : ReadyModel -> Html Msg
  104. viewSidebar model =
  105. div [ class "col-span-2 p-6 bg-slate-600" ]
  106. [ button
  107. [ onClick Decrement
  108. , disabled (not (Zipper.hasLeft model.draft))
  109. , class "bg-slate-900 rounded text-white p-2 shadow disabled:opacity-50"
  110. ]
  111. [ text "Previous" ]
  112. , span [ class "text-white px-3 font-medium" ] [ text ("Pick " ++ String.fromInt (Zipper.position model.draft)) ]
  113. , button
  114. [ onClick Increment
  115. , class "bg-slate-900 rounded text-white p-2 shadow disabled:opacity-50"
  116. , disabled (not (Zipper.hasRight model.draft))
  117. ]
  118. [ text "Next" ]
  119. ]
  120. viewError : ErrorModel -> Html Msg
  121. viewError model =
  122. div [ class "error" ]
  123. [ text model.error ]
  124. viewDraft : ReadyModel -> Html Msg
  125. viewDraft model =
  126. let
  127. chosen =
  128. (Zipper.focus model.draft).chosen
  129. picks =
  130. List.map (\c -> viewKeyedCard model (chosen.name == c.name) c)
  131. (Zipper.focus model.draft).available
  132. viewFocusStatButton s =
  133. button
  134. [ onClick (SetFocusStat s)
  135. , classList [ ( "bg-slate-900 rounded text-white p-2 shadow mr-2 mb-2", True ), ( "bg-slate-500 shadow-inset", model.focusStat == s ) ]
  136. ]
  137. [ text <|
  138. case s of
  139. FocusPickRate ->
  140. "Pick rate"
  141. FocusALPA ->
  142. "ALPA"
  143. FocusGIHWR ->
  144. "GIHWR"
  145. FocusALSA ->
  146. "ALSA"
  147. ]
  148. in
  149. -- Making this a keyed node forces Elm to recreate each card when the card name changes,
  150. -- so the old image doesn't hang around during loading of the new card image
  151. div [ class "flex flex-col space-between col-span-8 " ]
  152. [ Keyed.node "div"
  153. [ class "flex-grow grid grid-cols-8 auto-rows-min gap-6 p-6" ]
  154. picks
  155. , div []
  156. [ viewFocusStatButton FocusPickRate
  157. , viewFocusStatButton FocusALPA
  158. , viewFocusStatButton FocusGIHWR
  159. , viewFocusStatButton FocusALSA
  160. ]
  161. ]
  162. viewHighlightedCard : ReadyModel -> Html Msg
  163. viewHighlightedCard model =
  164. div [ class "col-span-2 shadow-xl bg-slate-600 p-6" ]
  165. [ case model.highlighted of
  166. Just highlighted ->
  167. let
  168. url =
  169. case ( model.flipHighlighted, highlighted.backImage ) of
  170. ( True, Just backUrl ) ->
  171. backUrl
  172. _ ->
  173. highlighted.frontImage
  174. in
  175. div
  176. []
  177. [ img
  178. [ src url
  179. , alt highlighted.name
  180. , onClick FlipHighlightedCard
  181. ]
  182. []
  183. , viewCardDetailedPerformance model.database highlighted
  184. ]
  185. Nothing ->
  186. div [] []
  187. ]
  188. formatPercentage : Float -> String
  189. formatPercentage value =
  190. Round.round 2 (value * 100) ++ "%"
  191. viewCardDetailedPerformance : Database.Database -> Draft.PickCard -> Html Msg
  192. viewCardDetailedPerformance db { name } =
  193. let
  194. makeCardFacts : CardData -> List ( String, String )
  195. makeCardFacts cardData =
  196. [ ( "Pick rate"
  197. , Just <|
  198. formatPercentage (Card.pickRate cardData)
  199. )
  200. , ( "GIHWR"
  201. , Maybe.map formatPercentage (Card.gihwr cardData)
  202. )
  203. , ( "ALPA"
  204. , Maybe.map (Round.round 2) (Card.alpa cardData)
  205. )
  206. , ( "ALSA"
  207. , Maybe.map (Round.round 2) (Card.alsa cardData)
  208. )
  209. ]
  210. |> List.filterMap (\( label, value ) -> Maybe.map (\v -> ( label, v )) value)
  211. viewFact : ( String, String ) -> Html Msg
  212. viewFact ( label, value ) =
  213. div
  214. [ class "" ]
  215. [ div [] [ text label ]
  216. , div [] [ text value ]
  217. ]
  218. in
  219. case Database.get name db of
  220. Just cardData ->
  221. div
  222. [ class "" ]
  223. (List.map viewFact (makeCardFacts cardData))
  224. Nothing ->
  225. div [] [ text "Could not find card in database" ]
  226. viewKeyedCard : ReadyModel -> Bool -> Draft.PickCard -> ( String, Html Msg )
  227. viewKeyedCard model wasChosen { name, frontImage, backImage } =
  228. let
  229. stats =
  230. Database.get name model.database
  231. focusStat =
  232. case model.focusStat of
  233. FocusALSA ->
  234. Maybe.map (Round.round 2) (Maybe.andThen Card.alsa stats)
  235. FocusALPA ->
  236. Maybe.map (Round.round 2) (Maybe.andThen Card.alpa stats)
  237. FocusGIHWR ->
  238. Maybe.map formatPercentage (Maybe.andThen Card.gihwr stats)
  239. FocusPickRate ->
  240. Maybe.map (formatPercentage << Card.pickRate) stats
  241. in
  242. ( name
  243. , div
  244. [ classList
  245. [ ( "relative", True )
  246. , ( "border-4 border-green-500", wasChosen )
  247. ]
  248. , Events.onMouseEnter (Highlight { name = name, frontImage = frontImage, backImage = backImage })
  249. ]
  250. [ img [ src frontImage, alt name ] []
  251. , span [ class "absolute top-0 left-0 bg-green-100" ] [ text <| Maybe.withDefault "?" focusStat ]
  252. ]
  253. )