Move the sidebar state into elm for consistency and simplicity.
This commit is contained in:
@@ -68,7 +68,7 @@ view model =
|
||||
case model.overlay of
|
||||
Just overlay ->
|
||||
[ div [ id "mui-overlay"
|
||||
, onClickIfId "mui-overlay" (model.wrap Hide) (model.wrap NoOp)
|
||||
, Util.onClickIfId "mui-overlay" (model.wrap Hide) (model.wrap NoOp)
|
||||
, Util.onKeyDown "Escape" (model.wrap Hide) (model.wrap NoOp)
|
||||
, tabindex 0
|
||||
]
|
||||
@@ -87,12 +87,3 @@ view model =
|
||||
]
|
||||
Nothing ->
|
||||
[]
|
||||
|
||||
|
||||
onClickIfId : String -> msg -> msg -> Attribute msg
|
||||
onClickIfId targetId message noOp =
|
||||
on "click" (ifIdDecoder |> Json.map (\clickedId -> if clickedId == targetId then message else noOp))
|
||||
|
||||
|
||||
ifIdDecoder : Json.Decoder String
|
||||
ifIdDecoder = Json.at [ "target", "id" ] Json.string
|
||||
|
||||
@@ -29,6 +29,7 @@ import MassiveDecks.Scenes.Config.Messages as Config
|
||||
import MassiveDecks.Scenes.Lobby.UI as UI
|
||||
import MassiveDecks.Scenes.Lobby.Messages exposing (ConsumerMessage(..), Message(..))
|
||||
import MassiveDecks.Scenes.Lobby.Models exposing (Model)
|
||||
import MassiveDecks.Scenes.Lobby.Sidebar as Sidebar
|
||||
import MassiveDecks.Util as Util exposing ((:>))
|
||||
|
||||
|
||||
@@ -45,6 +46,7 @@ init init lobbyAndHand secret =
|
||||
, init = init
|
||||
, notification = Nothing
|
||||
, qrNeedsRendering = False
|
||||
, sidebar = Sidebar.init 768 {- Should match the enhance-width in less. -}
|
||||
} ! []
|
||||
|
||||
|
||||
@@ -146,6 +148,12 @@ update message model =
|
||||
in
|
||||
({ model | playing = playing }, Cmd.map (LocalMessage << PlayingMessage) cmd)
|
||||
|
||||
SidebarMessage sidebarMessage ->
|
||||
let
|
||||
(sidebarModel, cmd) = Sidebar.update sidebarMessage model.sidebar
|
||||
in
|
||||
({ model | sidebar = sidebarModel }, Cmd.map (LocalMessage << SidebarMessage) cmd)
|
||||
|
||||
BrowserNotificationsMessage notificationMessage ->
|
||||
let
|
||||
(browserNotifications, localCmd, cmd) = BrowserNotifications.update notificationMessage model.browserNotifications
|
||||
|
||||
@@ -4,6 +4,7 @@ import MassiveDecks.Models.Game as Game
|
||||
import MassiveDecks.Models.Card as Card
|
||||
import MassiveDecks.Models.Player as Player exposing (Player)
|
||||
import MassiveDecks.Models.Notification as Notification exposing (Notification)
|
||||
import MassiveDecks.Scenes.Lobby.Sidebar as Sidebar
|
||||
import MassiveDecks.Scenes.Config.Messages as Config
|
||||
import MassiveDecks.Scenes.Playing.Messages as Playing
|
||||
import MassiveDecks.Components.Errors as Errors
|
||||
@@ -37,3 +38,4 @@ type Message
|
||||
| BrowserNotificationsMessage BrowserNotifications.Message
|
||||
| ConfigMessage Config.ConsumerMessage
|
||||
| PlayingMessage Playing.ConsumerMessage
|
||||
| SidebarMessage Sidebar.Message
|
||||
|
||||
@@ -5,6 +5,7 @@ import MassiveDecks.Models.Game as Game
|
||||
import MassiveDecks.Models.Player as Player
|
||||
import MassiveDecks.Models.Card as Card
|
||||
import MassiveDecks.Models.Notification as Notification exposing (Notification)
|
||||
import MassiveDecks.Scenes.Lobby.Sidebar as Sidebar
|
||||
import MassiveDecks.Scenes.Config.Models as Config
|
||||
import MassiveDecks.Scenes.Playing.Models as Playing
|
||||
import MassiveDecks.Components.BrowserNotifications as BrowserNotifications
|
||||
@@ -22,4 +23,5 @@ type alias Model =
|
||||
, init : Init
|
||||
, notification : Maybe Notification
|
||||
, qrNeedsRendering : Bool
|
||||
, sidebar : Sidebar.Model
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
port module MassiveDecks.Scenes.Lobby.Sidebar exposing (Model, Message(..), init, update)
|
||||
|
||||
import Task
|
||||
|
||||
import Window
|
||||
|
||||
import MassiveDecks.Util as Util
|
||||
|
||||
|
||||
{-| This component handles the players sidebar for the lobby view. On a larger display, this bar is shown, but can be
|
||||
hidden by the user. On small displays, it is hidden but can be popped out as a modal window over the top of the
|
||||
lobby.
|
||||
|
||||
No rendering is done within this module - instead the model should be inspected and the sidebar rendered as required.
|
||||
-}
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ enhanceWidth : Int
|
||||
, hidden : Bool
|
||||
, shownAsOverlay : Bool
|
||||
}
|
||||
|
||||
|
||||
type Message
|
||||
= Toggle
|
||||
| Show Int
|
||||
| Hide
|
||||
|
||||
|
||||
init : Int -> Model
|
||||
init enhanceWidth =
|
||||
{ enhanceWidth = enhanceWidth
|
||||
, hidden = False
|
||||
, shownAsOverlay = False
|
||||
}
|
||||
|
||||
|
||||
update : Message -> Model -> (Model, Cmd Message)
|
||||
update message model =
|
||||
case message of
|
||||
Toggle ->
|
||||
(model, Task.perform Util.impossible Show Window.width)
|
||||
|
||||
Show screenWidth ->
|
||||
if model.shownAsOverlay then
|
||||
({ model | shownAsOverlay = False }, Cmd.none)
|
||||
else if screenWidth > model.enhanceWidth then
|
||||
({ model | hidden = not model.hidden }, Cmd.none)
|
||||
else
|
||||
({ model | shownAsOverlay = True }, Cmd.none)
|
||||
|
||||
Hide ->
|
||||
({ model | shownAsOverlay = False }, Cmd.none)
|
||||
@@ -14,6 +14,7 @@ import MassiveDecks.Scenes.Config as Config
|
||||
import MassiveDecks.Scenes.Playing as Playing
|
||||
import MassiveDecks.Scenes.Lobby.Models exposing (Model)
|
||||
import MassiveDecks.Scenes.Lobby.Messages exposing (ConsumerMessage(..), Message(..))
|
||||
import MassiveDecks.Scenes.Lobby.Sidebar as Sidebar
|
||||
import MassiveDecks.Models.Player as Player exposing (Player, Status(..))
|
||||
import MassiveDecks.Models.Notification as Notification exposing (Notification)
|
||||
import MassiveDecks.Util as Util
|
||||
@@ -34,15 +35,16 @@ view model =
|
||||
in
|
||||
(h |> List.map (Html.map (PlayingMessage >> LocalMessage)), c |> List.map (Html.map (PlayingMessage >> LocalMessage)))
|
||||
in
|
||||
root [ appHeader header model
|
||||
, spacer
|
||||
, scores players
|
||||
, contentWrapper contents
|
||||
]
|
||||
root model.sidebar.hidden
|
||||
[ appHeader header model
|
||||
, spacer
|
||||
, scores model.sidebar.shownAsOverlay players
|
||||
, contentWrapper contents
|
||||
]
|
||||
|
||||
|
||||
root : List (Html msg) -> Html msg
|
||||
root contents = div [ class "content" ] contents
|
||||
root : Bool -> List (Html msg) -> Html msg
|
||||
root hideScores contents = div [ classList [ ("content", True), ("hide-scores", hideScores) ] ] contents
|
||||
|
||||
|
||||
contentWrapper : List (Html msg) -> Html msg
|
||||
@@ -53,19 +55,44 @@ spacer : Html msg
|
||||
spacer = div [ class "mui--appbar-height" ] []
|
||||
|
||||
|
||||
scores : List Player -> Html msg
|
||||
scores players = div [ id "scores" ]
|
||||
[ div [ id "scores-title", class "mui--appbar-line-height mui--text-title" ] [ text "Players" ]
|
||||
, div [ class "mui-divider" ] []
|
||||
, table [ class "mui-table" ]
|
||||
[ thead [] [ tr [] [ th [ class "state", title "State" ] [ Icon.icon "tasks" ]
|
||||
, th [ class "name" ] [ text "Player" ]
|
||||
, th [ class "score", title "Score" ] [ Icon.icon "star" ]
|
||||
]
|
||||
]
|
||||
, tbody [] (List.map score players)
|
||||
]
|
||||
]
|
||||
scores : Bool -> List Player -> Html ConsumerMessage
|
||||
scores shownAsOverlay players =
|
||||
let
|
||||
hideMessage = LocalMessage (SidebarMessage Sidebar.Hide)
|
||||
closeLink = if shownAsOverlay then
|
||||
[ a [ class "link close-link"
|
||||
, title "Hide."
|
||||
, attribute "tabindex" "0"
|
||||
, attribute "role" "button"
|
||||
, onClick hideMessage
|
||||
] [ Icon.icon "times" ]
|
||||
]
|
||||
else
|
||||
[]
|
||||
sidebar =
|
||||
div [ id "scores", classList [ ("shownAsOverlay", shownAsOverlay) ] ]
|
||||
[ div [ id "scores-title", class "mui--appbar-line-height mui--text-headline" ] ([ text "Players" ] ++ closeLink)
|
||||
, div [ class "mui-divider" ] []
|
||||
, table [ class "mui-table" ]
|
||||
[ thead [] [ tr [] [ th [ class "state", title "State" ] [ Icon.icon "tasks" ]
|
||||
, th [ class "name" ] [ text "Player" ]
|
||||
, th [ class "score", title "Score" ] [ Icon.icon "star" ]
|
||||
]
|
||||
]
|
||||
, tbody [] (List.map score players)
|
||||
]
|
||||
]
|
||||
in
|
||||
if shownAsOverlay then
|
||||
div [ id "mui-overlay"
|
||||
, Util.onClickIfId "mui-overlay" hideMessage (LocalMessage NoOp)
|
||||
, Util.onKeyDown "Escape" hideMessage (LocalMessage NoOp)
|
||||
, tabindex 0
|
||||
]
|
||||
[ sidebar ]
|
||||
else
|
||||
sidebar
|
||||
|
||||
|
||||
score : Player -> Html msg
|
||||
score player =
|
||||
@@ -87,19 +114,18 @@ score player =
|
||||
appHeader : List (Html ConsumerMessage) -> Model -> Html ConsumerMessage
|
||||
appHeader contents model = (header [] [ div [ class "mui-appbar mui--appbar-line-height" ]
|
||||
[ div [ class "mui--appbar-line-height" ]
|
||||
[ span [ class "score-buttons" ] (List.append [ scoresButton True, scoresButton False ] (notificationPopup model.notification))
|
||||
[ span [ class "score-buttons" ] ([ scoresButton ] ++ (notificationPopup model.notification))
|
||||
, span [ id "title", class "mui--text-title mui--visible-xs-inline-block" ] contents
|
||||
, gameMenu model ] ] ])
|
||||
|
||||
|
||||
scoresButton : Bool -> Html msg
|
||||
scoresButton shown =
|
||||
let
|
||||
showHideClasses = if shown then " mui--hidden-xs js-hide-scores" else " mui--visible-xs-inline-block js-show-scores"
|
||||
in
|
||||
button [ class ("scores-toggle mui-btn mui-btn--small mui-btn--primary badged" ++ showHideClasses)
|
||||
scoresButton : Html ConsumerMessage
|
||||
scoresButton =
|
||||
button [ class ("scores-toggle mui-btn mui-btn--small mui-btn--primary badged")
|
||||
, title "Players."
|
||||
] [ Icon.fwIcon "users" ]
|
||||
, onClick (LocalMessage (SidebarMessage Sidebar.Toggle))
|
||||
]
|
||||
[ Icon.fwIcon "users" ]
|
||||
|
||||
|
||||
notificationPopup : Maybe Notification -> List (Html ConsumerMessage)
|
||||
|
||||
@@ -150,3 +150,14 @@ isNothing m =
|
||||
onKeyDown : String -> msg -> msg -> Attribute msg
|
||||
onKeyDown key message noOp =
|
||||
on "keydown" (Json.at [ "key" ] Json.string |> Json.map (\pressed -> if pressed == key then message else noOp))
|
||||
|
||||
|
||||
{-| Perform an action on click, only if the id of the clicked element matches (i.e: only on click for a given element
|
||||
in the tree).
|
||||
-}
|
||||
onClickIfId : String -> msg -> msg -> Attribute msg
|
||||
onClickIfId targetId message noOp =
|
||||
on "click" (ifIdDecoder |> Json.map (\clickedId -> if clickedId == targetId then message else noOp))
|
||||
|
||||
ifIdDecoder : Json.Decoder String
|
||||
ifIdDecoder = Json.at [ "target", "id" ] Json.string
|
||||
|
||||
Reference in New Issue
Block a user