mirror of
https://github.com/ryannhg/elm-spa.git
synced 2024-11-25 23:43:02 +03:00
new effect api
This commit is contained in:
parent
6c8e1f1429
commit
86798cf5ea
@ -1,3 +1,7 @@
|
||||
[build]
|
||||
publish = "public"
|
||||
command = "npm i elm-spa@beta && npx elm-spa build"
|
||||
|
||||
[[redirects]]
|
||||
from = "/*"
|
||||
to = "/index.html"
|
||||
|
1
docs/public/content/examples.md
Normal file
1
docs/public/content/examples.md
Normal file
@ -0,0 +1 @@
|
||||
# Examples
|
1
docs/public/content/examples/authentication.md
Normal file
1
docs/public/content/examples/authentication.md
Normal file
@ -0,0 +1 @@
|
||||
# User Authentication
|
@ -3,7 +3,7 @@
|
||||
The [official __elm-spa__ CLI tool](https://npmjs.org/elm-spa) has a few commands to help you build single page applications. As we saw in [the previous section](/guide/overview), you can use the CLI from your terminal by running:
|
||||
|
||||
```terminal
|
||||
npm install -g elm-spa@latest
|
||||
npm install -g elm-spa@beta
|
||||
```
|
||||
|
||||
At any time running `elm-spa` or `elm-spa help` will show you the available commands:
|
||||
@ -24,20 +24,22 @@ The `new` command creates a new project in the current folder:
|
||||
elm-spa new
|
||||
```
|
||||
|
||||
This command will only create a few files, so don't worry about getting overwhelmed with new files in your repo! Other than a `.gitignore`, there are only 2 new files created.
|
||||
This command will only create a few files, so don't worry about getting overwhelmed with new files in your repo! Other than a `.gitignore`, there are only 3 new files created.
|
||||
|
||||
File | Description
|
||||
--- | ---
|
||||
`elm.json` | Your project's dependencies.
|
||||
`src` | An empty folder for your Elm code.
|
||||
`src/Pages/Home_.elm` | The homepage.
|
||||
`public/index.html` | The entrypoint to your application.
|
||||
|
||||
```
|
||||
your-project/
|
||||
- elm.json
|
||||
- src/
|
||||
- public/
|
||||
- index.html
|
||||
|- elm.json
|
||||
|- src/
|
||||
| |- Pages/
|
||||
| |- Home_.elm
|
||||
|- public/
|
||||
|- index.html
|
||||
```
|
||||
|
||||
The `public` folder is a place for static assets! For example, a file at `./public/style.css` will be available at `/style.css` in your web browser.
|
||||
|
@ -65,8 +65,8 @@ pre {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
main .markdown {
|
||||
animation: fadeIn 200ms 300ms ease-in forwards;
|
||||
main {
|
||||
animation: fadeIn 200ms 400ms ease-in forwards;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,14 @@
|
||||
module Domain.Index exposing
|
||||
( Index, decoder
|
||||
, Link, search
|
||||
, Section, sections
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
@docs Index, decoder
|
||||
@docs Link, search
|
||||
@docs Section, sections
|
||||
|
||||
-}
|
||||
|
||||
@ -81,3 +83,73 @@ search query index =
|
||||
}
|
||||
)
|
||||
|> List.filter (\link -> Utils.String.caseInsensitiveContains query link.label)
|
||||
|
||||
|
||||
|
||||
-- SECTIONS
|
||||
|
||||
|
||||
type alias Section =
|
||||
{ header : String
|
||||
, url : String
|
||||
, pages : List SectionLink
|
||||
}
|
||||
|
||||
|
||||
type alias SectionLink =
|
||||
{ label : String
|
||||
, url : String
|
||||
}
|
||||
|
||||
|
||||
sections : Index -> List Section
|
||||
sections index =
|
||||
let
|
||||
sectionOrder =
|
||||
[ "Guide"
|
||||
, "Examples"
|
||||
]
|
||||
|
||||
toLabelUrls =
|
||||
List.filterMap
|
||||
(\doc ->
|
||||
doc.headers
|
||||
|> Dict.filter (\_ level -> level == 1)
|
||||
|> Dict.toList
|
||||
|> List.head
|
||||
|> Maybe.map (Tuple.first >> (\label -> { label = label, url = doc.url }))
|
||||
)
|
||||
|
||||
topLevelLabelUrls : List { label : String, url : String }
|
||||
topLevelLabelUrls =
|
||||
let
|
||||
isOneLevelDeep doc =
|
||||
List.length (String.split "/" doc.url) == 2
|
||||
in
|
||||
index
|
||||
|> List.filter isOneLevelDeep
|
||||
|> toLabelUrls
|
||||
|
||||
toSection top children =
|
||||
{ header = top.label
|
||||
, url = top.url
|
||||
, pages = children
|
||||
}
|
||||
in
|
||||
topLevelLabelUrls
|
||||
|> List.map
|
||||
(\top ->
|
||||
index
|
||||
|> List.filter (.url >> (\url -> String.startsWith top.url url && url /= top.url))
|
||||
|> toLabelUrls
|
||||
|> toSection top
|
||||
)
|
||||
|> List.sortBy
|
||||
(\section ->
|
||||
sectionOrder
|
||||
|> List.indexedMap Tuple.pair
|
||||
|> List.filter (Tuple.second >> (==) section.header)
|
||||
|> List.map Tuple.first
|
||||
|> List.head
|
||||
|> Maybe.withDefault -1
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
module Pages.Guide.Section_.Article_ exposing (Model, Msg, page)
|
||||
module Pages.Examples exposing (Model, Msg, page)
|
||||
|
||||
import Page exposing (Page)
|
||||
import Request exposing (Request)
|
19
docs/src/Pages/Examples/Section_.elm
Normal file
19
docs/src/Pages/Examples/Section_.elm
Normal file
@ -0,0 +1,19 @@
|
||||
module Pages.Examples.Section_ exposing (Model, Msg, page)
|
||||
|
||||
import Page exposing (Page)
|
||||
import Request exposing (Request)
|
||||
import Shared
|
||||
import UI.Docs
|
||||
|
||||
|
||||
page : Shared.Model -> Request params -> Page Model Msg
|
||||
page =
|
||||
UI.Docs.page
|
||||
|
||||
|
||||
type alias Model =
|
||||
UI.Docs.Model
|
||||
|
||||
|
||||
type alias Msg =
|
||||
UI.Docs.Msg
|
@ -6,7 +6,6 @@ import Request exposing (Request)
|
||||
import Shared
|
||||
import UI
|
||||
import UI.Layout
|
||||
import Url exposing (Url)
|
||||
import View exposing (View)
|
||||
|
||||
|
||||
|
@ -6,7 +6,6 @@ import Request exposing (Request)
|
||||
import Shared
|
||||
import UI
|
||||
import UI.Layout
|
||||
import Url exposing (Url)
|
||||
import View exposing (View)
|
||||
|
||||
|
||||
|
@ -21,9 +21,14 @@ type alias Flags =
|
||||
|
||||
type alias Model =
|
||||
{ index : Index
|
||||
, token : Maybe Token
|
||||
}
|
||||
|
||||
|
||||
type alias Token =
|
||||
()
|
||||
|
||||
|
||||
type Msg
|
||||
= NoOp
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
module UI.Docs exposing (Model, Msg, page)
|
||||
|
||||
import Gen.Route as Route
|
||||
import Http
|
||||
import Page exposing (Page)
|
||||
import Request exposing (Request)
|
||||
@ -47,16 +46,6 @@ withDefault fallback fetchable =
|
||||
fallback
|
||||
|
||||
|
||||
toMaybe : Fetchable value -> Maybe value
|
||||
toMaybe fetchable =
|
||||
case fetchable of
|
||||
Success value ->
|
||||
Just value
|
||||
|
||||
_ ->
|
||||
Nothing
|
||||
|
||||
|
||||
init : Url -> ( Model, Cmd Msg )
|
||||
init url =
|
||||
( Model UI.Layout.init Loading
|
||||
|
@ -15,7 +15,6 @@ module UI.Layout exposing
|
||||
|
||||
import Html exposing (Html)
|
||||
import Html.Attributes as Attr
|
||||
import Html.Events as Events
|
||||
import Page exposing (Page, shared)
|
||||
import Request exposing (Request)
|
||||
import Shared
|
||||
@ -81,12 +80,16 @@ viewDocumentation options markdownContent view =
|
||||
, url = options.url
|
||||
}
|
||||
]
|
||||
, Html.main_ [ Attr.class "col flex" ] view
|
||||
, Html.div [ Attr.class "hidden-mobile sticky pad-y-lg", Attr.style "width" "16em" ]
|
||||
[ UI.Sidebar.viewTableOfContents
|
||||
{ content = markdownContent
|
||||
, url = options.url
|
||||
}
|
||||
, Html.main_ [ Attr.class "flex" ]
|
||||
[ UI.row.lg [ UI.align.top ]
|
||||
[ Html.div [ Attr.class "col flex" ] view
|
||||
, Html.div [ Attr.class "hidden-mobile sticky pad-y-lg", Attr.style "width" "16em" ]
|
||||
[ UI.Sidebar.viewTableOfContents
|
||||
{ content = markdownContent
|
||||
, url = options.url
|
||||
}
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
@ -12,27 +12,6 @@ import Url exposing (Url)
|
||||
import Utils.String
|
||||
|
||||
|
||||
sidebarSections : List Section
|
||||
sidebarSections =
|
||||
[ Section "Guide"
|
||||
"/guide"
|
||||
[ Link "Overview" "/guide"
|
||||
, Link "The CLI" "/guide/cli"
|
||||
, Link "Routing" "/guide/routing"
|
||||
, Link "Pages" "/guide/pages"
|
||||
, Link "Shared State" "/guide/shared-state"
|
||||
, Link "Requests" "/guide/requests"
|
||||
, Link "Views" "/guide/views"
|
||||
]
|
||||
, Section "Examples"
|
||||
"/guide"
|
||||
[ Link "User Authentication" "/guide/users"
|
||||
, Link "Elm UI" "/guide/apis"
|
||||
, Link "Page Transitions" "/guide/transitions"
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
parseTableOfContents : String -> List Section
|
||||
parseTableOfContents =
|
||||
Markdown.Parser.parse
|
||||
@ -46,12 +25,12 @@ parseTableOfContents =
|
||||
type alias Section =
|
||||
{ header : String
|
||||
, url : String
|
||||
, links : List Link
|
||||
, pages : List Link
|
||||
}
|
||||
|
||||
|
||||
type alias Link =
|
||||
{ name : String
|
||||
{ label : String
|
||||
, url : String
|
||||
}
|
||||
|
||||
@ -76,16 +55,16 @@ headersToSections =
|
||||
in
|
||||
case ( level, current ) of
|
||||
( Heading2, Just existing ) ->
|
||||
( sections ++ [ existing ], Just { header = text, url = url, links = [] } )
|
||||
( sections ++ [ existing ], Just { header = text, url = url, pages = [] } )
|
||||
|
||||
( Heading2, Nothing ) ->
|
||||
( sections, Just { header = text, url = url, links = [] } )
|
||||
( sections, Just { header = text, url = url, pages = [] } )
|
||||
|
||||
( Heading3, Just existing ) ->
|
||||
( sections, Just { existing | links = existing.links ++ [ { name = text, url = url } ] } )
|
||||
( sections, Just { existing | pages = existing.pages ++ [ { label = text, url = url } ] } )
|
||||
|
||||
( Heading3, Nothing ) ->
|
||||
( sections ++ [ { header = text, url = url, links = [] } ], Nothing )
|
||||
( sections ++ [ { header = text, url = url, pages = [] } ], Nothing )
|
||||
in
|
||||
List.foldl loop ( [], Nothing )
|
||||
>> (\( sections, maybe ) ->
|
||||
@ -135,7 +114,7 @@ tableOfContentsRenderer =
|
||||
|
||||
|
||||
viewSidebar : { url : Url, index : Index } -> Html msg
|
||||
viewSidebar { url } =
|
||||
viewSidebar { url, index } =
|
||||
let
|
||||
viewSidebarLink : Link -> Html msg
|
||||
viewSidebarLink link__ =
|
||||
@ -144,15 +123,15 @@ viewSidebar { url } =
|
||||
viewSidebarSection : Section -> Html msg
|
||||
viewSidebarSection section =
|
||||
UI.col.sm []
|
||||
[ Html.h4 [ Attr.class "h4 bold" ] [ Html.text section.header ]
|
||||
, if List.isEmpty section.links then
|
||||
[ Html.a [ Attr.href section.url, Attr.class "h4 bold" ] [ Html.text section.header ]
|
||||
, if List.isEmpty section.pages then
|
||||
Html.text ""
|
||||
|
||||
else
|
||||
UI.col.md [ Attr.class "border-left pad-y-sm pad-x-md align-left" ] (List.map viewSidebarLink section.links)
|
||||
UI.col.md [ Attr.class "border-left pad-y-sm pad-x-md align-left" ] (List.map viewSidebarLink section.pages)
|
||||
]
|
||||
in
|
||||
UI.col.md [] (List.map viewSidebarSection sidebarSections)
|
||||
UI.col.md [] (List.map viewSidebarSection (Domain.Index.sections index))
|
||||
|
||||
|
||||
viewDocumentationLink : Bool -> Link -> Html msg
|
||||
@ -162,7 +141,7 @@ viewDocumentationLink isActive link__ =
|
||||
, Attr.classList [ ( "bold text-blue", isActive ) ]
|
||||
, Attr.href link__.url
|
||||
]
|
||||
[ Html.text link__.name ]
|
||||
[ Html.text link__.label ]
|
||||
|
||||
|
||||
viewTableOfContents : { url : Url, content : String } -> Html msg
|
||||
@ -175,13 +154,13 @@ viewTableOfContents { url, content } =
|
||||
viewTocSection : Section -> Html msg
|
||||
viewTocSection section =
|
||||
Html.div [ Attr.class "col gap-xs align-left" ]
|
||||
[ viewTableOfContentsLink { name = section.header, url = section.url }
|
||||
, if List.isEmpty section.links then
|
||||
[ viewTableOfContentsLink { label = section.header, url = section.url }
|
||||
, if List.isEmpty section.pages then
|
||||
Html.text ""
|
||||
|
||||
else
|
||||
Html.div [ Attr.class "col pad-left-sm pad-xs gap-sm" ]
|
||||
(section.links
|
||||
(section.pages
|
||||
|> List.map (\l -> Html.div [ Attr.class "h6" ] [ viewTableOfContentsLink l ])
|
||||
)
|
||||
]
|
||||
|
5
elm.json
5
elm.json
@ -3,11 +3,12 @@
|
||||
"name": "ryannhg/elm-spa",
|
||||
"summary": "Single page apps made easy.",
|
||||
"license": "BSD-3-Clause",
|
||||
"version": "5.0.0",
|
||||
"version": "5.1.0",
|
||||
"exposed-modules": [
|
||||
"ElmSpa.Request",
|
||||
"ElmSpa.Page",
|
||||
"ElmSpa.Beta"
|
||||
"ElmSpa.Beta",
|
||||
"ElmSpa.Internals.Page"
|
||||
],
|
||||
"elm-version": "0.19.0 <= v < 0.20.0",
|
||||
"dependencies": {
|
||||
|
427
src/ElmSpa/Internals/Page.elm
Normal file
427
src/ElmSpa/Internals/Page.elm
Normal file
@ -0,0 +1,427 @@
|
||||
module ElmSpa.Internals.Page exposing
|
||||
( Page
|
||||
, static, sandbox, element, advanced
|
||||
, protected
|
||||
, Bundle, bundle
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
|
||||
# **Pages**
|
||||
|
||||
@docs Page
|
||||
|
||||
@docs static, sandbox, element, advanced
|
||||
|
||||
|
||||
# **User Authentication**
|
||||
|
||||
@docs protected
|
||||
|
||||
|
||||
# For generated code
|
||||
|
||||
@docs Bundle, bundle
|
||||
|
||||
-}
|
||||
|
||||
import Browser.Navigation exposing (Key)
|
||||
import ElmSpa.Request
|
||||
import Url exposing (Url)
|
||||
|
||||
|
||||
{-| Pages are the building blocks of **elm-spa**.
|
||||
|
||||
Instead of importing this module, your project will have a `Page` module with a much simpler type:
|
||||
|
||||
module Page exposing (Page, ...)
|
||||
|
||||
type Page model msg
|
||||
|
||||
This makes all the generic `route`, `effect`, and `view` arguments disappear!
|
||||
|
||||
-}
|
||||
type Page shared request route effect view model msg
|
||||
= Page (Internals shared request route effect view model msg)
|
||||
|
||||
|
||||
{-| A page that only needs to render a static view.
|
||||
|
||||
import Page
|
||||
|
||||
page : Page () Never
|
||||
page =
|
||||
Page.static
|
||||
{ view = view
|
||||
}
|
||||
|
||||
-- view : View Never
|
||||
|
||||
-}
|
||||
static :
|
||||
effect
|
||||
->
|
||||
{ view : view
|
||||
}
|
||||
-> Page shared request route effect view () msg
|
||||
static none page =
|
||||
Page
|
||||
(\_ _ ->
|
||||
Ok
|
||||
{ init = \_ -> ( (), none )
|
||||
, update = \_ _ -> ( (), none )
|
||||
, view = \_ -> page.view
|
||||
, subscriptions = \_ -> Sub.none
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
{-| A page that can keep track of application state.
|
||||
|
||||
( Inspired by [`Browser.sandbox`](https://package.elm-lang.org/packages/elm/browser/latest/Browser#sandbox) )
|
||||
|
||||
import Page
|
||||
|
||||
page : Page Model Msg
|
||||
page =
|
||||
Page.sandbox
|
||||
{ init = init
|
||||
, update = update
|
||||
, view = view
|
||||
}
|
||||
|
||||
-- init : Model
|
||||
-- update : Msg -> Model -> Model
|
||||
-- view : Model -> View Msg
|
||||
|
||||
-}
|
||||
sandbox :
|
||||
effect
|
||||
->
|
||||
{ init : model
|
||||
, update : msg -> model -> model
|
||||
, view : model -> view
|
||||
}
|
||||
-> Page shared request route effect view model msg
|
||||
sandbox none page =
|
||||
Page
|
||||
(\_ _ ->
|
||||
Ok
|
||||
{ init = \_ -> ( page.init, none )
|
||||
, update = \msg model -> ( page.update msg model, none )
|
||||
, view = page.view
|
||||
, subscriptions = \_ -> Sub.none
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
{-| A page that can handle effects like [HTTP requests or subscriptions](https://guide.elm-lang.org/effects/).
|
||||
|
||||
( Inspired by [`Browser.element`](https://package.elm-lang.org/packages/elm/browser/latest/Browser#element) )
|
||||
|
||||
import Page
|
||||
|
||||
page : Page Model Msg
|
||||
page =
|
||||
Page.element
|
||||
{ init = init
|
||||
, update = update
|
||||
, view = view
|
||||
, subscriptions = subscriptions
|
||||
}
|
||||
|
||||
-- init : ( Model, Cmd Msg )
|
||||
-- update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
-- view : Model -> View Msg
|
||||
-- subscriptions : Model -> Sub Msg
|
||||
|
||||
-}
|
||||
element :
|
||||
(Cmd msg -> effect)
|
||||
->
|
||||
{ init : ( model, Cmd msg )
|
||||
, update : msg -> model -> ( model, Cmd msg )
|
||||
, view : model -> view
|
||||
, subscriptions : model -> Sub msg
|
||||
}
|
||||
-> Page shared request route effect view model msg
|
||||
element fromCmd page =
|
||||
Page
|
||||
(\_ _ ->
|
||||
Ok
|
||||
{ init = \_ -> page.init |> Tuple.mapSecond fromCmd
|
||||
, update = \msg model -> page.update msg model |> Tuple.mapSecond fromCmd
|
||||
, view = page.view
|
||||
, subscriptions = page.subscriptions
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
{-| A page that can handles **custom** effects like sending a `Shared.Msg` or other general user-defined effects.
|
||||
|
||||
import Effect
|
||||
import Page
|
||||
|
||||
page : Page Model Msg
|
||||
page =
|
||||
Page.advanced
|
||||
{ init = init
|
||||
, update = update
|
||||
, view = view
|
||||
, subscriptions = subscriptions
|
||||
}
|
||||
|
||||
-- init : ( Model, Effect Msg )
|
||||
-- update : Msg -> Model -> ( Model, Effect Msg )
|
||||
-- view : Model -> View Msg
|
||||
-- subscriptions : Model -> Sub Msg
|
||||
|
||||
-}
|
||||
advanced :
|
||||
{ init : ( model, effect )
|
||||
, update : msg -> model -> ( model, effect )
|
||||
, view : model -> view
|
||||
, subscriptions : model -> Sub msg
|
||||
}
|
||||
-> Page shared request route effect view model msg
|
||||
advanced page =
|
||||
Page
|
||||
(\_ _ ->
|
||||
Ok
|
||||
{ init = always page.init
|
||||
, update = page.update
|
||||
, view = page.view
|
||||
, subscriptions = page.subscriptions
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
{-| Prefixing any of the four functions above with `protected` will guarantee that the page has access to a user. Here's an example with `sandbox`:
|
||||
|
||||
import Page
|
||||
|
||||
page : Page Model Msg
|
||||
page =
|
||||
Page.protected.sandbox
|
||||
{ init = init
|
||||
, update = update
|
||||
, view = view
|
||||
}
|
||||
|
||||
-- init : User -> Model
|
||||
-- update : User -> Msg -> Model -> Model
|
||||
-- update : User -> Model -> View Msg
|
||||
|
||||
-}
|
||||
protected :
|
||||
{ effectNone : effect
|
||||
, fromCmd : Cmd msg -> effect
|
||||
, user : shared -> request -> Maybe user
|
||||
, route : route
|
||||
}
|
||||
->
|
||||
{ static :
|
||||
{ view : user -> view
|
||||
}
|
||||
-> Page shared request route effect view () msg
|
||||
, sandbox :
|
||||
{ init : user -> model
|
||||
, update : user -> msg -> model -> model
|
||||
, view : user -> model -> view
|
||||
}
|
||||
-> Page shared request route effect view model msg
|
||||
, element :
|
||||
{ init : user -> ( model, Cmd msg )
|
||||
, update : user -> msg -> model -> ( model, Cmd msg )
|
||||
, view : user -> model -> view
|
||||
, subscriptions : user -> model -> Sub msg
|
||||
}
|
||||
-> Page shared request route effect view model msg
|
||||
, advanced :
|
||||
{ init : user -> ( model, effect )
|
||||
, update : user -> msg -> model -> ( model, effect )
|
||||
, view : user -> model -> view
|
||||
, subscriptions : user -> model -> Sub msg
|
||||
}
|
||||
-> Page shared request route effect view model msg
|
||||
}
|
||||
protected options =
|
||||
let
|
||||
protect pageWithUser page =
|
||||
Page
|
||||
(\shared req ->
|
||||
case options.user shared req of
|
||||
Just user ->
|
||||
Ok (pageWithUser user page)
|
||||
|
||||
Nothing ->
|
||||
Err options.route
|
||||
)
|
||||
in
|
||||
{ static =
|
||||
protect
|
||||
(\user page ->
|
||||
{ init = \_ -> ( (), options.effectNone )
|
||||
, update = \_ model -> ( model, options.effectNone )
|
||||
, view = \_ -> page.view user
|
||||
, subscriptions = \_ -> Sub.none
|
||||
}
|
||||
)
|
||||
, sandbox =
|
||||
protect
|
||||
(\user page ->
|
||||
{ init = \_ -> ( page.init user, options.effectNone )
|
||||
, update = \msg model -> ( page.update user msg model, options.effectNone )
|
||||
, view = page.view user
|
||||
, subscriptions = \_ -> Sub.none
|
||||
}
|
||||
)
|
||||
, element =
|
||||
protect
|
||||
(\user page ->
|
||||
{ init = \_ -> page.init user |> Tuple.mapSecond options.fromCmd
|
||||
, update = \msg model -> page.update user msg model |> Tuple.mapSecond options.fromCmd
|
||||
, view = page.view user
|
||||
, subscriptions = page.subscriptions user
|
||||
}
|
||||
)
|
||||
, advanced =
|
||||
protect
|
||||
(\user page ->
|
||||
{ init = \_ -> page.init user
|
||||
, update = page.update user
|
||||
, view = page.view user
|
||||
, subscriptions = page.subscriptions user
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- UPGRADING FOR GENERATED CODE
|
||||
|
||||
|
||||
type alias Request route params =
|
||||
ElmSpa.Request.Request route params
|
||||
|
||||
|
||||
{-| -}
|
||||
type alias Bundle params model msg shared effect pagesModel pagesMsg pagesView =
|
||||
{ init : params -> shared -> Url -> Key -> ( pagesModel, effect )
|
||||
, update : params -> msg -> model -> shared -> Url -> Key -> ( pagesModel, effect )
|
||||
, view : params -> model -> shared -> Url -> Key -> pagesView
|
||||
, subscriptions : params -> model -> shared -> Url -> Key -> Sub pagesMsg
|
||||
}
|
||||
|
||||
|
||||
{-| This function is used by the generated code to connect your pages together.
|
||||
|
||||
It's big, spooky, and makes writing **elm-spa** pages really nice!
|
||||
|
||||
-}
|
||||
bundle :
|
||||
{ redirecting : { model : pagesModel, view : pagesView }
|
||||
, toRoute : Url -> route
|
||||
, toUrl : route -> String
|
||||
, fromCmd : Cmd any -> pagesEffect
|
||||
, mapEffect : effect -> pagesEffect
|
||||
, mapView : view -> pagesView
|
||||
, page : shared -> Request route params -> Page shared (Request route params) route effect view model msg
|
||||
, toModel : params -> model -> pagesModel
|
||||
, toMsg : msg -> pagesMsg
|
||||
}
|
||||
-> Bundle params model msg shared pagesEffect pagesModel pagesMsg pagesView
|
||||
|
||||
|
||||
|
||||
-- { init : params -> shared -> Url -> Key -> ( pagesModel, pagesEffect )
|
||||
-- , update : params -> msg -> model -> shared -> Url -> Key -> ( pagesModel, pagesEffect )
|
||||
-- , view : params -> model -> shared -> Url -> Key -> pagesView
|
||||
-- , subscriptions : params -> model -> shared -> Url -> Key -> Sub pagesMsg
|
||||
-- }
|
||||
|
||||
|
||||
bundle { redirecting, toRoute, toUrl, fromCmd, mapEffect, mapView, page, toModel, toMsg } =
|
||||
{ init =
|
||||
\params shared url key ->
|
||||
let
|
||||
req =
|
||||
ElmSpa.Request.create (toRoute url) params url key
|
||||
in
|
||||
case toResult page shared req of
|
||||
Ok record ->
|
||||
record.init ()
|
||||
|> Tuple.mapBoth (toModel req.params) mapEffect
|
||||
|
||||
Err route ->
|
||||
( redirecting.model, fromCmd <| Browser.Navigation.replaceUrl req.key (toUrl route) )
|
||||
, update =
|
||||
\params msg model shared url key ->
|
||||
let
|
||||
req =
|
||||
ElmSpa.Request.create (toRoute url) params url key
|
||||
in
|
||||
case toResult page shared req of
|
||||
Ok record ->
|
||||
record.update msg model
|
||||
|> Tuple.mapBoth (toModel req.params) mapEffect
|
||||
|
||||
Err route ->
|
||||
( redirecting.model, fromCmd <| Browser.Navigation.replaceUrl req.key (toUrl route) )
|
||||
, view =
|
||||
\params model shared url key ->
|
||||
let
|
||||
req =
|
||||
ElmSpa.Request.create (toRoute url) params url key
|
||||
in
|
||||
case toResult page shared req of
|
||||
Ok record ->
|
||||
record.view model
|
||||
|> mapView
|
||||
|
||||
Err _ ->
|
||||
redirecting.view
|
||||
, subscriptions =
|
||||
\params model shared url key ->
|
||||
let
|
||||
req =
|
||||
ElmSpa.Request.create (toRoute url) params url key
|
||||
in
|
||||
case toResult page shared req of
|
||||
Ok record ->
|
||||
record.subscriptions model
|
||||
|> Sub.map toMsg
|
||||
|
||||
Err _ ->
|
||||
Sub.none
|
||||
}
|
||||
|
||||
|
||||
toResult :
|
||||
(shared -> Request route params -> Page shared (Request route params) route effect view model msg)
|
||||
-> shared
|
||||
-> Request route params
|
||||
-> Result route (PageRecord effect view model msg)
|
||||
toResult toPage shared req =
|
||||
let
|
||||
(Page toResult_) =
|
||||
toPage shared req
|
||||
in
|
||||
toResult_ shared req
|
||||
|
||||
|
||||
|
||||
-- INTERNALS
|
||||
|
||||
|
||||
type alias Internals shared request route effect view model msg =
|
||||
shared -> request -> Result route (PageRecord effect view model msg)
|
||||
|
||||
|
||||
type alias PageRecord effect view model msg =
|
||||
{ init : () -> ( model, effect )
|
||||
, update : msg -> model -> ( model, effect )
|
||||
, view : model -> view
|
||||
, subscriptions : model -> Sub msg
|
||||
}
|
2
src/cli/package-lock.json
generated
2
src/cli/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "elm-spa",
|
||||
"version": "6.0.3--beta",
|
||||
"version": "6.0.4--beta",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "elm-spa",
|
||||
"version": "6.0.3--beta",
|
||||
"version": "6.0.4--beta",
|
||||
"description": "single page apps made easy",
|
||||
"bin": "dist/src/index.js",
|
||||
"scripts": {
|
||||
|
@ -2,6 +2,7 @@ import path from 'path'
|
||||
|
||||
const reserved = {
|
||||
homepage: 'Home_',
|
||||
redirecting: 'Redirecting_',
|
||||
notFound: 'NotFound'
|
||||
}
|
||||
|
||||
@ -35,6 +36,7 @@ const config = {
|
||||
terser: `npx terser`
|
||||
},
|
||||
defaults: [
|
||||
[ 'Effect.elm' ],
|
||||
[ 'Main.elm' ],
|
||||
[ 'Shared.elm' ],
|
||||
[ `Pages`, `${reserved.notFound}.elm` ],
|
||||
|
80
src/cli/src/defaults/Effect.elm
Normal file
80
src/cli/src/defaults/Effect.elm
Normal file
@ -0,0 +1,80 @@
|
||||
module Effect exposing
|
||||
( Effect, none, map, batch
|
||||
, fromCmd, fromShared
|
||||
, toCmd
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
@docs Effect, none, map, batch
|
||||
@docs fromCmd, fromShared
|
||||
@docs toCmd
|
||||
|
||||
-}
|
||||
|
||||
import Shared
|
||||
import Task
|
||||
|
||||
|
||||
type Effect msg
|
||||
= None
|
||||
| Cmd (Cmd msg)
|
||||
| Shared Shared.Msg
|
||||
| Batch (List (Effect msg))
|
||||
|
||||
|
||||
none : Effect msg
|
||||
none =
|
||||
None
|
||||
|
||||
|
||||
map : (a -> b) -> Effect a -> Effect b
|
||||
map fn effect =
|
||||
case effect of
|
||||
None ->
|
||||
None
|
||||
|
||||
Cmd cmd ->
|
||||
Cmd (Cmd.map fn cmd)
|
||||
|
||||
Shared msg ->
|
||||
Shared msg
|
||||
|
||||
Batch list ->
|
||||
Batch (List.map (map fn) list)
|
||||
|
||||
|
||||
fromCmd : Cmd msg -> Effect msg
|
||||
fromCmd =
|
||||
Cmd
|
||||
|
||||
|
||||
fromShared : Shared.Msg -> Effect msg
|
||||
fromShared =
|
||||
Shared
|
||||
|
||||
|
||||
batch : List (Effect msg) -> Effect msg
|
||||
batch =
|
||||
Batch
|
||||
|
||||
|
||||
|
||||
-- Used by Main.elm
|
||||
|
||||
|
||||
toCmd : ( Shared.Msg -> msg, pageMsg -> msg ) -> Effect pageMsg -> Cmd msg
|
||||
toCmd ( fromSharedMsg, fromPageMsg ) effect =
|
||||
case effect of
|
||||
None ->
|
||||
Cmd.none
|
||||
|
||||
Cmd cmd ->
|
||||
Cmd.map fromPageMsg cmd
|
||||
|
||||
Shared msg ->
|
||||
Task.succeed msg
|
||||
|> Task.perform fromSharedMsg
|
||||
|
||||
Batch list ->
|
||||
Cmd.batch (List.map (toCmd ( fromSharedMsg, fromPageMsg )) list)
|
@ -2,9 +2,10 @@ module Main exposing (main)
|
||||
|
||||
import Browser
|
||||
import Browser.Navigation as Nav exposing (Key)
|
||||
import Effect
|
||||
import Gen.Pages as Pages
|
||||
import Gen.Route as Route
|
||||
import Request exposing (Request)
|
||||
import Request
|
||||
import Shared
|
||||
import Url exposing (Url)
|
||||
import View
|
||||
@ -40,14 +41,13 @@ init flags url key =
|
||||
( shared, sharedCmd ) =
|
||||
Shared.init (Request.create () url key) flags
|
||||
|
||||
( page, pageCmd, sharedPageCmd ) =
|
||||
( page, effect ) =
|
||||
Pages.init (Route.fromUrl url) shared url key
|
||||
in
|
||||
( Model url key shared page
|
||||
, Cmd.batch
|
||||
[ Cmd.map Shared sharedCmd
|
||||
, Cmd.map Shared sharedPageCmd
|
||||
, Cmd.map Page pageCmd
|
||||
, Effect.toCmd ( Shared, Page ) effect
|
||||
]
|
||||
)
|
||||
|
||||
@ -79,14 +79,11 @@ update msg model =
|
||||
ChangedUrl url ->
|
||||
if url.path /= model.url.path then
|
||||
let
|
||||
( page, pageCmd, sharedPageCmd ) =
|
||||
( page, effect ) =
|
||||
Pages.init (Route.fromUrl url) model.shared url model.key
|
||||
in
|
||||
( { model | url = url, page = page }
|
||||
, Cmd.batch
|
||||
[ Cmd.map Page pageCmd
|
||||
, Cmd.map Shared sharedPageCmd
|
||||
]
|
||||
, Effect.toCmd ( Shared, Page ) effect
|
||||
)
|
||||
|
||||
else
|
||||
@ -103,14 +100,11 @@ update msg model =
|
||||
|
||||
Page pageMsg ->
|
||||
let
|
||||
( page, pageCmd, sharedPageCmd ) =
|
||||
( page, effect ) =
|
||||
Pages.update pageMsg model.page model.shared model.url model.key
|
||||
in
|
||||
( { model | page = page }
|
||||
, Cmd.batch
|
||||
[ Cmd.map Page pageCmd
|
||||
, Cmd.map Shared sharedPageCmd
|
||||
]
|
||||
, Effect.toCmd ( Shared, Page ) effect
|
||||
)
|
||||
|
||||
|
||||
|
@ -1,26 +1,54 @@
|
||||
module Page exposing
|
||||
( Page
|
||||
, static, sandbox, element, shared
|
||||
, static, sandbox, element, advanced
|
||||
, protected
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
@docs Page
|
||||
@docs static, sandbox, element, shared
|
||||
@docs static, sandbox, element, advanced
|
||||
|
||||
-}
|
||||
|
||||
import ElmSpa.Page
|
||||
import Effect exposing (Effect)
|
||||
import ElmSpa.Internals.Page as ElmSpa
|
||||
import Gen.Route exposing (Route)
|
||||
import Request exposing (Request)
|
||||
import Shared
|
||||
import View exposing (View)
|
||||
|
||||
|
||||
|
||||
-- PROTECTED OPTIONS
|
||||
|
||||
|
||||
{-| Replace "()" with your User type
|
||||
-}
|
||||
type alias User =
|
||||
()
|
||||
|
||||
|
||||
{-| This function attempts to get your user from shared state.
|
||||
-}
|
||||
getUser : Shared.Model -> Request () -> Maybe User
|
||||
getUser _ _ =
|
||||
Nothing
|
||||
|
||||
|
||||
{-| This is the route elm-spa redirects to when a user is not signed in on a protected page.
|
||||
-}
|
||||
unauthorizedRoute : Route
|
||||
unauthorizedRoute =
|
||||
Gen.Route.NotFound
|
||||
|
||||
|
||||
|
||||
-- PAGES
|
||||
|
||||
|
||||
type alias Page model msg =
|
||||
{ init : () -> ( model, Cmd msg, List Shared.Msg )
|
||||
, update : msg -> model -> ( model, Cmd msg, List Shared.Msg )
|
||||
, view : model -> View msg
|
||||
, subscriptions : model -> Sub msg
|
||||
}
|
||||
ElmSpa.Page Shared.Model (Request ()) Gen.Route.Route (Effect msg) (View msg) model msg
|
||||
|
||||
|
||||
static :
|
||||
@ -28,7 +56,7 @@ static :
|
||||
}
|
||||
-> Page () Never
|
||||
static =
|
||||
ElmSpa.Page.static
|
||||
ElmSpa.static Effect.none
|
||||
|
||||
|
||||
sandbox :
|
||||
@ -38,7 +66,7 @@ sandbox :
|
||||
}
|
||||
-> Page model msg
|
||||
sandbox =
|
||||
ElmSpa.Page.sandbox
|
||||
ElmSpa.sandbox Effect.none
|
||||
|
||||
|
||||
element :
|
||||
@ -49,15 +77,50 @@ element :
|
||||
}
|
||||
-> Page model msg
|
||||
element =
|
||||
ElmSpa.Page.element
|
||||
ElmSpa.element Effect.fromCmd
|
||||
|
||||
|
||||
shared :
|
||||
{ init : ( model, Cmd msg, List Shared.Msg )
|
||||
, update : msg -> model -> ( model, Cmd msg, List Shared.Msg )
|
||||
advanced :
|
||||
{ init : ( model, Effect msg )
|
||||
, update : msg -> model -> ( model, Effect msg )
|
||||
, view : model -> View msg
|
||||
, subscriptions : model -> Sub msg
|
||||
}
|
||||
-> Page model msg
|
||||
shared =
|
||||
ElmSpa.Page.shared
|
||||
advanced =
|
||||
ElmSpa.advanced
|
||||
|
||||
|
||||
protected :
|
||||
{ static :
|
||||
{ view : User -> View msg
|
||||
}
|
||||
-> Page () msg
|
||||
, sandbox :
|
||||
{ init : User -> model
|
||||
, update : User -> msg -> model -> model
|
||||
, view : User -> model -> View msg
|
||||
}
|
||||
-> Page model msg
|
||||
, element :
|
||||
{ init : User -> ( model, Cmd msg )
|
||||
, update : User -> msg -> model -> ( model, Cmd msg )
|
||||
, view : User -> model -> View msg
|
||||
, subscriptions : User -> model -> Sub msg
|
||||
}
|
||||
-> Page model msg
|
||||
, advanced :
|
||||
{ init : User -> ( model, Effect msg )
|
||||
, update : User -> msg -> model -> ( model, Effect msg )
|
||||
, view : User -> model -> View msg
|
||||
, subscriptions : User -> model -> Sub msg
|
||||
}
|
||||
-> Page model msg
|
||||
}
|
||||
protected =
|
||||
ElmSpa.protected
|
||||
{ effectNone = Effect.none
|
||||
, fromCmd = Effect.fromCmd
|
||||
, user = getUser
|
||||
, route = unauthorizedRoute
|
||||
}
|
||||
|
@ -7,10 +7,8 @@ module Shared exposing
|
||||
, update
|
||||
)
|
||||
|
||||
import Browser.Navigation exposing (Key)
|
||||
import Json.Decode as Json
|
||||
import Request exposing (Request)
|
||||
import Url exposing (Url)
|
||||
|
||||
|
||||
type alias Flags =
|
||||
@ -26,17 +24,17 @@ type Msg
|
||||
|
||||
|
||||
init : Request () -> Flags -> ( Model, Cmd Msg )
|
||||
init _ flags =
|
||||
init _ _ =
|
||||
( {}, Cmd.none )
|
||||
|
||||
|
||||
update : Request () -> Msg -> Model -> ( Model, Cmd Msg )
|
||||
update request msg model =
|
||||
update _ msg model =
|
||||
case msg of
|
||||
NoOp ->
|
||||
( model, Cmd.none )
|
||||
|
||||
|
||||
subscriptions : Request () -> Model -> Sub Msg
|
||||
subscriptions request model =
|
||||
subscriptions _ _ =
|
||||
Sub.none
|
||||
|
@ -1,4 +1,4 @@
|
||||
module View exposing (View, map, placeholder, toBrowserDocument)
|
||||
module View exposing (View, map, none, placeholder, toBrowserDocument)
|
||||
|
||||
import Browser
|
||||
import Html exposing (Html)
|
||||
@ -17,6 +17,11 @@ placeholder str =
|
||||
}
|
||||
|
||||
|
||||
none : View msg
|
||||
none =
|
||||
placeholder ""
|
||||
|
||||
|
||||
map : (a -> b) -> View a -> View b
|
||||
map fn view =
|
||||
{ title = view.title
|
||||
|
@ -1,3 +1,4 @@
|
||||
import config from "../config"
|
||||
import {
|
||||
pagesImports, paramsImports,
|
||||
pagesModelDefinition,
|
||||
@ -11,6 +12,6 @@ ${paramsImports(pages)}
|
||||
${pagesImports(pages)}
|
||||
|
||||
|
||||
${pagesModelDefinition(pages, options)}
|
||||
${pagesModelDefinition([ [ config.reserved.redirecting ] ].concat(pages), options)}
|
||||
|
||||
`.trimLeft()
|
||||
|
@ -14,13 +14,15 @@ export default (pages : string[][], options : Options) : string => `
|
||||
module Gen.Pages exposing (Model, Msg, init, subscriptions, update, view)
|
||||
|
||||
import Browser.Navigation exposing (Key)
|
||||
import Request exposing (Request)
|
||||
import Effect exposing (Effect)
|
||||
import ElmSpa.Internals.Page
|
||||
${paramsImports(pages)}
|
||||
import Gen.Model as Model
|
||||
import Gen.Msg as Msg
|
||||
import Gen.Route as Route exposing (Route)
|
||||
import Page exposing (Page)
|
||||
${pagesImports(pages)}
|
||||
import Request exposing (Request)
|
||||
import Shared
|
||||
import Task
|
||||
import Url exposing (Url)
|
||||
@ -35,12 +37,12 @@ type alias Msg =
|
||||
Msg.Msg
|
||||
|
||||
|
||||
init : Route -> Shared.Model -> Url -> Key -> ( Model, Cmd Msg, Cmd Shared.Msg )
|
||||
init : Route -> Shared.Model -> Url -> Key -> ( Model, Effect Msg )
|
||||
init route =
|
||||
${pagesInitBody(pages)}
|
||||
|
||||
|
||||
update : Msg -> Model -> Shared.Model -> Url -> Key -> ( Model, Cmd Msg, Cmd Shared.Msg )
|
||||
update : Msg -> Model -> Shared.Model -> Url -> Key -> ( Model, Effect Msg )
|
||||
update msg_ model_ =
|
||||
${pagesUpdateBody(pages.filter(page => options.isStatic(page) === false), options)}
|
||||
${pages.length > 1 ? pagesUpdateCatchAll : ''}
|
||||
@ -67,49 +69,24 @@ ${pagesBundleDefinition(pages, options)}
|
||||
|
||||
|
||||
type alias Bundle params model msg =
|
||||
{ init : params -> Shared.Model -> Url -> Key -> ( Model, Cmd Msg, Cmd Shared.Msg )
|
||||
, update : params -> msg -> model -> Shared.Model -> Url -> Key -> ( Model, Cmd Msg, Cmd Shared.Msg )
|
||||
, view : params -> model -> Shared.Model -> Url -> Key -> View Msg
|
||||
, subscriptions : params -> model -> Shared.Model -> Url -> Key -> Sub Msg
|
||||
}
|
||||
ElmSpa.Internals.Page.Bundle params model msg Shared.Model (Effect Msg) Model Msg (View Msg)
|
||||
|
||||
|
||||
bundle :
|
||||
(Shared.Model -> Request params -> Page model msg)
|
||||
-> (params -> model -> Model)
|
||||
-> (msg -> Msg)
|
||||
-> Bundle params model msg
|
||||
bundle page toModel toMsg =
|
||||
let
|
||||
mapTriple :
|
||||
params
|
||||
-> ( model, Cmd msg, List Shared.Msg )
|
||||
-> ( Model, Cmd Msg, Cmd Shared.Msg )
|
||||
mapTriple params ( model, cmd, sharedMsgList ) =
|
||||
( toModel params model
|
||||
, Cmd.map toMsg cmd
|
||||
, sharedMsgList
|
||||
|> List.map (Task.succeed >> Task.perform identity)
|
||||
|> Cmd.batch
|
||||
)
|
||||
in
|
||||
{ init =
|
||||
\\params shared url key ->
|
||||
(page shared (Request.create params url key)).init ()
|
||||
|> mapTriple params
|
||||
, update =
|
||||
\\params msg model shared url key ->
|
||||
(page shared (Request.create params url key)).update msg model
|
||||
|> mapTriple params
|
||||
, view =
|
||||
\\params model shared url key ->
|
||||
(page shared (Request.create params url key)).view model
|
||||
|> View.map toMsg
|
||||
, subscriptions =
|
||||
\\params model shared url key ->
|
||||
(page shared (Request.create params url key)).subscriptions model
|
||||
|> Sub.map toMsg
|
||||
}
|
||||
ElmSpa.Internals.Page.bundle
|
||||
{ redirecting =
|
||||
{ model = Model.Redirecting_
|
||||
, view = View.none
|
||||
}
|
||||
, toRoute = Route.fromUrl
|
||||
, toUrl = Route.toHref
|
||||
, fromCmd = Effect.fromCmd
|
||||
, mapEffect = Effect.map toMsg
|
||||
, mapView = View.map toMsg
|
||||
, toModel = toModel
|
||||
, toMsg = toMsg
|
||||
, page = page
|
||||
}
|
||||
|
||||
|
||||
type alias Static params =
|
||||
@ -118,8 +95,8 @@ type alias Static params =
|
||||
|
||||
static : View Never -> (params -> Model) -> Static params
|
||||
static view_ toModel =
|
||||
{ init = \\params _ _ _ -> ( toModel params, Cmd.none, Cmd.none )
|
||||
, update = \\params _ _ _ _ _ -> ( toModel params, Cmd.none, Cmd.none )
|
||||
{ init = \\params _ _ _ -> ( toModel params, Effect.none )
|
||||
, update = \\params _ _ _ _ _ -> ( toModel params, Effect.none )
|
||||
, view = \\_ _ _ _ _ -> View.map never view_
|
||||
, subscriptions = \\_ _ _ _ _ -> Sub.none
|
||||
}
|
||||
|
@ -166,10 +166,12 @@ const pageModuleName = (path : string[]) : string =>
|
||||
|
||||
export const pagesModelDefinition = (paths : string[][], options : Options) : string =>
|
||||
customType('Model',
|
||||
paths.map(path =>
|
||||
options.isStatic(path)
|
||||
? `${modelVariant(path)} ${params(path)}`
|
||||
: `${modelVariant(path)} ${params(path)} ${model(path)}`
|
||||
paths.map(path =>
|
||||
path[0] === config.reserved.redirecting
|
||||
? config.reserved.redirecting
|
||||
: options.isStatic(path)
|
||||
? `${modelVariant(path)} ${params(path)}`
|
||||
: `${modelVariant(path)} ${params(path)} ${model(path)}`
|
||||
)
|
||||
)
|
||||
|
||||
@ -238,10 +240,10 @@ export const pagesUpdateBody = (paths: string[][], options : Options) : string =
|
||||
export const pagesUpdateCatchAll =
|
||||
`
|
||||
_ ->
|
||||
\\_ _ _ -> ( model_, Cmd.none, Cmd.none )`
|
||||
\\_ _ _ -> ( model_, Effect.none )`
|
||||
|
||||
export const pagesViewBody = (paths: string[][], options : Options) : string =>
|
||||
indent(caseExpression(paths, {
|
||||
indent(caseExpressionWithRedirectingModel(`\\_ _ _ -> View.none`, paths, {
|
||||
variable: 'model_',
|
||||
condition: path => `${destructuredModel(path, options)}`,
|
||||
result: path => `pages.${bundleName(path)}.view ${pageModelArguments(path, options)}`
|
||||
@ -249,12 +251,23 @@ export const pagesViewBody = (paths: string[][], options : Options) : string =>
|
||||
|
||||
|
||||
export const pagesSubscriptionsBody = (paths: string[][], options : Options) : string =>
|
||||
indent(caseExpression(paths, {
|
||||
indent(caseExpressionWithRedirectingModel(`\\_ _ _ -> Sub.none`, paths, {
|
||||
variable: 'model_',
|
||||
condition: path => `${destructuredModel(path, options)}`,
|
||||
result: path => `pages.${bundleName(path)}.subscriptions ${pageModelArguments(path, options)}`
|
||||
}))
|
||||
|
||||
const caseExpressionWithRedirectingModel = (fallback : string, items: string[][], options : { variable : string, condition : (item: string[]) => string, result: (item: string[]) => string }) =>
|
||||
caseExpression([ [ config.reserved.redirecting ] ].concat(items), {
|
||||
variable: options.variable,
|
||||
condition: (item) => item[0] === config.reserved.redirecting
|
||||
? `Model.${config.reserved.redirecting}`
|
||||
: options.condition(item),
|
||||
result: (item) => item[0] === config.reserved.redirecting
|
||||
? fallback
|
||||
: options.result(item)
|
||||
})
|
||||
|
||||
const caseExpression = <T>(items: T[], options : { variable : string, condition : (item: T) => string, result: (item: T) => string }) =>
|
||||
`case ${options.variable} of
|
||||
${items.map(item => ` ${options.condition(item)} ->\n ${options.result(item)}`).join('\n\n')}`
|
||||
|
Loading…
Reference in New Issue
Block a user