add in example

This commit is contained in:
Ryan Haskell-Glatz 2019-10-04 17:16:02 -05:00
commit 3ce88f8e24
21 changed files with 8920 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.cache
.DS_Store
dist
elm-stuff
node_modules

192
README.md Normal file
View File

@ -0,0 +1,192 @@
# ryannhg/elm-app
> a way to build single page apps with Elm.
## try it out
```
elm install ryannhg/elm-app
```
## quick overview
this package is a wrapper around Elm's `Browser.application`, adding in page transitions and utilities for adding in new pages and routes.
here's what it looks like to use it:
### src/Main.elm
This is the entrypoint to the app, it imports:
- `Application` - this package
- `App` - the top level `Model`, `Msg`, `init`, `update`, `subscriptions`, and `view`
- `
```elm
module Main exposing (main)
import Application exposing (Application)
import App
import Context
import Route
import Flags exposing (Flags)
main : Application Flags Context.Model Context.Msg App.Model App.Msg
main =
Application.create
{ transition = 300
, toRoute = Route.fromUrl
, title = Route.title
, context =
{ init = Context.init
, update = Context.update
, view = Context.view
, subscriptions = Context.subscriptions
}
, page =
{ init = App.init
, update = App.update
, view = App.view
, subscriptions = App.subscriptions
}
}
```
### src/Pages/Homepage.elm
> Uses `Application.Page.static`
The homepage is static, so it's just a `view`:
```elm
module Pages.Homepage exposing (view)
import Html exposing (Html)
view : Html Never
view =
Html.text "Homepage!"
```
### src/Pages/Counter.elm
> Uses `Application.Page.Sandbox`
The counter page doesn't have any side effects:
```elm
module Pages.Counter exposing (Model, Msg, init, update, view)
import Html exposing (..)
import Html.Events as Events
type alias Model =
{ counter : Int
}
type Msg
= Increment
| Decrement
init : Model
init =
{ counter = 0
}
update : Msg -> Model -> Model
update msg model =
case msg of
Decrement ->
{ model | counter = model.counter - 1 }
Increment ->
{ model | counter = model.counter + 1 }
view : Model -> Html Msg
view model =
div []
[ button [ Events.onClick Decrement ] [ text "-" ]
, text (String.fromInt model.counter)
, button [ Events.onClick Increment ] [ text "+" ]
]
```
### src/Pages/Random.elm
> Uses `Application.Page.element`
The random page doesn't need to update the context of the application:
```elm
module Pages.Random exposing
( Model
, Msg
, init
, subscriptions
, update
, view
)
import Flags exposing (Flags)
import Html exposing (..)
import Html.Events as Events
import Random
type alias Model =
{ roll : Maybe Int
}
type Msg
= Roll
| GotOutcome Int
init : Flags -> ( Model, Cmd Msg )
init _ =
( { roll = Nothing }
, Cmd.none
)
rollDice : Model -> ( Model, Cmd Msg )
rollDice model =
( model
, Random.generate GotOutcome (Random.int 1 6)
)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Roll ->
rollDice model
GotOutcome value ->
( { model | roll = Just value }
, Cmd.none
)
view : Model -> Html Msg
view model =
div []
[ button [ Events.onClick Roll ] [ text "Roll" ]
, p []
[ case model.roll of
Just roll ->
text (String.fromInt roll)
Nothing ->
text "Click the button!"
]
]
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none
```

24
elm.json Normal file
View File

@ -0,0 +1,24 @@
{
"type": "application",
"source-directories": [
"src"
],
"elm-version": "0.19.0",
"dependencies": {
"direct": {
"elm/browser": "1.0.1",
"elm/core": "1.0.2",
"elm/html": "1.0.0",
"elm/url": "1.0.0"
},
"indirect": {
"elm/json": "1.1.3",
"elm/time": "1.0.0",
"elm/virtual-dom": "1.0.2"
}
},
"test-dependencies": {
"direct": {},
"indirect": {}
}
}

4
examples/README.md Normal file
View File

@ -0,0 +1,4 @@
# examples
> some examples of how to use this package.
- [basic](./basic)

8
examples/basic/README.md Normal file
View File

@ -0,0 +1,8 @@
# examples/basic
> an intro into an app using `ryannhg/elm-app`
## how to run
1. `npm install`
1. `npm run dev`

26
examples/basic/elm.json Normal file
View File

@ -0,0 +1,26 @@
{
"type": "application",
"source-directories": [
"src",
"../../src"
],
"elm-version": "0.19.0",
"dependencies": {
"direct": {
"elm/browser": "1.0.1",
"elm/core": "1.0.2",
"elm/html": "1.0.0",
"elm/random": "1.0.0",
"elm/url": "1.0.0"
},
"indirect": {
"elm/json": "1.1.3",
"elm/time": "1.0.0",
"elm/virtual-dom": "1.0.2"
}
},
"test-dependencies": {
"direct": {},
"indirect": {}
}
}

13
examples/basic/index.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script src="./main.js"></script>
</body>
</html>

5
examples/basic/main.js Normal file
View File

@ -0,0 +1,5 @@
import { Elm } from './src/Main.elm'
Elm.Main.init({
node: document.getElementById('app')
})

7585
examples/basic/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
{
"name": "application",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "parcel index.html"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"elm": "^0.19.0-no-deps",
"elm-format": "^0.8.2",
"elm-hot": "^1.1.2",
"node-elm-compiler": "^5.0.4",
"parcel-bundler": "^1.12.3"
}
}

223
examples/basic/src/App.elm Normal file
View File

@ -0,0 +1,223 @@
module App exposing
( Model
, Msg
, init
, subscriptions
, update
, view
)
import Application.Page as Page exposing (Context)
import Context
import Flags exposing (Flags)
import Html exposing (Html)
import Pages.Counter
import Pages.Homepage
import Pages.NotFound
import Pages.Random
import Route exposing (Route)
type Model
= HomepageModel ()
| CounterModel Pages.Counter.Model
| RandomModel Pages.Random.Model
| NotFoundModel ()
type Msg
= HomepageMsg Never
| CounterMsg Pages.Counter.Msg
| RandomMsg Pages.Random.Msg
| NotFoundMsg Never
pages =
{ homepage =
Page.static
{ view = Pages.Homepage.view
, toModel = HomepageModel
}
, counter =
Page.sandbox
{ init = Pages.Counter.init
, update = Pages.Counter.update
, view = Pages.Counter.view
, toModel = CounterModel
, toMsg = CounterMsg
}
, random =
Page.element
{ init = Pages.Random.init
, update = Pages.Random.update
, subscriptions = Pages.Random.subscriptions
, view = Pages.Random.view
, toModel = RandomModel
, toMsg = RandomMsg
}
, notFound =
Page.static
{ view = Pages.NotFound.view
, toModel = NotFoundModel
}
}
init :
Context Flags Route Context.Model
-> ( Model, Cmd Msg, Cmd Context.Msg )
init context =
case context.route of
Route.Homepage ->
Page.init
{ page = pages.homepage }
context
Route.Counter ->
Page.init
{ page = pages.counter }
context
Route.Random ->
Page.init
{ page = pages.random }
context
Route.NotFound ->
Page.init
{ page = pages.notFound }
context
update :
Context Flags Route Context.Model
-> Msg
-> Model
-> ( Model, Cmd Msg, Cmd Context.Msg )
update context appMsg appModel =
case ( appModel, appMsg ) of
( HomepageModel model, HomepageMsg msg ) ->
Page.update
{ page = pages.homepage
, msg = msg
, model = model
}
context
( HomepageModel _, _ ) ->
( appModel
, Cmd.none
, Cmd.none
)
( CounterModel model, CounterMsg msg ) ->
Page.update
{ page = pages.counter
, msg = msg
, model = model
}
context
( CounterModel _, _ ) ->
( appModel
, Cmd.none
, Cmd.none
)
( RandomModel model, RandomMsg msg ) ->
Page.update
{ page = pages.random
, msg = msg
, model = model
}
context
( RandomModel _, _ ) ->
( appModel
, Cmd.none
, Cmd.none
)
( NotFoundModel model, NotFoundMsg msg ) ->
Page.update
{ page = pages.notFound
, msg = msg
, model = model
}
context
( NotFoundModel _, _ ) ->
( appModel
, Cmd.none
, Cmd.none
)
subscriptions :
Context Flags Route Context.Model
-> Model
-> Sub Msg
subscriptions context appModel =
case appModel of
HomepageModel model ->
Page.subscriptions
{ page = pages.homepage
, model = model
}
context
CounterModel model ->
Page.subscriptions
{ page = pages.counter
, model = model
}
context
RandomModel model ->
Page.subscriptions
{ page = pages.random
, model = model
}
context
NotFoundModel model ->
Page.subscriptions
{ page = pages.notFound
, model = model
}
context
view :
Context Flags Route Context.Model
-> Model
-> Html Msg
view context appModel =
case appModel of
HomepageModel model ->
Page.view
{ page = pages.homepage
, model = model
}
context
CounterModel model ->
Page.view
{ page = pages.counter
, model = model
}
context
RandomModel model ->
Page.view
{ page = pages.random
, model = model
}
context
NotFoundModel model ->
Page.view
{ page = pages.notFound
, model = model
}
context

View File

@ -0,0 +1,108 @@
module Context exposing
( Model
, Msg
, init
, subscriptions
, update
, view
)
import Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes as Attr
import Html.Events as Events
import Route exposing (Route)
type alias Model =
{ user : Maybe String
}
type Msg
= SignIn String
| SignOut
init : Route -> Flags -> ( Model, Cmd Msg )
init route flags =
( { user = Nothing }
, Cmd.none
)
update : Route -> Msg -> Model -> ( Model, Cmd Msg )
update route msg model =
case msg of
SignIn user ->
( { model | user = Just user }
, Cmd.none
)
SignOut ->
( { model | user = Nothing }
, Cmd.none
)
view :
{ route : Route
, context : Model
, toMsg : Msg -> msg
, viewPage : Html msg
}
-> Html msg
view { context, route, toMsg, viewPage } =
div [ Attr.class "layout" ]
[ Html.map toMsg (viewNavbar route context)
, br [] []
, viewPage
, br [] []
, Html.map toMsg (viewFooter context)
]
viewNavbar : Route -> Model -> Html Msg
viewNavbar currentRoute model =
header [ Attr.class "navbar" ]
[ ul []
(List.map
(viewLink currentRoute)
[ Route.Homepage, Route.Counter, Route.Random ]
)
, case model.user of
Just _ ->
button [ Events.onClick SignOut ] [ text <| "Sign out" ]
Nothing ->
button [ Events.onClick (SignIn "Ryan") ] [ text "Sign in" ]
]
viewLink : Route -> Route -> Html msg
viewLink currentRoute route =
li []
[ a
[ Attr.href (Route.toPath route)
, Attr.style "font-weight"
(if route == currentRoute then
"bold"
else
"normal"
)
]
[ text (Route.title route) ]
]
viewFooter : Model -> Html Msg
viewFooter model =
footer [ Attr.class "footer" ]
[ model.user |> Maybe.withDefault "Not signed in" |> text
]
subscriptions : Route -> Model -> Sub Msg
subscriptions route model =
Sub.none

View File

@ -0,0 +1,5 @@
module Flags exposing (Flags)
type alias Flags =
()

View File

@ -0,0 +1,32 @@
module Main exposing (main)
import App
import Application exposing (Application)
import Application.Page exposing (Context)
import Context
import Flags exposing (Flags)
import Html exposing (..)
import Html.Attributes as Attr
import Html.Events as Events
import Route exposing (Route)
main : Application Flags Context.Model Context.Msg App.Model App.Msg
main =
Application.create
{ transition = 200
, context =
{ init = Context.init
, update = Context.update
, view = Context.view
, subscriptions = Context.subscriptions
}
, page =
{ init = App.init
, update = App.update
, view = App.view
, subscriptions = App.subscriptions
}
, toRoute = Route.fromUrl
, title = Route.title
}

View File

@ -0,0 +1,39 @@
module Pages.Counter exposing (Model, Msg, init, update, view)
import Html exposing (..)
import Html.Events as Events
type alias Model =
{ counter : Int
}
type Msg
= Increment
| Decrement
init : Model
init =
{ counter = 0
}
update : Msg -> Model -> Model
update msg model =
case msg of
Decrement ->
{ model | counter = model.counter - 1 }
Increment ->
{ model | counter = model.counter + 1 }
view : Model -> Html Msg
view model =
div []
[ button [ Events.onClick Decrement ] [ text "-" ]
, text (String.fromInt model.counter)
, button [ Events.onClick Increment ] [ text "+" ]
]

View File

@ -0,0 +1,8 @@
module Pages.Homepage exposing (view)
import Html exposing (Html)
view : Html Never
view =
Html.text "Homepage!"

View File

@ -0,0 +1,8 @@
module Pages.NotFound exposing (view)
import Html exposing (Html)
view : Html Never
view =
Html.text "Page not found..."

View File

@ -0,0 +1,67 @@
module Pages.Random exposing
( Model
, Msg
, init
, subscriptions
, update
, view
)
import Flags exposing (Flags)
import Html exposing (..)
import Html.Events as Events
import Random
type alias Model =
{ roll : Maybe Int
}
type Msg
= Roll
| GotOutcome Int
init : Flags -> ( Model, Cmd Msg )
init _ =
( { roll = Nothing }
, Cmd.none
)
rollDice : Model -> ( Model, Cmd Msg )
rollDice model =
( model
, Random.generate GotOutcome (Random.int 1 6)
)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Roll ->
rollDice model
GotOutcome value ->
( { model | roll = Just value }
, Cmd.none
)
view : Model -> Html Msg
view model =
div []
[ button [ Events.onClick Roll ] [ text "Roll" ]
, p []
[ model.roll
|> Maybe.map String.fromInt
|> Maybe.withDefault "Click the button!"
|> text
]
]
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none

View File

@ -0,0 +1,58 @@
module Route exposing (Route(..), fromUrl, title, toPath)
import Url exposing (Url)
import Url.Parser as Parser exposing (Parser)
type Route
= Homepage
| Counter
| Random
| NotFound
fromUrl : Url -> Route
fromUrl =
Parser.parse router >> Maybe.withDefault NotFound
router : Parser (Route -> Route) Route
router =
Parser.oneOf
[ Parser.map Homepage Parser.top
, Parser.map Counter (Parser.s "counter")
, Parser.map Random (Parser.s "random")
]
toPath : Route -> String
toPath route =
(String.join "/" >> (++) "/") <|
case route of
Homepage ->
[]
Counter ->
[ "counter" ]
Random ->
[ "random" ]
NotFound ->
[ "not-found" ]
title : Route -> String
title route =
case route of
Homepage ->
"Home"
Counter ->
"Counter"
Random ->
"Random"
NotFound ->
"Not found"

306
src/Application.elm Normal file
View File

@ -0,0 +1,306 @@
module Application exposing (Application, create)
import Application.Page exposing (Context)
import Browser
import Browser.Navigation as Nav
import Html exposing (Html, div)
import Html.Attributes as Attr
import Process
import Task
import Url exposing (Url)
type alias Application flags contextModel contextMsg model msg =
Program flags (Model flags contextModel model) (Msg contextMsg msg)
type alias Model flags contextModel model =
{ key : Nav.Key
, url : Url
, flags : flags
, context : contextModel
, page : Loadable model
}
type Loadable a
= FirstLoad a
| Loading a
| Loaded a
isFirstLoad : Loadable a -> Bool
isFirstLoad loadable =
case loadable of
FirstLoad _ ->
True
_ ->
False
unwrap : Loadable a -> a
unwrap loadable =
case loadable of
FirstLoad a ->
a
Loading a ->
a
Loaded a ->
a
map : (a -> b) -> Loadable a -> Loadable b
map fn loadable =
case loadable of
FirstLoad a ->
FirstLoad (fn a)
Loading a ->
Loading (fn a)
Loaded a ->
Loaded (fn a)
type Msg contextMsg msg
= UrlChanged Url
| UrlRequested Browser.UrlRequest
| PageLoaded Url
| ContextMsg contextMsg
| PageMsg msg
type alias Config flags route contextModel contextMsg model msg =
{ context :
{ init : route -> flags -> ( contextModel, Cmd contextMsg )
, update : route -> contextMsg -> contextModel -> ( contextModel, Cmd contextMsg )
, subscriptions : route -> contextModel -> Sub contextMsg
, view :
{ route : route
, context : contextModel
, toMsg : contextMsg -> Msg contextMsg msg
, viewPage : Html (Msg contextMsg msg)
}
-> Html (Msg contextMsg msg)
}
, page :
{ init : Context flags route contextModel -> ( model, Cmd msg, Cmd contextMsg )
, update : Context flags route contextModel -> msg -> model -> ( model, Cmd msg, Cmd contextMsg )
, subscriptions : Context flags route contextModel -> model -> Sub msg
, view : Context flags route contextModel -> model -> Html msg
}
, toRoute : Url -> route
, title : route -> String
, transition : Float
}
create :
Config flags route contextModel contextMsg model msg
-> Application flags contextModel contextMsg model msg
create config =
Browser.application
{ init = init config
, update = update config
, view = view config
, subscriptions = subscriptions config
, onUrlChange = UrlChanged
, onUrlRequest = UrlRequested
}
init :
Config flags route contextModel contextMsg model msg
-> flags
-> Url
-> Nav.Key
-> ( Model flags contextModel model, Cmd (Msg contextMsg msg) )
init config flags url key =
let
route =
config.toRoute url
( contextModel, contextCmd ) =
config.context.init route flags
( pageModel, pageCmd, pageContextCmd ) =
config.page.init
{ route = route
, flags = flags
, context = contextModel
}
in
( { url = url
, key = key
, flags = flags
, context = contextModel
, page = FirstLoad pageModel
}
, Cmd.batch
[ delay config.transition (PageLoaded url)
, Cmd.map ContextMsg contextCmd
, Cmd.map ContextMsg pageContextCmd
, Cmd.map PageMsg pageCmd
]
)
delay : Float -> msg -> Cmd msg
delay ms msg =
Task.perform (\_ -> msg) (Process.sleep ms)
update :
Config flags route contextModel contextMsg model msg
-> Msg contextMsg msg
-> Model flags contextModel model
-> ( Model flags contextModel model, Cmd (Msg contextMsg msg) )
update config msg model =
case msg of
UrlRequested urlRequest ->
case urlRequest of
Browser.Internal url ->
( model
, Nav.pushUrl model.key (Url.toString url)
)
Browser.External url ->
( model
, Nav.load url
)
UrlChanged url ->
( { model | page = Loading (unwrap model.page) }
, delay config.transition (PageLoaded url)
)
PageLoaded url ->
let
( pageModel, pageCmd, contextCmd ) =
config.page.init
{ route = config.toRoute url
, flags = model.flags
, context = model.context
}
in
( { model | url = url, page = Loaded pageModel }
, Cmd.batch
[ Cmd.map PageMsg pageCmd
, Cmd.map ContextMsg contextCmd
]
)
ContextMsg msg_ ->
Tuple.mapBoth
(\context -> { model | context = context })
(Cmd.map ContextMsg)
(config.context.update (config.toRoute model.url) msg_ model.context)
PageMsg msg_ ->
let
( pageModel, pageCmd, contextCmd ) =
config.page.update
{ route = config.toRoute model.url
, flags = model.flags
, context = model.context
}
msg_
(unwrap model.page)
in
( { model | page = map (always pageModel) model.page }
, Cmd.batch
[ Cmd.map ContextMsg contextCmd
, Cmd.map PageMsg pageCmd
]
)
type alias Document msg =
{ title : String
, body : List (Html msg)
}
view :
Config flags route contextModel contextMsg model msg
-> Model flags contextModel model
-> Document (Msg contextMsg msg)
view config model =
let
transitionProp : Float -> String
transitionProp ms =
"opacity " ++ String.fromFloat ms ++ "ms ease-in-out"
layoutOpacity : Loadable a -> String
layoutOpacity loadable =
case loadable of
FirstLoad _ ->
"0"
Loading _ ->
"1"
Loaded _ ->
"1"
pageOpacity : Loadable a -> String
pageOpacity loadable =
case loadable of
FirstLoad _ ->
"0"
Loading _ ->
"0"
Loaded _ ->
"1"
in
{ title = config.title (config.toRoute model.url)
, body =
[ div
[ Attr.style "transition" (transitionProp config.transition)
, Attr.style "opacity" (layoutOpacity model.page)
]
[ config.context.view
{ route = config.toRoute model.url
, toMsg = ContextMsg
, context = model.context
, viewPage =
div
[ Attr.style "transition" (transitionProp config.transition)
, Attr.style "opacity" (pageOpacity model.page)
]
[ Html.map PageMsg
(config.page.view
{ route = config.toRoute model.url
, flags = model.flags
, context = model.context
}
(unwrap model.page)
)
]
}
]
]
}
subscriptions :
Config flags route contextModel contextMsg model msg
-> Model flags contextModel model
-> Sub (Msg contextMsg msg)
subscriptions config model =
Sub.batch
[ Sub.map ContextMsg (config.context.subscriptions (config.toRoute model.url) model.context)
, Sub.map PageMsg
(config.page.subscriptions
{ route = config.toRoute model.url
, flags = model.flags
, context = model.context
}
(unwrap model.page)
)
]

185
src/Application/Page.elm Normal file
View File

@ -0,0 +1,185 @@
module Application.Page exposing
( Context
, Page
, element
, init
, page
, sandbox
, static
, subscriptions
, update
, view
)
import Html exposing (Html)
type alias Context flags route contextModel =
{ flags : flags
, route : route
, context : contextModel
}
type alias Page route flags contextModel contextMsg model msg appModel appMsg =
{ init : Context flags route contextModel -> ( model, Cmd msg, Cmd contextMsg )
, update : Context flags route contextModel -> msg -> model -> ( model, Cmd msg, Cmd contextMsg )
, subscriptions : Context flags route contextModel -> model -> Sub msg
, view : Context flags route contextModel -> model -> Html msg
, toMsg : msg -> appMsg
, toModel : model -> appModel
}
-- PAGE HELPERS
init :
{ page : Page route flags contextModel contextMsg model msg appModel appMsg
}
-> Context flags route contextModel
-> ( appModel, Cmd appMsg, Cmd contextMsg )
init config context =
config.page.init context
|> mapTruple
{ fromMsg = config.page.toMsg
, fromModel = config.page.toModel
}
update :
{ page : Page route flags contextModel contextMsg model msg appModel appMsg
, msg : msg
, model : model
}
-> Context flags route contextModel
-> ( appModel, Cmd appMsg, Cmd contextMsg )
update config context =
config.page.update context config.msg config.model
|> mapTruple
{ fromMsg = config.page.toMsg
, fromModel = config.page.toModel
}
subscriptions :
{ page : Page route flags contextModel contextMsg model msg appModel appMsg
, model : model
}
-> Context flags route contextModel
-> Sub appMsg
subscriptions config context =
config.page.subscriptions context config.model
|> Sub.map config.page.toMsg
view :
{ page : Page route flags contextModel contextMsg model msg appModel appMsg
, model : model
}
-> Context flags route contextModel
-> Html appMsg
view config context =
config.page.view context config.model
|> Html.map config.page.toMsg
-- PAGE ADAPTERS
static :
{ view : Html Never
, toModel : () -> appModel
}
-> Page route flags contextModel contextMsg () Never appModel appMsg
static config =
{ init = \c -> ( (), Cmd.none, Cmd.none )
, update = \c m model -> ( model, Cmd.none, Cmd.none )
, subscriptions = \c m -> Sub.none
, view = \c m -> Html.map never config.view
, toMsg = never
, toModel = config.toModel
}
sandbox :
{ init : model
, update : msg -> model -> model
, view : model -> Html msg
, toMsg : msg -> appMsg
, toModel : model -> appModel
}
-> Page route flags contextModel contextMsg model msg appModel appMsg
sandbox config =
{ init = \c -> ( config.init, Cmd.none, Cmd.none )
, update = \c msg model -> ( config.update msg model, Cmd.none, Cmd.none )
, subscriptions = \c m -> Sub.none
, view = \c model -> config.view model
, toMsg = config.toMsg
, toModel = config.toModel
}
element :
{ init : flags -> ( model, Cmd msg )
, update : msg -> model -> ( model, Cmd msg )
, subscriptions : model -> Sub msg
, view : model -> Html msg
, toMsg : msg -> appMsg
, toModel : model -> appModel
}
-> Page route flags contextModel contextMsg model msg appModel appMsg
element config =
let
appendCmd ( model, cmd ) =
( model, cmd, Cmd.none )
in
{ init = \c -> config.init c.flags |> appendCmd
, update = \c msg model -> config.update msg model |> appendCmd
, subscriptions = \c model -> config.subscriptions model
, view = \c model -> config.view model
, toMsg = config.toMsg
, toModel = config.toModel
}
page :
{ init : Context flags route contextModel -> ( model, Cmd msg )
, update : Context flags route contextModel -> msg -> model -> ( model, Cmd msg )
, subscriptions : Context flags route contextModel -> model -> Sub msg
, view : Context flags route contextModel -> model -> Html msg
, toMsg : msg -> appMsg
, toModel : model -> appModel
}
-> Page route flags contextModel contextMsg model msg appModel appMsg
page config =
let
appendCmd ( model, cmd ) =
( model, cmd, Cmd.none )
in
{ init = \c -> config.init c |> appendCmd
, update = \c msg model -> config.update c msg model |> appendCmd
, subscriptions = \c model -> config.subscriptions c model
, view = \c model -> config.view c model
, toMsg = config.toMsg
, toModel = config.toModel
}
-- UTILS
mapTruple :
{ fromMsg : msg -> appMsg
, fromModel : model -> appModel
}
-> ( model, Cmd msg, Cmd contextMsg )
-> ( appModel, Cmd appMsg, Cmd contextMsg )
mapTruple { fromModel, fromMsg } ( a, b, c ) =
( fromModel a
, Cmd.map fromMsg b
, c
)