Switch the UI to a scrollable list

This commit is contained in:
Correl Roush 2022-02-10 23:05:51 -05:00
parent bc24b89d25
commit 00616067c8
2 changed files with 179 additions and 145 deletions

View file

@ -292,57 +292,83 @@ colors =
blue = blue =
E.rgb255 100 100 255 E.rgb255 100 100 255
grey = lighterGrey =
E.rgb255 60 60 60
lightGrey =
E.rgb255 50 50 50 E.rgb255 50 50 50
grey =
E.rgb255 40 40 40
darkGrey = darkGrey =
E.rgb255 30 30 30
darkerGrey =
E.rgb255 20 20 20 E.rgb255 20 20 20
white = white =
E.rgb255 255 255 255 E.rgb255 255 255 255
in in
{ primary = blue { primary = blue
, background = grey , background = lightGrey
, navBar = darkGrey , navBar = darkerGrey
, sidebar = lighterGrey
, selected = darkGrey
, hover = grey
, text = white , text = white
} }
searchBar : Model -> E.Element Msg
searchBar model =
E.row
[ E.padding 10
, E.spacing 10
, E.width (E.fill |> E.minimum 800)
, Background.color colors.navBar
]
[ Input.text
[ onEnter Search
, Background.color colors.background
, Font.color colors.text
, E.width E.fill
]
{ onChange = UpdateCriteria << UpdateName
, text = model.criteria.query
, placeholder = Nothing
, label = Input.labelHidden "Search Input"
}
, Input.button
[ Background.color colors.primary
, Font.color colors.text
, Border.rounded 10
, E.padding 10
]
{ onPress = Just Search
, label = E.text "Search"
}
, Input.radio [ E.padding 10 ]
{ onChange = UpdateCriteria << UpdateSortBy
, selected = Just model.criteria.sortBy
, label = Input.labelLeft [ Font.color colors.text ] (E.text "Sort by")
, options =
[ Input.option "rarity" <| E.el [ Font.color colors.text ] <| E.text "Rarity DESC"
, Input.option "price" <| E.el [ Font.color colors.text ] <| E.text "Price DESC"
]
}
, Input.checkbox []
{ onChange = UpdateCriteria << UpdateOwnedOnly
, icon = Input.defaultCheckbox
, checked = model.criteria.ownedOnly
, label = Input.labelRight [ Font.color colors.text ] (E.text "Owned only?")
}
]
viewCardBrowser : Model -> E.Element Msg viewCardBrowser : Model -> E.Element Msg
viewCardBrowser model = viewCardBrowser model =
let let
cardWidth =
-- 30% of the Scryfall border_crop image width (480)
144
cardHeight =
-- 30% of the Scryfall border_crop image height (680)
204
cardSpacing =
5
availableWidth =
(model.viewport.width // 5) * 4
cardColumns =
-- Either 6 or 9, based on viewport width
let
availableColumns =
availableWidth
// (cardWidth + cardSpacing)
in
(max 6 >> min 9) (availableColumns // 3 * 3)
cardRows =
18 // cardColumns
minInfoWidth =
1230 // 5
minBrowserWidth =
minInfoWidth * 4
viewCard : Dimensions -> Card.Card -> E.Element Msg viewCard : Dimensions -> Card.Card -> E.Element Msg
viewCard dimensions cardModel = viewCard dimensions cardModel =
E.el E.el
@ -350,8 +376,6 @@ viewCardBrowser model =
, E.clip , E.clip
, E.width <| E.px dimensions.width , E.width <| E.px dimensions.width
, E.height <| E.px dimensions.height , E.height <| E.px dimensions.height
, Events.onMouseEnter <| ShowCardDetails cardModel
, Events.onMouseLeave <| ClearCardDetails
] ]
<| <|
E.image E.image
@ -370,22 +394,6 @@ viewCardBrowser model =
, description = cardModel.name , description = cardModel.name
} }
navButton text maybeUrl =
case maybeUrl of
Just url ->
Input.button
[ E.height E.fill
, 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.fill ] E.none
priceBadge { currency, amount } = priceBadge { currency, amount } =
E.el E.el
[ Border.solid [ Border.solid
@ -403,6 +411,9 @@ viewCardBrowser model =
, E.el [ E.width <| E.fillPortion 2, Font.alignRight ] <| E.text amount , E.el [ E.width <| E.fillPortion 2, Font.alignRight ] <| E.text amount
] ]
cardInfoWidth =
E.px 300
cardDetails card = cardDetails card =
let let
prices = prices =
@ -419,71 +430,121 @@ viewCardBrowser model =
, E.padding 10 , E.padding 10
] ]
<| <|
E.el [ E.centerX ] (viewCard { width = 192, height = 272 } card) E.el [ E.centerX ]
(viewCard { width = 192, height = 272 } card)
:: (E.row [ E.spacing 5, E.centerX ] <| List.map priceBadge prices) :: (E.row [ E.spacing 5, E.centerX ] <| List.map priceBadge prices)
:: E.paragraph [ Font.heavy, Font.size 24, Font.center ] [ E.text card.name ] :: E.paragraph [ Font.heavy, Font.size 24, Font.center ] [ E.text card.name ]
:: List.map (\text -> E.paragraph [ Font.size 16 ] [ E.text text ]) :: List.map (\text -> E.paragraph [ Font.size 16 ] [ E.text text ])
(String.lines card.oracleText) (String.lines card.oracleText)
cardRow : Maybe Card.Card -> Card.Card -> E.Element Msg
cardRow activeCard cardModel =
let
interactiveAttributes =
if activeCard == Just cardModel then
[ Background.color colors.selected ]
else
[ E.pointer
, E.mouseOver [ Background.color colors.hover ]
, Events.onClick <| ShowCardDetails cardModel
]
in
E.row
([ E.width E.fill
, E.spacing 10
, E.padding 3
]
++ interactiveAttributes
)
[ E.el [ E.width <| E.px 100 ] <|
E.image
[ E.height <| E.px 60
, E.centerX
]
{ src =
Url.Builder.crossOrigin "https://api.scryfall.com"
[ "cards", cardModel.scryfallId ]
[ Url.Builder.string "format" "image"
, Url.Builder.string "version" "art_crop"
]
, description = cardModel.name
}
, E.el [ E.centerY ] <| E.text cardModel.name
]
details =
E.el
[ E.alignTop
, E.width cardInfoWidth
, E.height E.fill
, Background.color colors.sidebar
]
(Maybe.map
cardDetails
model.activeCard
|> Maybe.withDefault
E.none
)
navButton text maybeUrl =
case maybeUrl of
Just url ->
Input.button
[ E.height (E.px 30)
, 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.fill ] E.none
cards cardPage =
E.column
[ E.width E.fill
, E.height E.fill
]
[ E.row
[ E.spacing 5
, E.padding 5
, E.width E.fill
]
[ navButton "<-" cardPage.prev
, navButton "->" cardPage.next
]
, E.column
[ E.width E.fill
, E.height E.fill
, E.scrollbarY
]
<|
List.map (cardRow model.activeCard) cardPage.values
]
in in
case model.cardPage of case model.cardPage of
Failed -> Failed ->
E.none E.none
Loading cardPage -> Loading cardPage ->
E.row E.el
[ E.width E.fill [ E.centerX
, E.height (E.px (cardRows * (cardHeight + cardSpacing)))
, E.centerX
] ]
<| <|
[ navButton "" cardPage.prev E.html <|
, E.el Spinner.view manaSpinner model.spinner
[ E.width (E.px (cardColumns * (cardWidth + cardSpacing)))
, E.centerX
, E.spacing cardSpacing
]
<|
E.html <|
Spinner.view manaSpinner model.spinner
, navButton "" cardPage.next
]
Ready cardPage -> Ready cardPage ->
E.row E.row
[ E.centerX [ E.width E.fill
, E.height (E.px (3 * (cardHeight + cardSpacing))) , E.height E.fill
, Font.family , Font.color colors.text
[ Font.typeface , E.scrollbarY
"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
]
]
] ]
[ details, cards cardPage ]
onEnter : msg -> E.Attribute msg onEnter : msg -> E.Attribute msg
@ -507,51 +568,25 @@ view : Model -> Browser.Document Msg
view model = view model =
{ title = "Tutor" { title = "Tutor"
, body = , body =
[ E.layout [ Background.color colors.background ] <| [ E.layout
E.column [ E.width (E.fill |> E.minimum 1280), E.spacing 20 ] [ Background.color colors.background
[ E.row , E.height E.fill
[ E.padding 10 ]
, E.spacing 10 <|
E.column
[ E.width E.fill
, E.height E.fill
]
[ searchBar model
, viewCardBrowser model
, E.el
[ E.height (E.px 50)
, E.width E.fill , E.width E.fill
, Background.color colors.navBar , Background.color colors.navBar
, E.alignBottom
] ]
[ Input.text <|
[ onEnter Search E.text "footer"
, Background.color colors.background
, Font.color colors.text
, E.width E.fill
]
{ onChange = UpdateCriteria << UpdateName
, text = model.criteria.query
, placeholder = Nothing
, label = Input.labelHidden "Search Input"
}
, Input.button
[ Background.color colors.primary
, Font.color colors.text
, Border.rounded 10
, E.padding 10
]
{ onPress = Just Search
, label = E.text "Search"
}
, Input.radio [ E.padding 10 ]
{ onChange = UpdateCriteria << UpdateSortBy
, selected = Just model.criteria.sortBy
, label = Input.labelLeft [ Font.color colors.text ] (E.text "Sort by")
, options =
[ Input.option "rarity" <| E.el [ Font.color colors.text ] <| E.text "Rarity DESC"
, Input.option "price" <| E.el [ Font.color colors.text ] <| E.text "Price DESC"
]
}
, Input.checkbox []
{ onChange = UpdateCriteria << UpdateOwnedOnly
, icon = Input.defaultCheckbox
, checked = model.criteria.ownedOnly
, label = Input.labelRight [ Font.color colors.text ] (E.text "Owned only?")
}
]
, viewCardBrowser model
] ]
] ]
} }

View file

@ -22,7 +22,6 @@ type alias Card =
, prices : Prices , prices : Prices
} }
decode : Json.Decode.Decoder Card decode : Json.Decode.Decoder Card
decode = decode =
Json.Decode.succeed Card Json.Decode.succeed Card