mirror of
https://github.com/correl/elm-mdl.git
synced 2024-11-23 11:09:51 +00:00
Implemented Waterfall headermode. Fixes #21.
This commit is contained in:
parent
d1f56c1024
commit
5c2f86b13a
4 changed files with 167 additions and 66 deletions
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
, ("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
|
||||
]
|
||||
]
|
||||
|
|
Loading…
Reference in a new issue