mirror of
https://github.com/correl/planning-poker.git
synced 2024-11-14 19:19:30 +00:00
Add dark mode
This commit is contained in:
parent
129f249ca2
commit
ad9bd2db95
6 changed files with 252 additions and 97 deletions
|
@ -24,13 +24,30 @@ var room_id = uuid4()
|
||||||
var socket = new Socket("/socket", {params: {player_id: player_id}})
|
var socket = new Socket("/socket", {params: {player_id: player_id}})
|
||||||
socket.connect()
|
socket.connect()
|
||||||
|
|
||||||
|
function getCookie(cname) {
|
||||||
|
var name = cname + "=";
|
||||||
|
var decodedCookie = decodeURIComponent(document.cookie);
|
||||||
|
var ca = decodedCookie.split(';');
|
||||||
|
for(var i = 0; i <ca.length; i++) {
|
||||||
|
var c = ca[i];
|
||||||
|
while (c.charAt(0) == ' ') {
|
||||||
|
c = c.substring(1);
|
||||||
|
}
|
||||||
|
if (c.indexOf(name) == 0) {
|
||||||
|
return c.substring(name.length, c.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
var app = Elm.Main.init({
|
var app = Elm.Main.init({
|
||||||
node: document.getElementById("elm-main"),
|
node: document.getElementById("elm-main"),
|
||||||
flags: {
|
flags: {
|
||||||
player: player_id,
|
player: player_id,
|
||||||
room: room_id,
|
room: room_id,
|
||||||
width: window.innerWidth,
|
width: window.innerWidth,
|
||||||
height: window.innerHeight
|
height: window.innerHeight,
|
||||||
|
theme: getCookie("theme")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -51,6 +68,10 @@ app.ports.joinRoom.subscribe(options => {
|
||||||
channel.push(action.type, action.data)
|
channel.push(action.type, action.data)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Theme changes
|
||||||
|
app.ports.saveTheme.subscribe(theme => {
|
||||||
|
document.cookie = "theme=" + theme
|
||||||
|
})
|
||||||
channel.join()
|
channel.join()
|
||||||
.receive("ok", resp => {
|
.receive("ok", resp => {
|
||||||
console.log("Joined successfully", resp);
|
console.log("Joined successfully", resp);
|
||||||
|
|
|
@ -7,6 +7,7 @@ import Html
|
||||||
import PlanningPokerEntry as Entry
|
import PlanningPokerEntry as Entry
|
||||||
import PlanningPokerNotFound as NotFound
|
import PlanningPokerNotFound as NotFound
|
||||||
import PlanningPokerRoom as Room
|
import PlanningPokerRoom as Room
|
||||||
|
import PlanningPokerUI as UI
|
||||||
import Url exposing (Url)
|
import Url exposing (Url)
|
||||||
import Url.Parser as Parser exposing ((</>), Parser, s, string)
|
import Url.Parser as Parser exposing ((</>), Parser, s, string)
|
||||||
|
|
||||||
|
@ -16,6 +17,7 @@ type alias Flags =
|
||||||
, room : String
|
, room : String
|
||||||
, height : Int
|
, height : Int
|
||||||
, width : Int
|
, width : Int
|
||||||
|
, theme : String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,6 +26,7 @@ type alias Model =
|
||||||
, key : Nav.Key
|
, key : Nav.Key
|
||||||
, player : String
|
, player : String
|
||||||
, room : String
|
, room : String
|
||||||
|
, theme : UI.Theme
|
||||||
, dimensions : { width : Int, height : Int }
|
, dimensions : { width : Int, height : Int }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,12 +51,25 @@ type Msg
|
||||||
|
|
||||||
|
|
||||||
init : Flags -> Url -> Nav.Key -> ( Model, Cmd Msg )
|
init : Flags -> Url -> Nav.Key -> ( Model, Cmd Msg )
|
||||||
init { player, room, width, height } url key =
|
init { player, room, width, height, theme } url key =
|
||||||
|
let
|
||||||
|
parseTheme themeString =
|
||||||
|
case String.toLower themeString of
|
||||||
|
"light" ->
|
||||||
|
UI.Light
|
||||||
|
|
||||||
|
"dark" ->
|
||||||
|
UI.Dark
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
UI.Light
|
||||||
|
in
|
||||||
updateUrl url
|
updateUrl url
|
||||||
{ page = NotFound
|
{ page = NotFound
|
||||||
, key = key
|
, key = key
|
||||||
, player = player
|
, player = player
|
||||||
, room = room
|
, room = room
|
||||||
|
, theme = parseTheme theme
|
||||||
, dimensions = { width = width, height = height }
|
, dimensions = { width = width, height = height }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,14 +120,15 @@ updateUrl : Url -> Model -> ( Model, Cmd Msg )
|
||||||
updateUrl url model =
|
updateUrl url model =
|
||||||
case Parser.parse parser url of
|
case Parser.parse parser url of
|
||||||
Just Entry ->
|
Just Entry ->
|
||||||
toEntry model (Entry.init model.room)
|
toEntry model (Entry.init model.theme model.room)
|
||||||
|
|
||||||
Just (Room id) ->
|
Just (Room id) ->
|
||||||
case model.page of
|
case model.page of
|
||||||
EntryPage entryModel ->
|
EntryPage entryModel ->
|
||||||
toRoom model
|
toRoom model
|
||||||
(Room.init
|
(Room.init
|
||||||
{ id = id
|
{ theme = entryModel.theme
|
||||||
|
, id = id
|
||||||
, player = model.player
|
, player = model.player
|
||||||
, roomName = "Planning Poker"
|
, roomName = "Planning Poker"
|
||||||
, playerName = entryModel.playerName
|
, playerName = entryModel.playerName
|
||||||
|
@ -121,7 +138,8 @@ updateUrl url model =
|
||||||
_ ->
|
_ ->
|
||||||
toRoom model
|
toRoom model
|
||||||
(Room.init
|
(Room.init
|
||||||
{ id = id
|
{ theme = model.theme
|
||||||
|
, id = id
|
||||||
, player = model.player
|
, player = model.player
|
||||||
, roomName = "Planning Poker"
|
, roomName = "Planning Poker"
|
||||||
, playerName = ""
|
, playerName = ""
|
||||||
|
|
|
@ -8,11 +8,13 @@ port module PlanningPokerAPI exposing
|
||||||
, newProfile
|
, newProfile
|
||||||
, reset
|
, reset
|
||||||
, reveal
|
, reveal
|
||||||
|
, updateTheme
|
||||||
, vote
|
, vote
|
||||||
)
|
)
|
||||||
|
|
||||||
import Json.Decode as Decode
|
import Json.Decode as Decode
|
||||||
import Json.Encode as Encode
|
import Json.Encode as Encode
|
||||||
|
import PlanningPokerUI as UI
|
||||||
|
|
||||||
|
|
||||||
type RoomAction
|
type RoomAction
|
||||||
|
@ -28,6 +30,9 @@ port joinRoom : { room : String } -> Cmd msg
|
||||||
port roomActions : Encode.Value -> Cmd msg
|
port roomActions : Encode.Value -> Cmd msg
|
||||||
|
|
||||||
|
|
||||||
|
port saveTheme : String -> Cmd msg
|
||||||
|
|
||||||
|
|
||||||
newProfile : { playerName : String } -> Cmd msg
|
newProfile : { playerName : String } -> Cmd msg
|
||||||
newProfile =
|
newProfile =
|
||||||
NewProfile >> encodeAction >> roomActions
|
NewProfile >> encodeAction >> roomActions
|
||||||
|
@ -74,6 +79,16 @@ encodeAction action =
|
||||||
wrap "reveal" (Encode.object [])
|
wrap "reveal" (Encode.object [])
|
||||||
|
|
||||||
|
|
||||||
|
updateTheme : UI.Theme -> Cmd msg
|
||||||
|
updateTheme theme =
|
||||||
|
case theme of
|
||||||
|
UI.Light ->
|
||||||
|
saveTheme "light"
|
||||||
|
|
||||||
|
UI.Dark ->
|
||||||
|
saveTheme "dark"
|
||||||
|
|
||||||
|
|
||||||
port gotPresenceState : (Decode.Value -> msg) -> Sub msg
|
port gotPresenceState : (Decode.Value -> msg) -> Sub msg
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,8 @@ import PlanningPokerUI as UI
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
{ room : String
|
{ theme : UI.Theme
|
||||||
|
, room : String
|
||||||
, playerName : String
|
, playerName : String
|
||||||
, player : Maybe String
|
, player : Maybe String
|
||||||
, error : Maybe String
|
, error : Maybe String
|
||||||
|
@ -22,12 +23,14 @@ type alias Model =
|
||||||
|
|
||||||
type Msg
|
type Msg
|
||||||
= PlayerNameChanged String
|
= PlayerNameChanged String
|
||||||
|
| UpdateTheme UI.Theme
|
||||||
| CreateRoom
|
| CreateRoom
|
||||||
|
|
||||||
|
|
||||||
init : String -> ( Model, Cmd Msg )
|
init : UI.Theme -> String -> ( Model, Cmd Msg )
|
||||||
init room =
|
init theme room =
|
||||||
( { room = room
|
( { theme = theme
|
||||||
|
, room = room
|
||||||
, playerName = ""
|
, playerName = ""
|
||||||
, player = Nothing
|
, player = Nothing
|
||||||
, error = Nothing
|
, error = Nothing
|
||||||
|
@ -49,40 +52,48 @@ update key msg model =
|
||||||
else
|
else
|
||||||
( model, Cmd.none )
|
( model, Cmd.none )
|
||||||
|
|
||||||
|
UpdateTheme theme ->
|
||||||
|
( { model | theme = theme }, API.updateTheme theme )
|
||||||
|
|
||||||
|
|
||||||
view : Model -> Document Msg
|
view : Model -> Document Msg
|
||||||
view model =
|
view model =
|
||||||
{ title = "Planning Poker"
|
UI.toDocument model.theme
|
||||||
, body = [ layout model ]
|
{ title = "Planning Poker"
|
||||||
}
|
, body = [ layout model ]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
layout : Model -> Html Msg
|
layout : Model -> Element Msg
|
||||||
layout model =
|
layout model =
|
||||||
Element.layout [] <|
|
column [ width fill, height fill, centerY ]
|
||||||
column
|
[ column
|
||||||
[ width fill, centerY, spacing 30 ]
|
[ width fill, centerY, spacing 30 ]
|
||||||
[ el [ centerX ] (text "Oh, hey!")
|
[ el [ centerX ] (text "Oh, hey!")
|
||||||
, el [ centerX ] (text "Tell us who you are")
|
, el [ centerX ] (text "Tell us who you are")
|
||||||
, Input.text [ centerX, width (px 300), UI.onEnter CreateRoom ]
|
, UI.textInput model.theme
|
||||||
|
[ centerX, width (px 300), UI.onEnter CreateRoom ]
|
||||||
{ onChange = PlayerNameChanged
|
{ onChange = PlayerNameChanged
|
||||||
, text = model.playerName
|
, text = model.playerName
|
||||||
, label = Input.labelHidden "Your name"
|
, label = Input.labelHidden "Your name"
|
||||||
, placeholder = Just (Input.placeholder [] (text "Your name"))
|
, placeholder = Just (Input.placeholder [] (text "Your name"))
|
||||||
}
|
}
|
||||||
, el [ centerX ] (text "then")
|
, el [ centerX ] (text "then")
|
||||||
, UI.actionButton [ centerX ]
|
, UI.actionButton model.theme
|
||||||
|
[ centerX ]
|
||||||
{ isActive = not (String.isEmpty model.playerName)
|
{ isActive = not (String.isEmpty model.playerName)
|
||||||
, onPress = CreateRoom
|
, onPress = CreateRoom
|
||||||
, label = text "Make a room!"
|
, label = text "Make a room!"
|
||||||
}
|
}
|
||||||
, el
|
, el
|
||||||
[ centerX
|
[ centerX
|
||||||
, Background.color UI.colors.errorBackground
|
, Background.color (UI.colors model.theme).errorBackground
|
||||||
, padding 20
|
, padding 20
|
||||||
, Font.color UI.colors.errorForeground
|
, Font.color (UI.colors model.theme).errorForeground
|
||||||
, transparent (model.error == Nothing)
|
, transparent (model.error == Nothing)
|
||||||
]
|
]
|
||||||
<|
|
<|
|
||||||
text (Maybe.withDefault " " model.error)
|
text (Maybe.withDefault " " model.error)
|
||||||
]
|
]
|
||||||
|
, column [] [ UI.themePicker model.theme UpdateTheme ]
|
||||||
|
]
|
||||||
|
|
|
@ -22,7 +22,8 @@ import PlanningPokerUI as UI
|
||||||
|
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
{ state : State
|
{ theme : UI.Theme
|
||||||
|
, state : State
|
||||||
, room : Room
|
, room : Room
|
||||||
, player : String
|
, player : String
|
||||||
, playerName : String
|
, playerName : String
|
||||||
|
@ -45,6 +46,7 @@ type Msg
|
||||||
| GotVote (Result Decode.Error ReceivedVote)
|
| GotVote (Result Decode.Error ReceivedVote)
|
||||||
| GotReveal
|
| GotReveal
|
||||||
| GotReset
|
| GotReset
|
||||||
|
| UpdateTheme UI.Theme
|
||||||
|
|
||||||
|
|
||||||
type Presence
|
type Presence
|
||||||
|
@ -89,13 +91,14 @@ type Vote
|
||||||
|
|
||||||
|
|
||||||
init :
|
init :
|
||||||
{ id : String
|
{ theme : UI.Theme
|
||||||
|
, id : String
|
||||||
, player : String
|
, player : String
|
||||||
, roomName : String
|
, roomName : String
|
||||||
, playerName : String
|
, playerName : String
|
||||||
}
|
}
|
||||||
-> ( Model, Cmd Msg )
|
-> ( Model, Cmd Msg )
|
||||||
init { id, player, roomName, playerName } =
|
init { theme, id, player, roomName, playerName } =
|
||||||
let
|
let
|
||||||
room =
|
room =
|
||||||
{ id = id
|
{ id = id
|
||||||
|
@ -115,7 +118,8 @@ init { id, player, roomName, playerName } =
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
in
|
in
|
||||||
( { room = room
|
( { theme = theme
|
||||||
|
, room = room
|
||||||
, state = state
|
, state = state
|
||||||
, player = player
|
, player = player
|
||||||
, playerName = playerName
|
, playerName = playerName
|
||||||
|
@ -217,6 +221,9 @@ update key msg model =
|
||||||
, Cmd.none
|
, Cmd.none
|
||||||
)
|
)
|
||||||
|
|
||||||
|
UpdateTheme theme ->
|
||||||
|
( { model | theme = theme }, API.updateTheme theme )
|
||||||
|
|
||||||
|
|
||||||
view : { height : Int, width : Int } -> Model -> Document Msg
|
view : { height : Int, width : Int } -> Model -> Document Msg
|
||||||
view dimensions model =
|
view dimensions model =
|
||||||
|
@ -231,20 +238,22 @@ view dimensions model =
|
||||||
in
|
in
|
||||||
case model.state of
|
case model.state of
|
||||||
Playing ->
|
Playing ->
|
||||||
UI.toDocument
|
UI.toDocument model.theme
|
||||||
{ title = model.room.name
|
{ title = model.room.name
|
||||||
, body =
|
, body =
|
||||||
[ navBar { title = model.room.name, playerName = playerName }
|
[ navBar model.theme { title = model.room.name, playerName = playerName }
|
||||||
, viewRoom device model
|
, viewRoom device model
|
||||||
|
, UI.themePicker model.theme UpdateTheme
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
Joining ->
|
Joining ->
|
||||||
UI.toDocument
|
UI.toDocument model.theme
|
||||||
{ title = model.room.name
|
{ title = model.room.name
|
||||||
, body =
|
, body =
|
||||||
[ navBar { title = model.room.name, playerName = playerName }
|
[ navBar model.theme { title = model.room.name, playerName = playerName }
|
||||||
, joinForm model.room model.playerName
|
, joinForm model.theme model.room model.playerName
|
||||||
|
, UI.themePicker model.theme UpdateTheme
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +267,7 @@ viewRoom device model =
|
||||||
in
|
in
|
||||||
case device.class of
|
case device.class of
|
||||||
Phone ->
|
Phone ->
|
||||||
column [ width fill, spacing 20 ]
|
column [ width fill, spacing 20, centerY ]
|
||||||
[ viewPlayers (Dict.values model.room.players) model.showVotes
|
[ viewPlayers (Dict.values model.room.players) model.showVotes
|
||||||
, el [ width (fillPortion 3), alignTop ] <|
|
, el [ width (fillPortion 3), alignTop ] <|
|
||||||
viewCards model myVote
|
viewCards model myVote
|
||||||
|
@ -266,7 +275,7 @@ viewRoom device model =
|
||||||
]
|
]
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
column [ width fill, spacing 20 ]
|
column [ width fill, spacing 20, centerY, alignTop ]
|
||||||
[ row
|
[ row
|
||||||
[ width fill ]
|
[ width fill ]
|
||||||
[ el [ width (fillPortion 3), alignTop ] <|
|
[ el [ width (fillPortion 3), alignTop ] <|
|
||||||
|
@ -279,23 +288,16 @@ viewRoom device model =
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
navBar : { title : String, playerName : String } -> Element Msg
|
navBar : UI.Theme -> { title : String, playerName : String } -> Element Msg
|
||||||
navBar { title, playerName } =
|
navBar theme { title, playerName } =
|
||||||
row
|
UI.navBar theme
|
||||||
[ Background.color UI.colors.primary
|
|
||||||
, height (px 50)
|
|
||||||
, width fill
|
|
||||||
, padding 10
|
|
||||||
]
|
|
||||||
[ el
|
[ el
|
||||||
[ Font.alignLeft
|
[ Font.alignLeft
|
||||||
, Font.color UI.colors.background
|
|
||||||
, width fill
|
, width fill
|
||||||
]
|
]
|
||||||
(text title)
|
(text title)
|
||||||
, el
|
, el
|
||||||
[ Font.alignRight
|
[ Font.alignRight
|
||||||
, Font.color UI.colors.background
|
|
||||||
]
|
]
|
||||||
(text playerName)
|
(text playerName)
|
||||||
]
|
]
|
||||||
|
@ -309,10 +311,10 @@ viewCards model selected =
|
||||||
|
|
||||||
selectedColor =
|
selectedColor =
|
||||||
if enabled then
|
if enabled then
|
||||||
UI.colors.selected
|
(UI.colors model.theme).selected
|
||||||
|
|
||||||
else
|
else
|
||||||
UI.colors.disabled
|
(UI.colors model.theme).disabled
|
||||||
|
|
||||||
card value =
|
card value =
|
||||||
Input.button
|
Input.button
|
||||||
|
@ -328,7 +330,7 @@ viewCards model selected =
|
||||||
selectedColor
|
selectedColor
|
||||||
|
|
||||||
else
|
else
|
||||||
UI.colors.background
|
(UI.colors model.theme).background
|
||||||
, Font.size 50
|
, Font.size 50
|
||||||
]
|
]
|
||||||
{ onPress =
|
{ onPress =
|
||||||
|
@ -405,13 +407,13 @@ viewPlayers playerList showVotes =
|
||||||
moderatorTools : Model -> Element Msg
|
moderatorTools : Model -> Element Msg
|
||||||
moderatorTools model =
|
moderatorTools model =
|
||||||
row [ centerX, spacing 20 ]
|
row [ centerX, spacing 20 ]
|
||||||
[ UI.actionButton
|
[ UI.actionButton model.theme
|
||||||
[ centerX ]
|
[ centerX ]
|
||||||
{ isActive = not model.showVotes
|
{ isActive = not model.showVotes
|
||||||
, onPress = Reveal
|
, onPress = Reveal
|
||||||
, label = text "Reveal"
|
, label = text "Reveal"
|
||||||
}
|
}
|
||||||
, UI.actionButton
|
, UI.actionButton model.theme
|
||||||
[ centerX ]
|
[ centerX ]
|
||||||
{ isActive = True
|
{ isActive = True
|
||||||
, onPress = Reset
|
, onPress = Reset
|
||||||
|
@ -420,8 +422,8 @@ moderatorTools model =
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
joinForm : Room -> String -> Element Msg
|
joinForm : UI.Theme -> Room -> String -> Element Msg
|
||||||
joinForm room playerName =
|
joinForm theme room playerName =
|
||||||
let
|
let
|
||||||
players =
|
players =
|
||||||
Dict.values room.players
|
Dict.values room.players
|
||||||
|
@ -441,7 +443,8 @@ joinForm room playerName =
|
||||||
, label = Input.labelHidden "Your name"
|
, label = Input.labelHidden "Your name"
|
||||||
, placeholder = Just (Input.placeholder [] (text "Your name"))
|
, placeholder = Just (Input.placeholder [] (text "Your name"))
|
||||||
}
|
}
|
||||||
, UI.actionButton [ centerX ]
|
, UI.actionButton theme
|
||||||
|
[ centerX ]
|
||||||
{ isActive = not (String.isEmpty playerName)
|
{ isActive = not (String.isEmpty playerName)
|
||||||
, onPress = JoinRoom
|
, onPress = JoinRoom
|
||||||
, label = text "Join!"
|
, label = text "Join!"
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
module PlanningPokerUI exposing
|
module PlanningPokerUI exposing
|
||||||
( actionButton
|
( Theme(..)
|
||||||
|
, actionButton
|
||||||
, colors
|
, colors
|
||||||
, fontSizes
|
, fontSizes
|
||||||
, heroText
|
, heroText
|
||||||
|
, navBar
|
||||||
, onEnter
|
, onEnter
|
||||||
|
, textInput
|
||||||
|
, themePicker
|
||||||
, toDocument
|
, toDocument
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,20 +20,63 @@ import Html.Events
|
||||||
import Json.Decode as Decode
|
import Json.Decode as Decode
|
||||||
|
|
||||||
|
|
||||||
colors =
|
type Theme
|
||||||
|
= Light
|
||||||
|
| Dark
|
||||||
|
|
||||||
|
|
||||||
|
colors theme =
|
||||||
let
|
let
|
||||||
primary =
|
blue : Color
|
||||||
blue
|
blue =
|
||||||
|
rgb255 100 100 255
|
||||||
|
|
||||||
|
lightGrey : Color
|
||||||
|
lightGrey =
|
||||||
|
rgb255 200 200 200
|
||||||
|
|
||||||
|
grey : Color
|
||||||
|
grey =
|
||||||
|
rgb255 50 50 50
|
||||||
|
|
||||||
|
darkGrey : Color
|
||||||
|
darkGrey =
|
||||||
|
rgb255 20 20 20
|
||||||
|
|
||||||
|
red : Color
|
||||||
|
red =
|
||||||
|
rgb255 255 100 100
|
||||||
|
|
||||||
|
white : Color
|
||||||
|
white =
|
||||||
|
rgb255 255 255 255
|
||||||
|
|
||||||
|
black : Color
|
||||||
|
black =
|
||||||
|
rgb255 0 0 0
|
||||||
in
|
in
|
||||||
{ primary = primary
|
case theme of
|
||||||
, background = white
|
Light ->
|
||||||
, selected = primary
|
{ primary = blue
|
||||||
, disabled = lightGrey
|
, background = white
|
||||||
, errorBackground = red
|
, selected = blue
|
||||||
, errorForeground = white
|
, disabled = lightGrey
|
||||||
, text = black
|
, errorBackground = red
|
||||||
, buttonText = white
|
, errorForeground = white
|
||||||
}
|
, text = black
|
||||||
|
, buttonText = white
|
||||||
|
}
|
||||||
|
|
||||||
|
Dark ->
|
||||||
|
{ primary = blue
|
||||||
|
, background = darkGrey
|
||||||
|
, selected = blue
|
||||||
|
, disabled = grey
|
||||||
|
, errorBackground = red
|
||||||
|
, errorForeground = white
|
||||||
|
, text = white
|
||||||
|
, buttonText = lightGrey
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fontSizes =
|
fontSizes =
|
||||||
|
@ -39,56 +86,58 @@ fontSizes =
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
blue : Color
|
textInput :
|
||||||
blue =
|
Theme
|
||||||
rgb255 100 100 255
|
-> List (Attribute msg)
|
||||||
|
->
|
||||||
|
{ onChange : String -> msg
|
||||||
|
, text : String
|
||||||
|
, label : Input.Label msg
|
||||||
|
, placeholder : Maybe (Input.Placeholder msg)
|
||||||
|
}
|
||||||
|
-> Element msg
|
||||||
|
textInput theme attrs { onChange, text, label, placeholder } =
|
||||||
|
Input.text
|
||||||
|
(Font.color (colors theme).text
|
||||||
|
:: Background.color (colors theme).background
|
||||||
|
:: attrs
|
||||||
|
)
|
||||||
|
{ onChange = onChange, text = text, label = label, placeholder = placeholder }
|
||||||
|
|
||||||
|
|
||||||
lightGrey : Color
|
button :
|
||||||
lightGrey =
|
Theme
|
||||||
rgb255 200 200 200
|
-> List (Attribute msg)
|
||||||
|
|
||||||
|
|
||||||
red : Color
|
|
||||||
red =
|
|
||||||
rgb255 255 100 100
|
|
||||||
|
|
||||||
|
|
||||||
white : Color
|
|
||||||
white =
|
|
||||||
rgb255 255 255 255
|
|
||||||
|
|
||||||
|
|
||||||
black : Color
|
|
||||||
black =
|
|
||||||
rgb255 0 0 0
|
|
||||||
|
|
||||||
|
|
||||||
actionButton :
|
|
||||||
List (Attribute msg)
|
|
||||||
-> { isActive : Bool, onPress : msg, label : Element msg }
|
-> { isActive : Bool, onPress : msg, label : Element msg }
|
||||||
-> Element msg
|
-> Element msg
|
||||||
actionButton attrs { isActive, onPress, label } =
|
button theme attrs { isActive, onPress, label } =
|
||||||
let
|
let
|
||||||
( color, maybeEvent ) =
|
( color, maybeEvent ) =
|
||||||
if isActive then
|
if isActive then
|
||||||
( blue, Just onPress )
|
( (colors theme).primary, Just onPress )
|
||||||
|
|
||||||
else
|
else
|
||||||
( lightGrey, Nothing )
|
( (colors theme).disabled, Nothing )
|
||||||
in
|
in
|
||||||
Input.button
|
Input.button
|
||||||
([ padding 20
|
(Background.color color
|
||||||
, Background.color color
|
:: Font.color (colors theme).buttonText
|
||||||
, Font.color white
|
:: attrs
|
||||||
]
|
|
||||||
++ attrs
|
|
||||||
)
|
)
|
||||||
{ onPress = maybeEvent
|
{ onPress = maybeEvent
|
||||||
, label = label
|
, label = label
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
actionButton :
|
||||||
|
Theme
|
||||||
|
-> List (Attribute msg)
|
||||||
|
-> { isActive : Bool, onPress : msg, label : Element msg }
|
||||||
|
-> Element msg
|
||||||
|
actionButton theme attrs opts =
|
||||||
|
button theme (padding 20 :: attrs) opts
|
||||||
|
|
||||||
|
|
||||||
heroText :
|
heroText :
|
||||||
List (Attribute msg)
|
List (Attribute msg)
|
||||||
-> String
|
-> String
|
||||||
|
@ -97,11 +146,49 @@ heroText attrs s =
|
||||||
el ([ Font.size fontSizes.huge ] ++ attrs) (text s)
|
el ([ Font.size fontSizes.huge ] ++ attrs) (text s)
|
||||||
|
|
||||||
|
|
||||||
toDocument : { title : String, body : List (Element msg) } -> Document msg
|
navBar : Theme -> List (Element msg) -> Element msg
|
||||||
toDocument { title, body } =
|
navBar theme elements =
|
||||||
|
row
|
||||||
|
[ Background.color (colors theme).primary
|
||||||
|
, Font.color (colors theme).buttonText
|
||||||
|
, height (px 50)
|
||||||
|
, width fill
|
||||||
|
, padding 10
|
||||||
|
]
|
||||||
|
elements
|
||||||
|
|
||||||
|
|
||||||
|
themePicker : Theme -> (Theme -> msg) -> Element msg
|
||||||
|
themePicker theme onChange =
|
||||||
|
row []
|
||||||
|
[ case theme of
|
||||||
|
Light ->
|
||||||
|
button theme
|
||||||
|
[ padding 5 ]
|
||||||
|
{ isActive = True
|
||||||
|
, onPress = onChange Dark
|
||||||
|
, label = text "Dark mode 🌙"
|
||||||
|
}
|
||||||
|
|
||||||
|
Dark ->
|
||||||
|
button theme
|
||||||
|
[ padding 5 ]
|
||||||
|
{ isActive = True
|
||||||
|
, onPress = onChange Light
|
||||||
|
, label = text "Light mode 🌞"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
toDocument : Theme -> { title : String, body : List (Element msg) } -> Document msg
|
||||||
|
toDocument theme { title, body } =
|
||||||
{ title = title
|
{ title = title
|
||||||
, body =
|
, body =
|
||||||
[ layout [] <|
|
[ layout
|
||||||
|
[ Font.color (colors theme).text
|
||||||
|
, Background.color (colors theme).background
|
||||||
|
]
|
||||||
|
<|
|
||||||
column [ width fill, height fill, spacing 20 ] body
|
column [ width fill, height fill, spacing 20 ] body
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue