Implemented Waterfall headermode. Fixes #21.

This commit is contained in:
Søren Debois 2016-03-28 21:25:11 +02:00
parent d1f56c1024
commit 5c2f86b13a
4 changed files with 167 additions and 66 deletions

View file

@ -9,6 +9,7 @@ import Task exposing (Task)
import Array exposing (Array)
import Material.Layout as Layout exposing (defaultLayoutModel)
import Material.Icon as Icon
import Material
import Demo.Buttons
@ -30,6 +31,8 @@ layoutModel : Layout.Model
layoutModel =
{ defaultLayoutModel
| state = Layout.initState (List.length tabs)
, mode = Layout.Waterfall True
, fixedHeader = False
}
@ -83,26 +86,28 @@ drawer =
[ Layout.title "Example drawer"
, Layout.navigation
[ Layout.link
[href "https://github.com/debois/elm-mdl"]
[text "github"]
[ href "https://www.getmdl.io/components/index.html" ]
[ text "MDL" ]
, Layout.link
[href "http://package.elm-lang.org/packages/debois/elm-mdl/1.0.0/"]
[text "elm-package"]
[ href "https://www.google.com/design/spec/material-design/introduction.html"]
[ text "Material Design"]
]
]
header : List Html
header =
[ Layout.row
[ Layout.title "elm-mdl"
, Layout.spacer
, Layout.navigation
[ Layout.link
[ href "https://www.getmdl.io/components/index.html" ]
[ text "MDL" ]
[href "https://github.com/debois/elm-mdl"]
[span [] [text "github"] ]
, Layout.link
[ href "https://www.google.com/design/spec/material-design/introduction.html"]
[ text "Material Design"]
[href "http://package.elm-lang.org/packages/debois/elm-mdl/latest/"]
[text "elm-package"]
]
]
]
@ -142,9 +147,9 @@ view addr model =
in
Layout.view (Signal.forwardTo addr LayoutAction) model.layout
{ header = Just header
, drawer = Just drawer
, tabs = Just tabTitles
{ header = header
, drawer = drawer
, tabs = tabTitles
, main = [ top ]
}
{- The following line is not needed when you manually set up

View file

@ -9,7 +9,7 @@
<!-- MDL -->
<link href='https://fonts.googleapis.com/css?family=Roboto:400,300,500|Roboto+Mono|Roboto+Condensed:400,700&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="https://code.getmdl.io/1.1.1/material.teal-red.min.css" />
<link rel="stylesheet" href="https://code.getmdl.io/1.1.3/material.teal-red.min.css" />
</head>
<body>

View file

@ -73,7 +73,7 @@ css primary accent =
BlueGrey -> ""
_ -> "." ++ toString primary ++ "-" ++ toString accent
in
[ "https://code.getmdl.io/1.1.1/material" ++ cssFile ++ ".min.css"
[ "https://code.getmdl.io/1.1.3/material" ++ cssFile ++ ".min.css"
, "https://fonts.googleapis.com/icon?family=Material+Icons"
, "https://fonts.googleapis.com/css?family=Roboto:400,300,500|Roboto+Mono|Roboto+Condensed:400,700&subset=latin,latin-ext"
]
@ -90,7 +90,7 @@ your .html file:
<!-- MDL -->
<link href='https://fonts.googleapis.com/css?family=Roboto:400,300,500|Roboto+Mono|Roboto+Condensed:400,700&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="https://code.getmdl.io/1.1.1/material.min.css" />
<link rel="stylesheet" href="https://code.getmdl.io/1.1.3/material.min.css" />
Supply primary and accent colors as parameters. Refer to the
Material Design Lite [Custom CSS theme builder](https://www.getmdl.io/customize/index.html)

View file

@ -1,8 +1,8 @@
module Material.Layout
( setupSizeChangeSignal
, Mode, Model, defaultLayoutModel, initState
, Mode(..), Model, defaultLayoutModel, initState
, Action(SwitchTab, ToggleDrawer), update
, spacer, title, navigation, link
, row, spacer, title, navigation, link
, Contents, view
) where
@ -36,7 +36,7 @@ module Material.Layout
@docs Contents, view
## Sub-views
@docs spacer, title, navigation, link
@docs row, spacer, title, navigation, link
# Setup
@docs setupSizeChangeSignal
@ -47,14 +47,18 @@ import Array exposing (Array)
import Maybe exposing (andThen, map)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Html.Events exposing (onClick, on)
import Effects exposing (Effects)
import Window
import Json.Decode as Json
import Material.Helpers exposing (..)
import Material.Ripple as Ripple
import Material.Icon as Icon
import DOM
-- SETUP
@ -82,6 +86,8 @@ setupSizeChangeSignal f =
type alias State' =
{ tabs : Array Ripple.Model
, isSmallScreen : Bool
, isCompact : Bool
, isAnimating : Bool
}
@ -131,6 +137,8 @@ initState : Int -> State
initState no_tabs =
S { tabs = Array.repeat no_tabs Ripple.model
, isSmallScreen = False -- TODO
, isCompact = False
, isAnimating = False
}
@ -163,8 +171,11 @@ type Action
| ToggleDrawer
-- Private
| SmallScreen Bool -- True means small screen
| ScrollTab Int
| ScrollTab Float
| ScrollContents Float
| Ripple Int Ripple.Action
| Click
| TransitionEnd
{-| Component update.
@ -198,15 +209,37 @@ update action model =
in
({ model | state = S state' }, effect)
ScrollTab tab ->
(model, Effects.none) -- TODO
ScrollContents scrollTop ->
let
headerVisible = state.isSmallScreen || model.fixedHeader
state' =
{ state
| isCompact = scrollTop > 0
, isAnimating = headerVisible
}
in
( { model | state = S state' }, Effects.none )
TransitionEnd ->
( { model | state = S { state | isAnimating = False } }
, Effects.none
)
Click ->
( { model | state = S { state | isAnimating = True, isCompact = False } }
, Effects.none
)
-- AUXILIARY VIEWS
{-| Push subsequent elements in header row or drawer column to the right/bottom.
-}
spacer : Html
@ -233,6 +266,12 @@ link attrs contents =
a (class "mdl-navigation__link" :: attrs) contents
{-| Header row.
-}
row : List Html -> Html
row =
div [ class "mdl-layout__header-row" ]
-- MAIN VIEWS
@ -243,18 +282,34 @@ link attrs contents =
- A `Seamed` header does not cast shadow, is permanently affixed to the top of the
screen.
- A `Scroll`'ing header scrolls with contents.
- A `Waterfall` header drops either the top (argument True) or bottom (argument False)
header-row when content scrolls.
-}
type Mode
= Standard
| Seamed
| Scroll
-- | Waterfall
| Waterfall Bool
isWaterfall : Mode -> Bool
isWaterfall mode =
case mode of
Waterfall _ -> True
_ -> False
type alias Addr = Signal.Address Action
toList : Maybe a -> List a
toList x =
case x of
Nothing -> []
Just y -> [y]
tabsView : Addr -> Model -> List Html -> Html
tabsView addr model tabs =
let chevron direction offset =
@ -277,6 +332,7 @@ tabsView addr model tabs =
[ ("mdl-layout__tab-bar", True)
, ("mdl-js-ripple-effect", model.rippleTabs)
, ("mds-js-ripple-effect--ignore-events", model.rippleTabs)
, ("is-casting-shadow", model.mode == Standard)
]
]
(tabs |> mapWithIndex (\tabIndex tab ->
@ -302,18 +358,44 @@ tabsView addr model tabs =
]
headerView : Model -> (Maybe Html, Maybe (List Html), Maybe Html) -> Html
headerView model (drawerButton, row, tabs) =
filter Html.header
[ classList
headerView : Addr -> Model -> (Maybe Html, List Html, Maybe Html) -> Html
headerView addr model (drawerButton, rows, tabs) =
let
mode =
case model.mode of
Standard -> ""
Scroll -> "mdl-layout__header--scroll"
Seamed -> "mdl-layout__header--seamed"
Waterfall True -> "mdl-layout__header--waterfall mdl-layout__header--waterfall-hide-top"
Waterfall False -> "mdl-layout__header--waterfall"
in
Html.header
([ classList
[ ("mdl-layout__header", True)
, ("is-casting-shadow", model.mode == Standard)
, ("is-casting-shadow",
model.mode == Standard ||
(isWaterfall model.mode && (s model).isCompact)
)
, ("is-animating", (s model).isAnimating)
, ("is-compact", (s model).isCompact)
, (mode, mode /= "")
]
]
[ drawerButton
, row |> Maybe.map (div [ class "mdl-layout__header-row" ])
, tabs
|> List.append (
if isWaterfall model.mode then
[ onClick addr Click
, on "transitionend" Json.value (\_ -> Signal.message addr TransitionEnd)
]
else
[]
)
)
(List.concatMap (\x -> x)
[ toList drawerButton
, rows
, toList tabs
]
)
drawerButton : Addr -> Html
@ -349,17 +431,18 @@ drawerView addr model elems =
{-| Content of the layout only (contents of main pane is set elsewhere). Every
part is optional. If `header` is `Nothing`, tabs will not be shown.
part is optional; if you supply an empty list for either, the sub-component is
omitted.
The `header` and `drawer` contains the contents of the header row and drawer,
respectively. Use `spacer`, `title`, `nav`, and
`link`, as well as regular Html to construct these. The `tabs` contains
The `header` and `drawer` contains the contents of the header rows and drawer,
respectively. Use `row`, `spacer`, `title`, `nav`, and `link`, as well as
regular Html to construct these. The `tabs` contains
the title of each tab.
-}
type alias Contents =
{ header : Maybe (List Html)
, drawer : Maybe (List Html)
, tabs : Maybe (List Html)
{ header : List Html
, drawer : List Html
, tabs : List Html
, main : List Html
}
@ -371,11 +454,11 @@ view addr model { drawer, header, tabs, main } =
let
(contentDrawerButton, headerDrawerButton) =
case (drawer, header, model.fixedHeader) of
(Just _, Just _, True) ->
(_ :: _, _ :: _, True) ->
-- Drawer with fixedHeader: Add the button to the header
(Nothing, Just <| drawerButton addr)
(Just _, _, _) ->
(_ :: _, _, _) ->
-- Drawer, no or non-fixed header: Add the button before contents.
(Just <| drawerButton addr, Nothing)
@ -383,38 +466,51 @@ view addr model { drawer, header, tabs, main } =
-- No drawer: no button.
(Nothing, Nothing)
mode =
case model.mode of
Standard -> ""
Scroll -> "mdl-layout__header-scroll"
-- Waterfall -> "mdl-layout__header-waterfall"
Seamed -> "mdl-layout__header-seamed"
hasHeader =
tabs /= Nothing || header /= Nothing
not (List.isEmpty tabs && List.isEmpty header)
tabsElems =
if List.isEmpty tabs then
Nothing
else
Just (tabsView addr model tabs)
in
div
[ class "mdl-layout__container" ]
[ classList
[ ("mdl-layout__container", True)
, ("has-scrolling-header", model.mode == Scroll)
]
]
[ filter div
[ classList
[ ("mdl-layout", True)
[ ("mdl-layout ", True)
, ("is-upgraded", True)
, ("is-small-screen", (s model).isSmallScreen)
, ("has-drawer", drawer /= Nothing)
, ("has-tabs", tabs /= Nothing)
, ("has-drawer", drawer /= [])
, ("has-tabs", tabs /= [])
, ("mdl-js-layout", True)
, ("mdl-layout--fixed-drawer", model.fixedDrawer && drawer /= Nothing)
, ("mdl-layout--fixed-drawer", model.fixedDrawer && drawer /= [])
, ("mdl-layout--fixed-header", model.fixedHeader && hasHeader)
, ("mdl-layout--fixed-tabs", model.fixedTabs && tabs /= Nothing)
, ("mdl-layout--fixed-tabs", model.fixedTabs && tabs /= [])
]
]
[ if hasHeader then
Just <| headerView model (headerDrawerButton, header, Maybe.map (tabsView addr model) tabs)
Just <| headerView addr model (headerDrawerButton, header, tabsElems)
else
Nothing
, drawer |> Maybe.map (\_ -> obfuscator addr model)
, drawer |> Maybe.map (drawerView addr model)
, if List.isEmpty drawer then Nothing else Just (obfuscator addr model)
, if List.isEmpty drawer then Nothing else Just (drawerView addr model drawer)
, contentDrawerButton
, Just <| main' [ class "mdl-layout__content" ] main
, main'
( class "mdl-layout__content"
:: (
if isWaterfall model.mode then
[ on "scroll" (DOM.target DOM.scrollTop) (ScrollContents >> Signal.message addr) ]
else
[]
)
)
main
|> Just
]
]