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 ]