Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ui.DrawerMenu #72

Open
wants to merge 2 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 188 additions & 0 deletions source/Ui/DrawerMenu.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
module Ui.DrawerMenu exposing (..)

{-| This module provides a component for creating a menu either on the left
or the right side of the window that is hidden by default.

# Model
@docs Model, Msg, init, update

# View
@docs view

# Functions
@docs toggle
-}
import Html.Events.Extra exposing (onTransitionEnd)
import Html.Attributes exposing (property, style)
import Html.Events exposing (onClick)
import Html exposing (node, text, a)

import Ui.Styles.DrawerMenu exposing (defaultStyle)
import Ui.Styles

import Json.Decode as Json
import Json.Encode as JE

import Ui.Icons
import Ui.Link
import Ui

{-| Represents a side.
-}
type Side
= Right
| Left


{-| Representation of a drawer menu:
- **visible** - Whether or not the drawer menu is open
-}
type alias Model =
{ contentsShown : Bool
, bottom : Int
, open : Bool
, side : Side
, top : Int
}


{-| Representation of the view model of a drawer menu.
- **address** - The address for the messages
- **contents** - The contents to displa
-}
type alias ViewModel msg =
{ contents : List (Html.Html msg)
, address : Msg -> msg
}


{-| Representation of a link:
- **contents** - The contents of the link, usually just text
- **target** - The value for the target attribute
- **url** - The value for the href attribute
- **msg** - The message to trigger
-}
type alias Item msg =
{ contents : List (Html.Html msg)
, icon : Maybe (Html.Html msg)
, target : Maybe String
, url : Maybe String
, msg : Maybe msg
}


{-| Initializes a drawer menu.

menu =
Ui.DrawerMenu.init
|> Ui.DrawerMenu.side Ui.DrawerMenu.Left
|> Ui.DrawerMenu.top 0
|> Ui.DrawerMenu.bottom 0
-}
init : Model
init =
{ contentsShown = False
, open = False
, side = Left
, bottom = 0
, top = 0
}


{-| Messages that a drawer menu can receive.
-}
type Msg
= HideContents
| Close


{-| Updates a drawer menu

updatedMenu = Ui.DrawerMenu.update msg menu
-}
update : Msg -> Model -> Model
update msg model =
case msg of
Close ->
close model

HideContents ->
{ model | contentsShown = False }


{-| Renders a drawer menu.
-}
view : ViewModel msg -> Model -> Html.Html msg
view { address, contents } model =
let
transitionEvent =
if not model.open then
let
decoder =
Json.at
[ "target", "_menu" ]
(Json.succeed (address HideContents))
in
[ onTransitionEnd decoder
, property "_menu" (JE.string "0")
]
else
[]

mainContents =
if model.contentsShown then
contents
else
[]
in
node "ui-drawer-menu"
( [ Ui.Styles.apply defaultStyle
, Ui.attributeList [ ( "open", model.open ) ]
, transitionEvent
]
|> List.concat
)
[ node "ui-drawer-menu-backdrop" [ onClick (address Close) ] []
, node "ui-drawer-menu-main" [] mainContents
]

title : String -> Html.Html msg
title value =
node "ui-drawer-menu-title" [] [ text value ]

devider : Html.Html msg
devider =
node "ui-drawer-menu-devider" [] []

item : Item msg -> Html.Html msg
item { target, url, msg, contents, icon } =
let
iconNode =
case icon of
Just svg ->
node "ui-drawer-menu-item-icon" [] [ svg ]

Nothing ->
text ""
in
node "ui-drawer-menu-item" []
[ Ui.Link.view
{ target = target
, url = url
, msg = msg
, contents =
[ iconNode
, node "ui-drawer-menu-item-contents" [] contents
]
}
]



open : Model -> Model
open model =
{ model | open = True, contentsShown = True }

close : Model -> Model
close model =
{ model | open = False }
171 changes: 171 additions & 0 deletions source/Ui/Styles/DrawerMenu.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
module Ui.Styles.DrawerMenu exposing (..)

