404 lines
10 KiB
Elm
404 lines
10 KiB
Elm
module UI exposing
|
|
( Dimensions
|
|
, cardRow
|
|
, colors
|
|
, footer
|
|
, getViewport
|
|
, isMobile
|
|
, layout
|
|
, manaSpinner
|
|
, pageNotFound
|
|
, priceBadge
|
|
, subtitle
|
|
, text
|
|
, title
|
|
)
|
|
|
|
import Browser.Dom
|
|
import Card
|
|
import Color
|
|
import Element as E
|
|
import Element.Background as Background
|
|
import Element.Border as Border
|
|
import Element.Font as Font
|
|
import Maybe.Extra
|
|
import Round
|
|
import Spinner
|
|
import Symbol
|
|
import Task
|
|
import Url.Builder
|
|
|
|
|
|
type alias Dimensions =
|
|
{ width : Int
|
|
, height : Int
|
|
}
|
|
|
|
|
|
colors =
|
|
let
|
|
blue =
|
|
E.rgb255 100 100 255
|
|
|
|
slate =
|
|
E.rgb255 150 150 200
|
|
|
|
lighterGrey =
|
|
E.rgb255 60 60 60
|
|
|
|
lightGrey =
|
|
E.rgb255 50 50 50
|
|
|
|
grey =
|
|
E.rgb255 40 40 40
|
|
|
|
darkGrey =
|
|
E.rgb255 30 30 30
|
|
|
|
darkerGrey =
|
|
E.rgb255 20 20 20
|
|
|
|
white =
|
|
E.rgb255 255 255 255
|
|
|
|
offwhite =
|
|
E.rgb255 200 200 200
|
|
|
|
mythic =
|
|
E.rgb255 205 55 0
|
|
|
|
rare =
|
|
E.rgb255 218 165 32
|
|
|
|
uncommon =
|
|
E.rgb255 112 128 144
|
|
|
|
common =
|
|
E.rgb255 47 79 79
|
|
in
|
|
{ primary = blue
|
|
, secondary = slate
|
|
, background = lightGrey
|
|
, navBar = darkerGrey
|
|
, sidebar = lighterGrey
|
|
, selected = darkGrey
|
|
, hover = grey
|
|
, title = white
|
|
, subtitle = offwhite
|
|
, text = offwhite
|
|
, mythic = mythic
|
|
, rare = rare
|
|
, uncommon = uncommon
|
|
, common = common
|
|
, mana =
|
|
{ white =
|
|
{ fg = E.rgb255 249 250 244
|
|
, bg = E.rgb255 248 231 185
|
|
}
|
|
, black =
|
|
{ fg = E.rgb255 21 11 0
|
|
, bg = E.rgb255 166 159 157
|
|
}
|
|
, blue =
|
|
{ fg = E.rgb255 14 104 171
|
|
, bg = E.rgb255 179 206 234
|
|
}
|
|
, green =
|
|
{ fg = E.rgb255 0 114 62
|
|
, bg = E.rgb255 196 211 202
|
|
}
|
|
, red =
|
|
{ fg = E.rgb255 211 32 42
|
|
, bg = E.rgb255 235 159 130
|
|
}
|
|
, generic =
|
|
{ fg = E.rgb255 190 190 190
|
|
, bg = E.rgb255 250 250 250
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
getViewport : (Dimensions -> msg) -> Cmd msg
|
|
getViewport msg =
|
|
Task.perform
|
|
(\x ->
|
|
msg
|
|
{ width = floor x.viewport.width
|
|
, height = floor x.viewport.height
|
|
}
|
|
)
|
|
Browser.Dom.getViewport
|
|
|
|
|
|
isMobile : E.Device -> Bool
|
|
isMobile device =
|
|
case device.orientation of
|
|
E.Landscape ->
|
|
False
|
|
|
|
E.Portrait ->
|
|
True
|
|
|
|
|
|
manaSpinner : Spinner.Config
|
|
manaSpinner =
|
|
let
|
|
color index =
|
|
if index < 1.0 then
|
|
Color.red
|
|
|
|
else if index < 2.0 then
|
|
Color.green
|
|
|
|
else if index < 3.0 then
|
|
Color.purple
|
|
|
|
else if index < 4.0 then
|
|
Color.blue
|
|
|
|
else
|
|
Color.white
|
|
|
|
default =
|
|
Spinner.defaultConfig
|
|
in
|
|
{ default
|
|
| lines = 5.0
|
|
, length = 0.0
|
|
, width = 20
|
|
, color = color
|
|
}
|
|
|
|
|
|
text : String -> E.Element msg
|
|
text string =
|
|
E.el [ Font.color colors.text ] <| E.text string
|
|
|
|
|
|
title : String -> E.Element msg
|
|
title string =
|
|
E.el [ Font.color colors.title ] <| E.text string
|
|
|
|
|
|
subtitle : String -> E.Element msg
|
|
subtitle string =
|
|
E.el [ Font.size 16, Font.italic, Font.color colors.subtitle ] <| E.text string
|
|
|
|
|
|
priceBadge : { currency : String, amount : Float } -> E.Element msg
|
|
priceBadge { currency, amount } =
|
|
E.el
|
|
[ Border.rounded 5
|
|
, Border.color colors.text
|
|
, E.width <| E.px 60
|
|
, E.padding 2
|
|
, Font.family [ Font.typeface "sans" ]
|
|
, Font.size 10
|
|
]
|
|
<|
|
|
E.row [ E.width E.fill ]
|
|
[ E.el [ E.width <| E.fillPortion 1 ] <| E.text <| String.toUpper currency
|
|
, E.el [ E.width <| E.fillPortion 2, Font.alignRight ] <| E.text <| Round.round 2 amount
|
|
]
|
|
|
|
|
|
cardRow : { foil : Bool, subtitle : String } -> List (E.Attribute msg) -> Symbol.Table -> Card.Card -> E.Element msg
|
|
cardRow options attributes symbols card =
|
|
let
|
|
badge color foil string =
|
|
E.el
|
|
[ Border.rounded 5
|
|
, Border.color color
|
|
, Border.width 1
|
|
, E.width <| E.px 60
|
|
, Font.family [ Font.typeface "sans" ]
|
|
, Font.size 10
|
|
, Font.color colors.title
|
|
]
|
|
<|
|
|
E.row [ E.height E.fill, E.width E.fill ]
|
|
[ E.el [ E.padding 2, E.width E.fill ] <| E.text string
|
|
, E.row [ E.padding 1, E.height E.fill, E.width E.fill, Background.color color ]
|
|
[ if foil then
|
|
E.el
|
|
[ E.width E.fill
|
|
, E.height E.fill
|
|
, Background.gradient
|
|
{ angle = 4.0
|
|
, steps =
|
|
[ E.rgb 148 0 211
|
|
, E.rgb 75 0 130
|
|
, E.rgb 0 0 255
|
|
, E.rgb 0 255 0
|
|
, E.rgb 255 255 0
|
|
, E.rgb 255 127 0
|
|
, E.rgb 255 0 0
|
|
]
|
|
}
|
|
]
|
|
E.none
|
|
|
|
else
|
|
E.none
|
|
]
|
|
]
|
|
|
|
setBadge : Bool -> E.Element msg
|
|
setBadge isFoil =
|
|
let
|
|
color =
|
|
case card.rarity of
|
|
"mythic" ->
|
|
colors.mythic
|
|
|
|
"rare" ->
|
|
colors.rare
|
|
|
|
"uncommon" ->
|
|
colors.uncommon
|
|
|
|
_ ->
|
|
colors.common
|
|
in
|
|
badge color isFoil card.setCode
|
|
|
|
prices =
|
|
Maybe.Extra.values
|
|
[ Maybe.map (\usd -> { currency = "usd", amount = usd }) <|
|
|
Maybe.Extra.or card.prices.usd card.prices.usd_foil
|
|
, Maybe.map (\eur -> { currency = "eur", amount = eur }) <|
|
|
Maybe.Extra.or card.prices.eur card.prices.eur_foil
|
|
, Maybe.map (\tix -> { currency = "tix", amount = tix }) card.prices.tix
|
|
]
|
|
in
|
|
E.row
|
|
(List.append
|
|
[ E.width E.fill
|
|
, E.spacing 10
|
|
, E.padding 3
|
|
, E.mouseOver [ Background.color colors.hover ]
|
|
]
|
|
attributes
|
|
)
|
|
[ 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", card.scryfallId ]
|
|
[ Url.Builder.string "format" "image"
|
|
, Url.Builder.string "version" "art_crop"
|
|
]
|
|
, description = card.name
|
|
}
|
|
, E.column [ E.centerY, E.height E.fill, E.width E.fill, E.clipX ]
|
|
[ Symbol.text symbols 12 card.manaCost
|
|
, E.el [ Font.color colors.title ] <| E.text card.name
|
|
, E.el [ Font.size 16, Font.italic, Font.color colors.subtitle ] <| E.text options.subtitle
|
|
]
|
|
, E.column [ E.alignRight, E.height E.fill ] <|
|
|
setBadge options.foil
|
|
:: List.map priceBadge prices
|
|
]
|
|
|
|
|
|
pageNotFound : E.Element msg
|
|
pageNotFound =
|
|
E.column [ E.centerX, E.centerY, E.spacing 20 ]
|
|
[ E.el [ E.centerX, Font.size 320, Font.heavy ] <| text "404"
|
|
, E.paragraph [ E.centerX, Font.center ]
|
|
[ subtitle "The page you requested could not be found" ]
|
|
]
|
|
|
|
|
|
footer : E.Element msg -> E.Element msg
|
|
footer element =
|
|
E.el
|
|
[ E.height (E.px 50)
|
|
, E.width E.fill
|
|
, E.clipX
|
|
, E.padding 10
|
|
, Font.color colors.text
|
|
, Background.color colors.navBar
|
|
, E.alignBottom
|
|
]
|
|
element
|
|
|
|
|
|
type alias Page msg =
|
|
{ toolbar : Maybe (E.Element msg)
|
|
, sidebar : Maybe (E.Element msg)
|
|
, content : E.Element msg
|
|
, footer : Maybe (E.Element msg)
|
|
}
|
|
|
|
|
|
layout : E.Device -> Page msg -> E.Element msg
|
|
layout device page =
|
|
let
|
|
toolbar : E.Element msg -> E.Element msg
|
|
toolbar element =
|
|
E.el
|
|
[ E.padding 10
|
|
, E.spacing 10
|
|
, E.width E.fill
|
|
, Background.color colors.navBar
|
|
]
|
|
element
|
|
|
|
sidebar : E.Element msg -> E.Element msg
|
|
sidebar element =
|
|
if isMobile device then
|
|
E.el
|
|
[ E.height E.fill
|
|
, E.width E.fill
|
|
, E.scrollbarY
|
|
, Background.color colors.sidebar
|
|
]
|
|
element
|
|
|
|
else
|
|
E.el
|
|
[ E.alignTop
|
|
, E.width <| E.fillPortion 1
|
|
, E.height E.fill
|
|
, E.scrollbarY
|
|
, Background.color colors.sidebar
|
|
]
|
|
element
|
|
|
|
content : E.Element msg -> E.Element msg
|
|
content element =
|
|
E.el
|
|
[ E.width <| E.fillPortion 3
|
|
, E.height E.fill
|
|
]
|
|
element
|
|
|
|
maybe : Maybe (E.Element msg) -> E.Element msg
|
|
maybe element =
|
|
Maybe.withDefault E.none element
|
|
in
|
|
E.column
|
|
[ E.width E.fill
|
|
, E.height E.fill
|
|
]
|
|
[ maybe <| Maybe.map toolbar page.toolbar
|
|
, if isMobile device then
|
|
Maybe.withDefault page.content page.sidebar
|
|
|
|
else
|
|
E.row
|
|
[ E.width E.fill
|
|
, E.height E.fill
|
|
, Font.color colors.text
|
|
, E.scrollbarY
|
|
]
|
|
[ maybe <| Maybe.map sidebar page.sidebar
|
|
, content page.content
|
|
]
|
|
, footer <| maybe page.footer
|
|
]
|