elm-mdl/Material/Grid.elm
2016-03-13 22:47:00 +01:00

227 lines
5.7 KiB
Elm

module Material.Grid
( grid, gridWithOptions, Options
, cell
, Device(..)
, Align(..)
, size
, offset
, align
, hide
, order
) where
{-| From the
[Material Design Lite documentation](http://www.getmdl.io/components/#layout-section/grid):
> The Material Design Lite (MDL) grid component is a simplified method for laying
> out content for multiple screen sizes. It reduces the usual coding burden
> required to correctly display blocks of content in a variety of display
> conditions.
>
> The MDL grid is defined and enclosed by a container element. A grid has 12
> columns in the desktop screen size, 8 in the tablet size, and 4 in the phone
> size, each size having predefined margins and gutters. Cells are laid out
> sequentially in a row, in the order they are defined, with some exceptions:
>
> - If a cell doesn't fit in the row in one of the screen sizes, it flows
> into the following line.
> - If a cell has a specified column size equal to or larger than the number
> of columns for the current screen size, it takes up the entirety of its
> row."
Example use:
import Material.Grid exposing (grid, cell, size, Device(..))
top : Html
top =
grid
[ cell [ size All 4 ]
[ h4 [] [text "Cell 1"]
]
, cell [ offset All 2, size All 4 ]
[ h4 [] [text "Cell 2"]
, p [] [text "This cell is offset by 2"]
]
, cell [ size All 6 ]
[ h4 [] [text "Cell 3"]
]
, cell [ size Tablet 6, size Desktop 12, size Phone 2 ]
[ h4 [] [text "Cell 4"]
, p [] [text "Size varies with device"]
]
]
# Grid container
@docs grid, Options, gridWithOptions
# Cells
Cells are configured with a `List CellConfig`; this configuration dictates the
size, offset, and alignment behaviour of the cell. Construct
individual `CellConfig` elements using `size`, `offset`, and `align`.
@docs cell, Device, size, offset, Align, align, hide, order
-}
{- TODO.
1. From MDL docs:
"You can set a maximum grid width, after which the grid stays centered with
padding on either side, by setting its max-width CSS property."
2. mdl-cell--stretch
-}
import Html exposing (..)
import Html.Attributes exposing (..)
import String
import Material.Aux exposing (clip, filter)
{-| The `spacing` parameter indicates whether or not the grid should have
spacing between cells. The `maxWidth` parameter, which must be a valid CSS
dimension, indicates the maximum
width of the grid; if the grid is in a larger container, it stays centered with
padding on either side.
-}
type alias Options =
{ spacing : Bool
, maxWidth : Maybe String
}
{-| By default, a grid has spacing between columns, but no maximum width.
-}
defaultOptions : Options
defaultOptions =
{ spacing = True
, maxWidth = Nothing
}
{-| Construct a grid with options.
-}
gridWithOptions : Options -> List Cell -> Html
gridWithOptions options elms =
div
[ classList
[ ("mdl-grid", True)
, ("mdl-grid--no-spacing", not options.spacing)
]
, style (
options.maxWidth
|> Maybe.map (\maxwidth -> [("max-width", maxwidth)])
|> Maybe.withDefault []
)
]
(List.map (\(Cell elm) -> elm) elms)
{-| Construct a grid with default options (i.e., default spacing, no
maximum width.) Use `cell` some number of times to construct the argument
list.
-}
grid : List Cell -> Html
grid = gridWithOptions defaultOptions
{-| Device specifiers, used with `size` and `offset`. (A `Device` really
encapsulates a screen size.)
-}
type Device = All | Desktop | Tablet | Phone
{- Cell configuration. Construct with `size`, `offset`, and `align`.
-}
type CellConfig = Config String
{- Opaque cell type.
-}
type Cell = Cell Html
suffix : Device -> String
suffix device =
case device of
All -> ""
Desktop -> "-desktop"
Tablet -> "-tablet"
Phone -> "-phone"
{-| Specify cell size. On devices of type `Device`, the
cell being specified spans `Int` columns.
-}
size : Device -> Int -> CellConfig
size device k =
let c =
case device of
All -> clip 1 12 k
Desktop -> clip 1 12 k
Tablet -> clip 1 8 k
Phone -> clip 1 4 k
in
"mdl-cell--" ++ toString c ++ "-col" ++ suffix device |> Config
{-| Specify cell offset, i.e., empty number of empty cells before the present
one. On devices of type `Device`, leave `Int` columns blank before the present
one begins.
-}
offset : Device -> Int -> CellConfig
offset device k =
let c =
case device of
All -> clip 1 11 k
Desktop -> clip 1 11 k
Tablet -> clip 1 7 k
Phone -> clip 1 3 k
in
"mdl-cell--" ++ toString c ++ "-offset" ++ suffix device |> Config
{-| Vertical alignment of cells; use with `align`.
-}
type Align = Top | Middle | Bottom
{-| Specify vertical cell alignment. See `Align`.
-}
align : Align -> CellConfig
align a =
Config <| case a of
Top -> "mdl-cell--top"
Middle -> "mdl-cell--middle"
Bottom -> "mdl-cell--bottom"
{-| Specify that a cell should be hidden on given `Device`.
-}
hide : Device -> CellConfig
hide device =
Config <| case device of
All -> ""
_ -> "mdl-cell--hide-" ++ suffix device
{-| Specify that a cell should re-order itself to position 'Int' on `Device`.
-}
order : Device -> Int -> CellConfig
order device n =
Config <| "mdl-cell--order-" ++ (toString <| clip 1 12 n) ++ suffix device
{-| Construct a cell for use in the argument list for `grid`.
Construct the cell configuration (first argument) using `size`, `offset`, and
`align`. Supply contents for the cell as the second argument.
-}
cell : List CellConfig -> List Html -> Cell
cell configs elms =
Cell <| div
[class <| String.join " " ("mdl-cell" :: (List.map (\(Config s) -> s) configs))]
elms