{-| Styles for a drawer menu.

@docs style
-}
import Css.Properties exposing (..)
import Css exposing (..)

import Ui.Styles.Theme as Theme exposing (Theme)
import Ui.Styles.Mixins as Mixins
import Ui.Styles exposing (Style)

{-| Styles for a drawer menu using the default theme.
-}
defaultStyle : Style
defaultStyle =
Ui.Styles.attributes (style Theme.default)


{-| Returns the style node for a drawer menu using the given theme.
-}
style : Theme -> Node
style theme =
mixin
[ position fixed
, bottom zero
, right zero
, left zero
, top zero
, zIndex -1

, transition
[ { property = "all"
, duration = (ms 1)
, easing = "linear"
, delay = (ms 320)
}
]

, selector "&[open]"
[ zIndex theme.zIndexes.drawer
, transition
[ { property = "all"
, duration = (ms 1)
, easing = "linear"
, delay = (ms 0)
}
]

, selector "ui-drawer-menu-backdrop"
[ opacity 1
, transition
[ { easing = "cubic-bezier(0.215, 0.61, 0.355, 1)"
, property = "opacity"
, duration = (ms 320)
, delay = (ms 0)
}
]
]
, selector "ui-drawer-menu-main"
[ left zero
, transition
[ { easing = "cubic-bezier(0.215, 0.61, 0.355, 1)"
, duration = (ms 320)
, property = "left"
, delay = (ms 0)
}
]
]
]

, selector "ui-drawer-menu-backdrop"
[ background "rgba(0,0,0,0.6)"
, position absolute
, bottom zero
, right zero
, left zero
, top zero

, opacity 0
, zIndex 0
, transition
[ { easing = "cubic-bezier(0.55, 0.055, 0.675, 0.19)"
, property = "opacity"
, duration = (ms 320)
, delay = (ms 0)
}
]
]

, selector "ui-drawer-menu-main"
[ Mixins.defaults

, boxShadow
[ { x = "0"
, y = "0"
, blur = px 40
, spread = "0"
, color = "rgba(0,0,0,0.2)"
, inset = False
}
]
, background theme.colors.primary.color
, color theme.colors.primary.bw
, fontFamily theme.fontFamily
, position absolute
, minWidth (px 200)
, left (pct -100)
, bottom zero
, top zero
, zIndex 1

, transition
[ { easing = "cubic-bezier(0.55, 0.055, 0.675, 0.19)"
, duration = (ms 320)
, property = "left"
, delay = (ms 0)
}
]
]

, selector "ui-drawer-menu-devider"
[ borderTop "1px solid rgba(255, 255, 255, 0.2)"
, margin ((px 10) . zero)
, display block
]

, selector "ui-drawer-menu-title"
[ padding ((px 10) . (px 20))
, fontSize (px 24)
, display block
]

, selector "ui-drawer-menu-item + ui-drawer-menu-item a"
[ marginTop (px 5)
]

, selector "ui-drawer-menu-item a"
[ padding ((px 10) . (px 20))
, alignItems center
, fontSize (px 20)
, display flex

, selector "&[tabindex]"
[ cursor pointer
, outline none

, selector "&:focus"
[ background theme.colors.focus.color
]
]

, selector "ui-drawer-menu-item-contents"
[ flex_ "1"
]

, selector "ui-drawer-menu-item-icon"
[ marginRight (px 15)
, flex_ "0 0 auto"
, height (em 1)
, width (em 1)
]

, selector "svg"
[ fill currentColor
, height (em 1)
, width (em 1)
]
]
]
2 changes: 2 additions & 0 deletions source/Ui/Styles/Theme.elm
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type alias Theme =
, zIndexes :
{ notifications : Int
, dropdown : Int
, drawer : Int
, header : Int
, modal : Int
, fab : Int
Expand Down Expand Up @@ -102,6 +103,7 @@ default =
, zIndexes =
{ notifications = 2000
, dropdown = 1000
, drawer = 500
, modal = 100
, header = 50
, fab = 90
Expand Down
Loading