Add a card detail view
This commit is contained in:
parent
b12d0c62ca
commit
b0fff226ea
4 changed files with 99 additions and 32 deletions
|
@ -90,6 +90,7 @@ class SearchHandler(tornado.web.RequestHandler):
|
|||
"color_identity": tutor.models.Color.to_string(
|
||||
card.color_identity
|
||||
),
|
||||
"oracle_text": card.oracle_text,
|
||||
}
|
||||
for card in cards[:limit]
|
||||
]
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
<head>
|
||||
<title>Bulk Tagging Dashboard</title>
|
||||
<script type="text/javascript" src="elm.js"></script>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Libre+Baskerville&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
<meta charset="utf-8"/>
|
||||
<body>
|
||||
|
|
122
www/src/App.elm
122
www/src/App.elm
|
@ -10,6 +10,7 @@ import Dict
|
|||
import Element as E
|
||||
import Element.Background as Background
|
||||
import Element.Border as Border
|
||||
import Element.Events as Events
|
||||
import Element.Font as Font
|
||||
import Element.Input as Input
|
||||
import Html.Events
|
||||
|
@ -24,7 +25,7 @@ import Url.Parser exposing ((</>), (<?>))
|
|||
import Url.Parser.Query
|
||||
|
||||
|
||||
type alias Window =
|
||||
type alias Dimensions =
|
||||
{ width : Int
|
||||
, height : Int
|
||||
}
|
||||
|
@ -38,22 +39,25 @@ type alias Criteria =
|
|||
|
||||
type alias Model =
|
||||
{ navigationKey : Browser.Navigation.Key
|
||||
, viewport : Window
|
||||
, viewport : Dimensions
|
||||
, spinner : Spinner.Model
|
||||
, criteria : Criteria
|
||||
, cardPage : CardPage
|
||||
, activeCard : Maybe Card.Card
|
||||
}
|
||||
|
||||
|
||||
type Msg
|
||||
= UrlChanged Url.Url
|
||||
| ViewportChanged Window
|
||||
| ViewportChanged Dimensions
|
||||
| LinkClicked Browser.UrlRequest
|
||||
| SpinnerMsg Spinner.Msg
|
||||
| UpdateCriteria CriteriaMsg
|
||||
| Search
|
||||
| GetPage Url.Url
|
||||
| FoundCards (Result Http.Error (Paginated.Page Card.Card))
|
||||
| ShowCardDetails Card.Card
|
||||
| ClearCardDetails
|
||||
|
||||
|
||||
type CriteriaMsg
|
||||
|
@ -181,6 +185,7 @@ init _ url key =
|
|||
, spinner = Spinner.init
|
||||
, criteria = criteria
|
||||
, cardPage = Loading Paginated.empty
|
||||
, activeCard = Nothing
|
||||
}
|
||||
, Cmd.batch
|
||||
[ search criteria
|
||||
|
@ -262,6 +267,12 @@ update msg model =
|
|||
FoundCards (Err _) ->
|
||||
( model, Cmd.none )
|
||||
|
||||
ShowCardDetails card ->
|
||||
( { model | activeCard = Just card }, Cmd.none )
|
||||
|
||||
ClearCardDetails ->
|
||||
( { model | activeCard = Nothing }, Cmd.none )
|
||||
|
||||
|
||||
colors =
|
||||
let
|
||||
|
@ -288,36 +299,51 @@ viewCardBrowser : Model -> E.Element Msg
|
|||
viewCardBrowser model =
|
||||
let
|
||||
cardWidth =
|
||||
-- 50% of the Scryfall border_crop image width
|
||||
240
|
||||
-- 30% of the Scryfall border_crop image width (480)
|
||||
144
|
||||
|
||||
cardHeight =
|
||||
-- 50% of the Scryfall border_crop image height
|
||||
340
|
||||
-- 30% of the Scryfall border_crop image height (680)
|
||||
204
|
||||
|
||||
cardSpacing =
|
||||
10
|
||||
5
|
||||
|
||||
navigationButtonWidth =
|
||||
100
|
||||
availableWidth =
|
||||
(model.viewport.width // 5) * 4
|
||||
|
||||
cardColumns =
|
||||
-- Either 3, 6, or 9, based on viewport width
|
||||
-- Either 6 or 9, based on viewport width
|
||||
let
|
||||
availableColumns =
|
||||
(model.viewport.width - (2 * navigationButtonWidth))
|
||||
availableWidth
|
||||
// (cardWidth + cardSpacing)
|
||||
in
|
||||
(max 3 >> min 9) (availableColumns // 3 * 3)
|
||||
(max 6 >> min 9) (availableColumns // 3 * 3)
|
||||
|
||||
cardRows =
|
||||
18 // cardColumns
|
||||
|
||||
viewCard cardModel =
|
||||
E.el [ Border.rounded 10, E.clip, E.width <| E.px cardWidth, E.height <| E.px cardHeight ] <|
|
||||
minInfoWidth =
|
||||
1230 // 5
|
||||
|
||||
minBrowserWidth =
|
||||
minInfoWidth * 4
|
||||
|
||||
viewCard : Dimensions -> Card.Card -> E.Element Msg
|
||||
viewCard dimensions cardModel =
|
||||
E.el
|
||||
[ Border.rounded 10
|
||||
, E.clip
|
||||
, E.width <| E.px dimensions.width
|
||||
, E.height <| E.px dimensions.height
|
||||
, Events.onMouseEnter <| ShowCardDetails cardModel
|
||||
, Events.onMouseLeave <| ClearCardDetails
|
||||
]
|
||||
<|
|
||||
E.image
|
||||
[ E.width <| E.px cardWidth
|
||||
, E.height <| E.px cardHeight
|
||||
[ E.width <| E.px dimensions.width
|
||||
, E.height <| E.px dimensions.height
|
||||
, E.behindContent <|
|
||||
E.html <|
|
||||
Spinner.view manaSpinner model.spinner
|
||||
|
@ -336,15 +362,27 @@ viewCardBrowser model =
|
|||
Just url ->
|
||||
Input.button
|
||||
[ E.height E.fill
|
||||
, E.width (E.px navigationButtonWidth)
|
||||
, E.width E.fill
|
||||
, Background.color colors.primary
|
||||
, Border.rounded 5
|
||||
, Font.color colors.text
|
||||
, Font.center
|
||||
]
|
||||
{ label = E.text text, onPress = Just (GetPage url) }
|
||||
|
||||
Nothing ->
|
||||
E.el [ E.width (E.px navigationButtonWidth) ] E.none
|
||||
E.el [ E.width E.fill ] E.none
|
||||
|
||||
cardDetails card =
|
||||
E.column
|
||||
[ E.spacing 20
|
||||
, E.padding 10
|
||||
]
|
||||
<|
|
||||
E.el [ E.centerX ] (viewCard { width = 192, height = 272 } card)
|
||||
:: E.paragraph [ Font.heavy, Font.size 24, Font.center ] [ E.text card.name ]
|
||||
:: List.map (\text -> E.paragraph [ Font.size 16 ] [ E.text text ])
|
||||
(String.lines card.oracleText)
|
||||
in
|
||||
case model.cardPage of
|
||||
Failed ->
|
||||
|
@ -371,19 +409,39 @@ viewCardBrowser model =
|
|||
|
||||
Ready cardPage ->
|
||||
E.row
|
||||
[ E.width E.fill
|
||||
, E.height (E.px (cardRows * (cardHeight + cardSpacing)))
|
||||
, E.centerX
|
||||
]
|
||||
<|
|
||||
[ navButton "←" cardPage.prev
|
||||
, E.wrappedRow
|
||||
[ E.width (E.px (cardColumns * (cardWidth + cardSpacing)))
|
||||
, E.centerX
|
||||
, E.spacing cardSpacing
|
||||
[ E.centerX
|
||||
, E.height (E.px (3 * (cardHeight + cardSpacing)))
|
||||
, Font.family
|
||||
[ Font.typeface
|
||||
"Libre Baskerville"
|
||||
, Font.serif
|
||||
]
|
||||
]
|
||||
[ E.column
|
||||
[ E.height E.fill
|
||||
, E.width <| E.px minInfoWidth
|
||||
, Font.color colors.text
|
||||
]
|
||||
[ Maybe.map cardDetails model.activeCard |> Maybe.withDefault E.none ]
|
||||
, E.column
|
||||
[ E.width (E.fill |> E.minimum minBrowserWidth)
|
||||
, E.height E.fill
|
||||
, E.spacing 10
|
||||
]
|
||||
<|
|
||||
[ E.wrappedRow
|
||||
[ E.width (E.px (cardColumns * (cardWidth + cardSpacing)))
|
||||
, E.spacing cardSpacing
|
||||
, E.paddingEach { left = cardSpacing // 2, top = 0, bottom = 0, right = 0 }
|
||||
, E.centerX
|
||||
, E.centerY
|
||||
]
|
||||
(List.map (viewCard { width = cardWidth, height = cardHeight }) cardPage.values)
|
||||
, E.row [ E.width E.fill, E.spacing 20 ]
|
||||
[ navButton "←" cardPage.prev
|
||||
, navButton "→" cardPage.next
|
||||
]
|
||||
]
|
||||
(List.map viewCard cardPage.values)
|
||||
, navButton "→" cardPage.next
|
||||
]
|
||||
|
||||
|
||||
|
@ -409,7 +467,7 @@ view model =
|
|||
{ title = "Tutor"
|
||||
, body =
|
||||
[ E.layout [ Background.color colors.background ] <|
|
||||
E.column [ E.width E.fill, E.spacing 20 ]
|
||||
E.column [ E.width (E.fill |> E.minimum 1280), E.spacing 20 ]
|
||||
[ E.row
|
||||
[ E.padding 10
|
||||
, E.spacing 10
|
||||
|
|
|
@ -9,6 +9,7 @@ type alias Card =
|
|||
, name : String
|
||||
, setCode : String
|
||||
, rarity : String
|
||||
, oracleText : String
|
||||
}
|
||||
|
||||
|
||||
|
@ -19,3 +20,7 @@ decode =
|
|||
|> JDP.required "name" Json.Decode.string
|
||||
|> JDP.required "set_code" Json.Decode.string
|
||||
|> JDP.required "rarity" Json.Decode.string
|
||||
|> JDP.required "oracle_text"
|
||||
(Json.Decode.nullable Json.Decode.string
|
||||
|> Json.Decode.map (Maybe.withDefault "")
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue