elm-mdl/src/Material.elm

192 lines
5.6 KiB
Elm
Raw Normal View History

2016-04-06 13:28:37 +00:00
module Material
( Model, model
, Action, update
)
where
2016-03-14 09:42:49 +00:00
2016-04-06 13:28:37 +00:00
{-|
Material Design component library for Elm based on Google's
2016-03-14 09:42:49 +00:00
[Material Design Lite](https://www.getmdl.io/).
2016-04-06 13:28:37 +00:00
Click
[here](https://debois.github.io/elm-mdl/)
for a live demo.
2016-03-19 22:47:21 +00:00
2016-04-06 13:28:37 +00:00
# Component model
2016-04-07 07:52:11 +00:00
The component model of the library is simply the Elm Architecture (TEA), i.e.,
each component has types `Model` and `Action`, and values `view` and `update`. A
minimal example using this library in plain TEA can be found
[here](https://github.com/debois/elm-mdl/blob/master/examples/Component-TEA.elm).
2016-03-14 09:42:49 +00:00
2016-04-07 07:52:11 +00:00
Using more than a few component in plain TEA is unwieldy because of the large
amount of boilerplate one has to write. This library provides the "component
support" for getting rid of most of that boilerplate. A minimal example using
component support is
2016-04-06 13:28:37 +00:00
[here](http://github.com/debois/elm-mdl/blob/master/examples/Component.elm).
2016-03-14 09:42:49 +00:00
2016-04-07 07:52:11 +00:00
It is important to note that component support lives __within__ TEA;
it is not an alternative architecture.
2016-03-14 09:42:49 +00:00
2016-04-06 13:28:37 +00:00
# Getting started
2016-03-19 22:47:21 +00:00
2016-04-06 13:28:37 +00:00
The easiest way to get started is to start with one of the minimal examples above.
2016-04-07 07:52:11 +00:00
We recommend going with the library's
[component support](http://github.com/debois/elm-mdl/blob/master/examples/Component.elm)
rather than working directly in plain Elm Architecture.
2016-03-14 09:42:49 +00:00
# TODO
Using TEA, Style.
2016-04-07 07:52:11 +00:00
# Component Support
2016-03-14 09:42:49 +00:00
2016-04-06 13:28:37 +00:00
This module contains only convenience functions for working with nested
components in the Elm architecture. A minimal example using this library
with component support can be found
[here](http://github.com/debois/elm-mdl/blob/master/examples/Component.elm).
We encourage you to use the library in this fashion.
2016-03-14 09:42:49 +00:00
2016-04-06 13:28:37 +00:00
All examples in this subsection is from the
[above minimal example](http://github.com/debois/elm-mdl/blob/master/examples/Component.elm)
2016-03-14 09:42:49 +00:00
2016-04-06 13:28:37 +00:00
Here is how you use component support in general. First, boilerplate.
2016-03-14 09:42:49 +00:00
2016-04-06 13:28:37 +00:00
1. Include `Material`:
2016-03-14 09:42:49 +00:00
<!-- 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.3/material.min.css" />
2016-03-14 09:42:49 +00:00
2016-04-06 13:28:37 +00:00
2. Add a model container Material components to your model:
2016-03-14 09:42:49 +00:00
2016-04-07 07:52:11 +00:00
type alias Model =
{ ...
, mdl : Material.Model
}
2016-03-14 09:42:49 +00:00
2016-04-07 07:52:11 +00:00
model : Model =
{ ...
, mdl = Material.model
}
2016-04-06 13:28:37 +00:00
3. Add an action for Material components.
2016-04-07 07:52:11 +00:00
type Action =
...
| MDL (Material.Action Action)
2016-04-06 13:28:37 +00:00
4. Handle that action in your update function as follows:
2016-04-07 07:52:11 +00:00
update action model =
case action of
...
MDL action' ->
let (mdl', fx) =
Material.update MDL action' model.mdl
in
( { model | mdl = mdl' } , fx )
2016-04-06 13:28:37 +00:00
Next, make the component instances you need. Do this in the View section of your
source file. Let's say you need a textfield for name entry, and you'd like to
be notifed whenever the field changes value through your own NameChanged action:
2016-04-07 07:52:11 +00:00
import Material.Textfield as Textfield
2016-04-06 13:28:37 +00:00
2016-04-07 07:52:11 +00:00
...
2016-04-06 13:28:37 +00:00
2016-04-07 07:52:11 +00:00
type Action =
...
| NameChanged String
2016-04-06 13:28:37 +00:00
2016-04-07 07:52:11 +00:00
...
2016-03-17 12:31:43 +00:00
2016-04-07 07:52:11 +00:00
update action model =
case action of
...
NameChanged name ->
-- Do whatever you need to do.
2016-03-17 12:31:43 +00:00
2016-04-07 07:52:11 +00:00
...
2016-03-17 12:31:43 +00:00
2016-04-07 07:52:11 +00:00
nameInput : Textfield.Instance Material.Model Action
nameInput =
Textfield.instance 2 MDL Textfield.model
[ Textfield.fwdInput NameChanged
]
view addr model =
...
nameInput.view addr model.mdl
2016-04-06 13:28:37 +00:00
The win relative to using plain Elm Architecture is that adding a component
neither requires you to update your model, your Actions, nor your update function.
(As in the above example, you will frequently have to update the latter two anyway,
but now it's not boilerplate, its "business logic".)
## Optimising for size
Using this module will force all elm-mdl components to be built and included in
your application. If this is unacceptable, you can custom-build a version of this
module that uses only the components you need. To do so, you need to re-implement
2016-04-07 07:52:11 +00:00
the present module, modifying the values `model` and `Model` by commenting out the
components you are not using. The module source can be found
[here](https://github.com/debois/elm-mdl/blob/master/src/Material.elm).
2016-04-06 13:28:37 +00:00
You do not need to re-build the entire elm-mdl library; simply copy the
2016-04-07 07:52:11 +00:00
source of this module, give it a new name, modify as it as indicated above,
then use your modified module rather than this one.
2016-04-06 13:28:37 +00:00
@docs Model, model, Action, update
2016-03-17 12:31:43 +00:00
-}
2016-04-06 13:28:37 +00:00
import Dict
import Effects exposing (Effects)
2016-03-17 12:31:43 +00:00
2016-04-06 13:28:37 +00:00
import Material.Button as Button
import Material.Textfield as Textfield
import Material.Snackbar as Snackbar
2016-04-06 13:28:37 +00:00
import Material.Component as Component exposing (Indexed)
2016-04-07 07:52:11 +00:00
{-| Model encompassing all Material components. Since some components store
user actions in their model (notably Snackbar), the model is generic in the
type of such "observations".
2016-03-17 12:31:43 +00:00
-}
type alias Model =
2016-04-06 13:28:37 +00:00
{ button : Indexed Button.Model
, textfield : Indexed Textfield.Model
, snackbar : Maybe (Snackbar.Model Int)
2016-04-06 13:28:37 +00:00
}
2016-03-17 12:31:43 +00:00
2016-04-06 13:28:37 +00:00
{-| Initial model.
-}
model : Model
2016-04-06 13:28:37 +00:00
model =
{ button = Dict.empty
, textfield = Dict.empty
2016-04-07 07:52:11 +00:00
, snackbar = Nothing
2016-04-06 13:28:37 +00:00
}
2016-03-17 12:31:43 +00:00
2016-04-06 13:28:37 +00:00
{-| Action encompassing actions of all Material components.
2016-03-17 12:31:43 +00:00
-}
2016-04-07 07:52:11 +00:00
type alias Action obs =
Component.Action Model obs
2016-04-06 13:28:37 +00:00
{-| Update function for the above Action.
2016-03-17 12:31:43 +00:00
-}
2016-04-06 13:28:37 +00:00
update :
2016-04-07 07:52:11 +00:00
(Action obs -> obs)
-> Action obs
-> Model
-> (Model, Effects obs)
2016-04-06 13:28:37 +00:00
update =
Component.update