mirror of
https://github.com/ryan-haskell/elm-spa.git
synced 2024-10-26 13:49:55 +03:00
begin work on layouts
This commit is contained in:
parent
afb3df6792
commit
6b280b4a6f
5
examples/08-layouts/.gitignore
vendored
Normal file
5
examples/08-layouts/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
.DS_Store
|
||||
.elm-spa
|
||||
elm-stuff
|
||||
node_modules
|
||||
dist
|
28
examples/08-layouts/README.md
Normal file
28
examples/08-layouts/README.md
Normal file
@ -0,0 +1,28 @@
|
||||
# my new project
|
||||
> 🌳 built with [elm-spa](https://elm-spa.dev)
|
||||
|
||||
## dependencies
|
||||
|
||||
This project requires the latest LTS version of [Node.js](https://nodejs.org/)
|
||||
|
||||
```bash
|
||||
npm install -g elm elm-spa
|
||||
```
|
||||
|
||||
## running locally
|
||||
|
||||
```bash
|
||||
elm-spa server # starts this app at http:/localhost:1234
|
||||
```
|
||||
|
||||
### other commands
|
||||
|
||||
```bash
|
||||
elm-spa add # add a new page to the application
|
||||
elm-spa build # production build
|
||||
elm-spa watch # runs build as you code (without the server)
|
||||
```
|
||||
|
||||
## learn more
|
||||
|
||||
You can learn more at [elm-spa.dev](https://elm-spa.dev)
|
28
examples/08-layouts/elm.json
Normal file
28
examples/08-layouts/elm.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"type": "application",
|
||||
"source-directories": [
|
||||
"src",
|
||||
".elm-spa/defaults",
|
||||
".elm-spa/generated",
|
||||
"../../src"
|
||||
],
|
||||
"elm-version": "0.19.1",
|
||||
"dependencies": {
|
||||
"direct": {
|
||||
"elm/browser": "1.0.2",
|
||||
"elm/core": "1.0.5",
|
||||
"elm/html": "1.0.0",
|
||||
"elm/json": "1.1.3",
|
||||
"elm/url": "1.0.0",
|
||||
"elm-community/list-extra": "8.3.1"
|
||||
},
|
||||
"indirect": {
|
||||
"elm/time": "1.0.0",
|
||||
"elm/virtual-dom": "1.0.2"
|
||||
}
|
||||
},
|
||||
"test-dependencies": {
|
||||
"direct": {},
|
||||
"indirect": {}
|
||||
}
|
||||
}
|
20
examples/08-layouts/public/index.html
Normal file
20
examples/08-layouts/public/index.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="https://nope.rhg.dev/dist/3.0.0/core.min.css">
|
||||
<style>
|
||||
.h1 { font-weight: bold; font-size: 2rem }
|
||||
.h3 { font-weight: bold; font-size: 1.25rem }
|
||||
.h4 { font-weight: bold; }
|
||||
a { text-decoration: underline;}
|
||||
a, button { cursor: pointer;}
|
||||
.border-right { border-right: solid 1px #ddd;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script src="/dist/elm.js"></script>
|
||||
<script> Elm.Main.init() </script>
|
||||
</body>
|
||||
</html>
|
118
examples/08-layouts/src/Gen/Layout.elm
Normal file
118
examples/08-layouts/src/Gen/Layout.elm
Normal file
@ -0,0 +1,118 @@
|
||||
module Gen.Layout exposing
|
||||
( Layout, static, sandbox
|
||||
, Bundle, toBundle
|
||||
--, element, advanced
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
@docs Layout, static, sandbox, element, advanced
|
||||
@docs Bundle, toBundle
|
||||
|
||||
-}
|
||||
|
||||
import Effect exposing (Effect)
|
||||
import Request exposing (Request)
|
||||
import Shared
|
||||
import View exposing (View)
|
||||
|
||||
|
||||
|
||||
-- LAYOUT
|
||||
|
||||
|
||||
type Layout model msg mainMsg
|
||||
= Layout (Internals model msg mainMsg)
|
||||
|
||||
|
||||
static : { view : { viewPage : View mainMsg } -> View mainMsg } -> Layout () msg mainMsg
|
||||
static layout =
|
||||
Layout
|
||||
{ record =
|
||||
{ init = ( (), Effect.none )
|
||||
, update = \_ model -> ( model, Effect.none )
|
||||
, view = \options _ -> layout.view { viewPage = options.viewPage }
|
||||
, subscriptions = \_ -> Sub.none
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sandbox :
|
||||
{ init : model
|
||||
, update : msg -> model -> model
|
||||
, view : { viewPage : View mainMsg, toMainMsg : msg -> mainMsg } -> model -> View mainMsg
|
||||
}
|
||||
-> Layout model msg mainMsg
|
||||
sandbox layout =
|
||||
Layout
|
||||
{ record =
|
||||
{ init = ( layout.init, Effect.none )
|
||||
, update = \msg model -> ( layout.update msg model, Effect.none )
|
||||
, view = layout.view
|
||||
, subscriptions = \_ -> Sub.none
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- BUNDLE
|
||||
|
||||
|
||||
type alias Internals model msg mainMsg =
|
||||
{ record : LayoutRecord model msg mainMsg
|
||||
}
|
||||
|
||||
|
||||
type alias LayoutRecord model msg mainMsg =
|
||||
{ init : ( model, Effect msg )
|
||||
, update : msg -> model -> ( model, Effect msg )
|
||||
, subscriptions : model -> Sub msg
|
||||
, view :
|
||||
{ viewPage : View mainMsg
|
||||
, toMainMsg : msg -> mainMsg
|
||||
}
|
||||
-> model
|
||||
-> View mainMsg
|
||||
}
|
||||
|
||||
|
||||
type alias Bundle model msg genModel genMsg mainMsg =
|
||||
{ init : Shared.Model -> Request -> ( genModel, Effect genMsg )
|
||||
, update : msg -> model -> Shared.Model -> Request -> ( genModel, Effect genMsg )
|
||||
, subscriptions : model -> Shared.Model -> Request -> Sub genMsg
|
||||
, view : model -> { viewPage : View mainMsg, toMainMsg : genMsg -> mainMsg } -> Shared.Model -> Request -> View mainMsg
|
||||
}
|
||||
|
||||
|
||||
toBundle :
|
||||
(model -> genModel)
|
||||
-> (msg -> genMsg)
|
||||
-> (Shared.Model -> Request -> Layout model msg mainMsg)
|
||||
-> Bundle model msg genModel genMsg mainMsg
|
||||
toBundle toModel toMsg toLayout =
|
||||
let
|
||||
toRecord shared req =
|
||||
case toLayout shared req of
|
||||
Layout { record } ->
|
||||
record
|
||||
in
|
||||
{ init =
|
||||
\shared req ->
|
||||
(toRecord shared req).init
|
||||
|> Tuple.mapBoth toModel (Effect.map toMsg)
|
||||
, update =
|
||||
\msg model shared req ->
|
||||
(toRecord shared req).update msg model
|
||||
|> Tuple.mapBoth toModel (Effect.map toMsg)
|
||||
, subscriptions =
|
||||
\model shared req ->
|
||||
(toRecord shared req).subscriptions model
|
||||
|> Sub.map toMsg
|
||||
, view =
|
||||
\model options shared req ->
|
||||
(toRecord shared req).view
|
||||
{ viewPage = options.viewPage
|
||||
, toMainMsg = toMsg >> options.toMainMsg
|
||||
}
|
||||
model
|
||||
}
|
107
examples/08-layouts/src/Gen/Layouts.elm
Normal file
107
examples/08-layouts/src/Gen/Layouts.elm
Normal file
@ -0,0 +1,107 @@
|
||||
module Gen.Layouts exposing
|
||||
( Layout(..)
|
||||
, Model, init
|
||||
, Msg, update
|
||||
, view
|
||||
, subscriptions
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
@docs Layout
|
||||
@docs Model, init
|
||||
@docs Msg, update
|
||||
@docs view
|
||||
@docs subscriptions
|
||||
|
||||
-}
|
||||
|
||||
import Effect exposing (Effect)
|
||||
import Gen.Layout
|
||||
import Layouts.Sidebar
|
||||
import Request exposing (Request)
|
||||
import Shared
|
||||
import View exposing (View)
|
||||
|
||||
|
||||
type Layout
|
||||
= Sidebar
|
||||
|
||||
|
||||
|
||||
-- BUNDLE
|
||||
|
||||
|
||||
type alias Bundle model msg mainMsg =
|
||||
{ init : Shared.Model -> Request -> ( Model, Effect Msg )
|
||||
, update : msg -> model -> Shared.Model -> Request -> ( Model, Effect Msg )
|
||||
, subscriptions : model -> Shared.Model -> Request -> Sub Msg
|
||||
, view : model -> { viewPage : View mainMsg, toMainMsg : Msg -> mainMsg } -> Shared.Model -> Request -> View mainMsg
|
||||
}
|
||||
|
||||
|
||||
layouts :
|
||||
{ sidebar : Bundle Layouts.Sidebar.Model Layouts.Sidebar.Msg mainMsg
|
||||
}
|
||||
layouts =
|
||||
{ sidebar = Gen.Layout.toBundle Sidebar_Model Sidebar_Msg Layouts.Sidebar.layout
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- INIT
|
||||
|
||||
|
||||
type Model
|
||||
= Sidebar_Model Layouts.Sidebar.Model
|
||||
|
||||
|
||||
init : Layout -> Shared.Model -> Request -> ( Model, Effect Msg )
|
||||
init layout =
|
||||
case layout of
|
||||
Sidebar ->
|
||||
layouts.sidebar.init
|
||||
|
||||
|
||||
|
||||
-- UPDATE
|
||||
|
||||
|
||||
type Msg
|
||||
= Sidebar_Msg Layouts.Sidebar.Msg
|
||||
|
||||
|
||||
update : Msg -> Model -> Shared.Model -> Request -> ( Model, Effect Msg )
|
||||
update msg_ model_ =
|
||||
case ( msg_, model_ ) of
|
||||
( Sidebar_Msg msg, Sidebar_Model model ) ->
|
||||
layouts.sidebar.update msg model
|
||||
|
||||
|
||||
|
||||
-- _ ->
|
||||
-- \_ _ -> ( model_, Effect.none )
|
||||
-- SUBSCRIPTIONS
|
||||
|
||||
|
||||
subscriptions : Model -> Shared.Model -> Request -> Sub Msg
|
||||
subscriptions model_ =
|
||||
case model_ of
|
||||
Sidebar_Model model ->
|
||||
layouts.sidebar.subscriptions model
|
||||
|
||||
|
||||
|
||||
-- VIEW
|
||||
|
||||
|
||||
view :
|
||||
Model
|
||||
-> { viewPage : View mainMsg, toMainMsg : Msg -> mainMsg }
|
||||
-> Shared.Model
|
||||
-> Request
|
||||
-> View mainMsg
|
||||
view model_ =
|
||||
case model_ of
|
||||
Sidebar_Model model ->
|
||||
layouts.sidebar.view model
|
228
examples/08-layouts/src/Gen/Pages_.elm
Normal file
228
examples/08-layouts/src/Gen/Pages_.elm
Normal file
@ -0,0 +1,228 @@
|
||||
module Gen.Pages_ exposing (Model, Msg, init, layout, subscriptions, update, view)
|
||||
|
||||
import Browser.Navigation exposing (Key)
|
||||
import Effect exposing (Effect)
|
||||
import ElmSpa.Page
|
||||
import Gen.Layouts
|
||||
import Gen.Model as Model
|
||||
import Gen.Msg as Msg
|
||||
import Gen.Params.Apps
|
||||
import Gen.Params.Devices
|
||||
import Gen.Params.Home_
|
||||
import Gen.Params.NotFound
|
||||
import Gen.Params.People
|
||||
import Gen.Params.Settings.General
|
||||
import Gen.Params.Settings.Profile
|
||||
import Gen.Params.SignIn
|
||||
import Gen.Route as Route exposing (Route)
|
||||
import Page exposing (Page)
|
||||
import Pages.Apps
|
||||
import Pages.Devices
|
||||
import Pages.Home_
|
||||
import Pages.NotFound
|
||||
import Pages.People
|
||||
import Pages.Settings.General
|
||||
import Pages.Settings.Profile
|
||||
import Pages.SignIn
|
||||
import Request exposing (Request)
|
||||
import Shared
|
||||
import Task
|
||||
import Url exposing (Url)
|
||||
import View exposing (View)
|
||||
|
||||
|
||||
type alias Model =
|
||||
Model.Model
|
||||
|
||||
|
||||
type alias Msg =
|
||||
Msg.Msg
|
||||
|
||||
|
||||
init : Route -> Shared.Model -> Url -> Key -> ( Model, Effect Msg )
|
||||
init route =
|
||||
case route of
|
||||
Route.Apps ->
|
||||
pages.apps.init ()
|
||||
|
||||
Route.Devices ->
|
||||
pages.devices.init ()
|
||||
|
||||
Route.Home_ ->
|
||||
pages.home_.init ()
|
||||
|
||||
Route.People ->
|
||||
pages.people.init ()
|
||||
|
||||
Route.SignIn ->
|
||||
pages.signIn.init ()
|
||||
|
||||
Route.Settings__General ->
|
||||
pages.settings__general.init ()
|
||||
|
||||
Route.Settings__Profile ->
|
||||
pages.settings__profile.init ()
|
||||
|
||||
Route.NotFound ->
|
||||
pages.notFound.init ()
|
||||
|
||||
|
||||
update : Msg -> Model -> Shared.Model -> Url -> Key -> ( Model, Effect Msg )
|
||||
update msg_ model_ =
|
||||
case ( msg_, model_ ) of
|
||||
_ ->
|
||||
\_ _ _ -> ( model_, Effect.none )
|
||||
|
||||
|
||||
view : Model -> Shared.Model -> Url -> Key -> View Msg
|
||||
view model_ =
|
||||
case model_ of
|
||||
Model.Redirecting_ ->
|
||||
\_ _ _ -> View.none
|
||||
|
||||
Model.Apps params ->
|
||||
pages.apps.view params ()
|
||||
|
||||
Model.Devices params ->
|
||||
pages.devices.view params ()
|
||||
|
||||
Model.Home_ params ->
|
||||
pages.home_.view params ()
|
||||
|
||||
Model.People params ->
|
||||
pages.people.view params ()
|
||||
|
||||
Model.SignIn params ->
|
||||
pages.signIn.view params ()
|
||||
|
||||
Model.Settings__General params ->
|
||||
pages.settings__general.view params ()
|
||||
|
||||
Model.Settings__Profile params ->
|
||||
pages.settings__profile.view params ()
|
||||
|
||||
Model.NotFound params ->
|
||||
pages.notFound.view params ()
|
||||
|
||||
|
||||
subscriptions : Model -> Shared.Model -> Url -> Key -> Sub Msg
|
||||
subscriptions model_ =
|
||||
case model_ of
|
||||
Model.Redirecting_ ->
|
||||
\_ _ _ -> Sub.none
|
||||
|
||||
Model.Apps params ->
|
||||
pages.apps.subscriptions params ()
|
||||
|
||||
Model.Devices params ->
|
||||
pages.devices.subscriptions params ()
|
||||
|
||||
Model.Home_ params ->
|
||||
pages.home_.subscriptions params ()
|
||||
|
||||
Model.People params ->
|
||||
pages.people.subscriptions params ()
|
||||
|
||||
Model.SignIn params ->
|
||||
pages.signIn.subscriptions params ()
|
||||
|
||||
Model.Settings__General params ->
|
||||
pages.settings__general.subscriptions params ()
|
||||
|
||||
Model.Settings__Profile params ->
|
||||
pages.settings__profile.subscriptions params ()
|
||||
|
||||
Model.NotFound params ->
|
||||
pages.notFound.subscriptions params ()
|
||||
|
||||
|
||||
|
||||
-- INTERNALS
|
||||
|
||||
|
||||
pages :
|
||||
{ apps : Static Gen.Params.Apps.Params
|
||||
, devices : Static Gen.Params.Devices.Params
|
||||
, home_ : Static Gen.Params.Home_.Params
|
||||
, people : Static Gen.Params.People.Params
|
||||
, signIn : Static Gen.Params.SignIn.Params
|
||||
, settings__general : Static Gen.Params.Settings.General.Params
|
||||
, settings__profile : Static Gen.Params.Settings.Profile.Params
|
||||
, notFound : Static Gen.Params.NotFound.Params
|
||||
}
|
||||
pages =
|
||||
{ apps = static Pages.Apps.view Model.Apps
|
||||
, devices = static Pages.Devices.view Model.Devices
|
||||
, home_ = static Pages.Home_.view Model.Home_
|
||||
, people = static Pages.People.view Model.People
|
||||
, signIn = static Pages.SignIn.view Model.SignIn
|
||||
, settings__general = static Pages.Settings.General.view Model.Settings__General
|
||||
, settings__profile = static Pages.Settings.Profile.view Model.Settings__Profile
|
||||
, notFound = static Pages.NotFound.view Model.NotFound
|
||||
}
|
||||
|
||||
|
||||
type alias Bundle params model msg =
|
||||
ElmSpa.Page.Bundle params model msg Shared.Model (Effect Msg) Model Msg (View Msg)
|
||||
|
||||
|
||||
bundle page toModel toMsg =
|
||||
ElmSpa.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 =
|
||||
Bundle params () Never
|
||||
|
||||
|
||||
static : View Never -> (params -> Model) -> Static params
|
||||
static view_ toModel =
|
||||
{ init = \params _ _ _ -> ( toModel params, Effect.none )
|
||||
, update = \params _ _ _ _ _ -> ( toModel params, Effect.none )
|
||||
, view = \_ _ _ _ _ -> View.map never view_
|
||||
, subscriptions = \_ _ _ _ _ -> Sub.none
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- LAYOUTS
|
||||
|
||||
|
||||
layout : Route -> Maybe Gen.Layouts.Layout
|
||||
layout route =
|
||||
case route of
|
||||
Route.Home_ ->
|
||||
Just Pages.Home_.layout
|
||||
|
||||
Route.Apps ->
|
||||
Just Pages.Apps.layout
|
||||
|
||||
Route.Devices ->
|
||||
Just Pages.Devices.layout
|
||||
|
||||
Route.People ->
|
||||
Just Pages.People.layout
|
||||
|
||||
Route.Settings__General ->
|
||||
Just Pages.Settings.General.layout
|
||||
|
||||
Route.Settings__Profile ->
|
||||
Just Pages.Settings.Profile.layout
|
||||
|
||||
Route.SignIn ->
|
||||
Nothing
|
||||
|
||||
Route.NotFound ->
|
||||
Nothing
|
156
examples/08-layouts/src/Layouts/Sidebar.elm
Normal file
156
examples/08-layouts/src/Layouts/Sidebar.elm
Normal file
@ -0,0 +1,156 @@
|
||||
module Layouts.Sidebar exposing (Model, Msg, layout)
|
||||
|
||||
import Gen.Layout exposing (Layout)
|
||||
import Gen.Route as Route exposing (Route)
|
||||
import Html exposing (Html)
|
||||
import Html.Attributes as Attr
|
||||
import Html.Events as Events
|
||||
import List.Extra
|
||||
import Request exposing (Request)
|
||||
import Shared
|
||||
import View exposing (View)
|
||||
|
||||
|
||||
layout : Shared.Model -> Request -> Layout Model Msg mainMsg
|
||||
layout shared req =
|
||||
Gen.Layout.sandbox
|
||||
{ init = init
|
||||
, update = update
|
||||
, view = view
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- INIT
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ expandedSections : List Section
|
||||
}
|
||||
|
||||
|
||||
type Section
|
||||
= SystemOfRecord
|
||||
| Settings
|
||||
|
||||
|
||||
init : Model
|
||||
init =
|
||||
{ expandedSections = []
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- UPDATE
|
||||
|
||||
|
||||
type Msg
|
||||
= Toggle Section
|
||||
|
||||
|
||||
update : Msg -> Model -> Model
|
||||
update msg model =
|
||||
case msg of
|
||||
Toggle section ->
|
||||
let
|
||||
expandedSections =
|
||||
if List.member section model.expandedSections then
|
||||
List.Extra.remove section model.expandedSections
|
||||
|
||||
else
|
||||
section :: model.expandedSections
|
||||
in
|
||||
{ model | expandedSections = expandedSections }
|
||||
|
||||
|
||||
|
||||
-- VIEW
|
||||
|
||||
|
||||
view :
|
||||
{ viewPage : View mainMsg
|
||||
, toMainMsg : Msg -> mainMsg
|
||||
}
|
||||
-> Model
|
||||
-> View mainMsg
|
||||
view { viewPage, toMainMsg } model =
|
||||
{ title = viewPage.title
|
||||
, body =
|
||||
[ Html.div [ Attr.class "row align-top pad-xl gap-lg fill-y" ]
|
||||
[ Html.map toMainMsg (viewSidebar model)
|
||||
, Html.div [ Attr.class "page" ] viewPage.body
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
viewSidebar : Model -> Html Msg
|
||||
viewSidebar model =
|
||||
Html.aside [ Attr.class "col gap-xl fill-y border-right pad-right-lg" ]
|
||||
[ Html.div [ Attr.class "col gap-md" ]
|
||||
[ Html.a [ Attr.class "h3", Attr.href (Route.toHref Route.Home_) ] [ Html.text "Super App" ]
|
||||
, Html.div [ Attr.class "col gap-md" ]
|
||||
(List.map (viewSidebarSection model.expandedSections) [ SystemOfRecord, Settings ])
|
||||
]
|
||||
, viewSectionLink { label = "Sign out", route = Route.SignIn }
|
||||
]
|
||||
|
||||
|
||||
viewSidebarSection : List Section -> Section -> Html Msg
|
||||
viewSidebarSection expandedSections section =
|
||||
let
|
||||
isExpanded =
|
||||
List.member section expandedSections
|
||||
|
||||
viewExpandedItems =
|
||||
if isExpanded then
|
||||
Html.div [ Attr.class "col gap-sm pad-left-lg" ] (List.map viewSectionLink (sectionLinks section))
|
||||
|
||||
else
|
||||
Html.text ""
|
||||
|
||||
icon =
|
||||
if isExpanded then
|
||||
"☝️ "
|
||||
|
||||
else
|
||||
"👇 "
|
||||
in
|
||||
Html.section [ Attr.class "col gap-sm" ]
|
||||
[ Html.button [ Attr.class "h4", Events.onClick (Toggle section) ] [ Html.text (icon ++ sectionName section) ]
|
||||
, viewExpandedItems
|
||||
]
|
||||
|
||||
|
||||
viewSectionLink : { label : String, route : Route } -> Html msg
|
||||
viewSectionLink { label, route } =
|
||||
Html.a [ Attr.class "row h5", Attr.href (Route.toHref route) ] [ Html.text label ]
|
||||
|
||||
|
||||
|
||||
-- SECTION
|
||||
|
||||
|
||||
sectionName : Section -> String
|
||||
sectionName section =
|
||||
case section of
|
||||
SystemOfRecord ->
|
||||
"System of record"
|
||||
|
||||
Settings ->
|
||||
"Settings"
|
||||
|
||||
|
||||
sectionLinks : Section -> List { label : String, route : Route }
|
||||
sectionLinks section =
|
||||
case section of
|
||||
SystemOfRecord ->
|
||||
[ { label = "Apps", route = Route.Apps }
|
||||
, { label = "Devices", route = Route.Devices }
|
||||
, { label = "People", route = Route.People }
|
||||
]
|
||||
|
||||
Settings ->
|
||||
[ { label = "General", route = Route.Settings__General }
|
||||
, { label = "Profile", route = Route.Settings__Profile }
|
||||
]
|
244
examples/08-layouts/src/Main.elm
Normal file
244
examples/08-layouts/src/Main.elm
Normal file
@ -0,0 +1,244 @@
|
||||
module Main exposing (main)
|
||||
|
||||
import Browser
|
||||
import Browser.Navigation as Nav exposing (Key)
|
||||
import Effect exposing (Effect)
|
||||
import Gen.Layouts exposing (Layout)
|
||||
import Gen.Model
|
||||
import Gen.Pages_ as Pages
|
||||
import Gen.Route as Route
|
||||
import Request exposing (Request)
|
||||
import Shared
|
||||
import Url exposing (Url)
|
||||
import View
|
||||
|
||||
|
||||
main : Program Shared.Flags Model Msg
|
||||
main =
|
||||
Browser.application
|
||||
{ init = init
|
||||
, update = update
|
||||
, view = view
|
||||
, subscriptions = subscriptions
|
||||
, onUrlChange = ChangedUrl
|
||||
, onUrlRequest = ClickedLink
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- INIT
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ url : Url
|
||||
, key : Key
|
||||
, shared : Shared.Model
|
||||
, layout : Maybe { kind : Layout, model : Gen.Layouts.Model }
|
||||
, page : Pages.Model
|
||||
}
|
||||
|
||||
|
||||
init : Shared.Flags -> Url -> Key -> ( Model, Cmd Msg )
|
||||
init flags url key =
|
||||
let
|
||||
req =
|
||||
Request.create () url key
|
||||
|
||||
( shared, sharedCmd ) =
|
||||
Shared.init req flags
|
||||
|
||||
( page, pageEffect ) =
|
||||
Pages.init (Route.fromUrl url) shared url key
|
||||
|
||||
maybeLayout =
|
||||
Pages.layout (Route.fromUrl url)
|
||||
|> Maybe.map (initializeLayout { shared = shared, request = req })
|
||||
in
|
||||
( Model url key shared (Maybe.map toKindAndModel maybeLayout) page
|
||||
, Cmd.batch
|
||||
[ Cmd.map Shared sharedCmd
|
||||
, Effect.toCmd ( Shared, Page ) pageEffect
|
||||
, Effect.toCmd ( Shared, Layout ) (toLayoutEffect maybeLayout)
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
initializeLayout : { shared : Shared.Model, request : Request } -> Layout -> { kind : Layout, model : Gen.Layouts.Model, effect : Effect Gen.Layouts.Msg }
|
||||
initializeLayout { shared, request } layoutKind =
|
||||
let
|
||||
( model, effect ) =
|
||||
Gen.Layouts.init layoutKind shared request
|
||||
in
|
||||
{ kind = layoutKind
|
||||
, model = model
|
||||
, effect = effect
|
||||
}
|
||||
|
||||
|
||||
toKindAndModel : { a | kind : b, model : c } -> { kind : b, model : c }
|
||||
toKindAndModel x =
|
||||
{ kind = x.kind, model = x.model }
|
||||
|
||||
|
||||
toLayoutEffect : Maybe { a | effect : Effect msg } -> Effect msg
|
||||
toLayoutEffect maybeLayout =
|
||||
maybeLayout
|
||||
|> Maybe.map .effect
|
||||
|> Maybe.withDefault Effect.none
|
||||
|
||||
|
||||
|
||||
-- UPDATE
|
||||
|
||||
|
||||
type Msg
|
||||
= ChangedUrl Url
|
||||
| ClickedLink Browser.UrlRequest
|
||||
| Shared Shared.Msg
|
||||
| Layout Gen.Layouts.Msg
|
||||
| Page Pages.Msg
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update msg model =
|
||||
case msg of
|
||||
ClickedLink (Browser.Internal url) ->
|
||||
( model
|
||||
, Nav.pushUrl model.key (Url.toString url)
|
||||
)
|
||||
|
||||
ClickedLink (Browser.External url) ->
|
||||
( model
|
||||
, Nav.load url
|
||||
)
|
||||
|
||||
ChangedUrl url ->
|
||||
if url.path /= model.url.path then
|
||||
let
|
||||
route =
|
||||
Route.fromUrl url
|
||||
|
||||
( page, effect ) =
|
||||
Pages.init route model.shared url model.key
|
||||
|
||||
currentLayout =
|
||||
model.layout
|
||||
|
||||
newLayoutKind =
|
||||
Pages.layout route
|
||||
in
|
||||
if Maybe.map .kind currentLayout == newLayoutKind then
|
||||
( { model | url = url, page = page }
|
||||
, Effect.toCmd ( Shared, Page ) effect
|
||||
)
|
||||
|
||||
else
|
||||
let
|
||||
maybeLayout =
|
||||
newLayoutKind
|
||||
|> Maybe.map
|
||||
(initializeLayout
|
||||
{ shared = model.shared
|
||||
, request = Request.create () url model.key
|
||||
}
|
||||
)
|
||||
in
|
||||
( { model
|
||||
| url = url
|
||||
, page = page
|
||||
, layout = Maybe.map toKindAndModel maybeLayout
|
||||
}
|
||||
, Cmd.batch
|
||||
[ Effect.toCmd ( Shared, Page ) effect
|
||||
, Effect.toCmd ( Shared, Layout ) (toLayoutEffect maybeLayout)
|
||||
]
|
||||
)
|
||||
|
||||
else
|
||||
( { model | url = url }, Cmd.none )
|
||||
|
||||
Shared sharedMsg ->
|
||||
let
|
||||
( shared, sharedCmd ) =
|
||||
Shared.update (Request.create () model.url model.key) sharedMsg model.shared
|
||||
|
||||
( page, effect ) =
|
||||
Pages.init (Route.fromUrl model.url) shared model.url model.key
|
||||
in
|
||||
if page == Gen.Model.Redirecting_ then
|
||||
( { model | shared = shared, page = page }
|
||||
, Cmd.batch
|
||||
[ Cmd.map Shared sharedCmd
|
||||
, Effect.toCmd ( Shared, Page ) effect
|
||||
]
|
||||
)
|
||||
|
||||
else
|
||||
( { model | shared = shared }
|
||||
, Cmd.map Shared sharedCmd
|
||||
)
|
||||
|
||||
Page pageMsg ->
|
||||
let
|
||||
( page, effect ) =
|
||||
Pages.update pageMsg model.page model.shared model.url model.key
|
||||
in
|
||||
( { model | page = page }
|
||||
, Effect.toCmd ( Shared, Page ) effect
|
||||
)
|
||||
|
||||
Layout layoutMsg ->
|
||||
case model.layout of
|
||||
Just layout ->
|
||||
let
|
||||
req =
|
||||
Request.create () model.url model.key
|
||||
|
||||
( newLayoutModel, effect ) =
|
||||
Gen.Layouts.update layoutMsg layout.model model.shared req
|
||||
in
|
||||
( { model | layout = Just { layout | model = newLayoutModel } }
|
||||
, Effect.toCmd ( Shared, Layout ) effect
|
||||
)
|
||||
|
||||
Nothing ->
|
||||
( model, Cmd.none )
|
||||
|
||||
|
||||
|
||||
-- VIEW
|
||||
|
||||
|
||||
view : Model -> Browser.Document Msg
|
||||
view model =
|
||||
let
|
||||
viewPage =
|
||||
Pages.view model.page model.shared model.url model.key
|
||||
|> View.map Page
|
||||
|
||||
viewLayout =
|
||||
case model.layout of
|
||||
Just layout ->
|
||||
Gen.Layouts.view layout.model
|
||||
{ viewPage = viewPage
|
||||
, toMainMsg = Layout
|
||||
}
|
||||
model.shared
|
||||
(Request.create () model.url model.key)
|
||||
|
||||
Nothing ->
|
||||
viewPage
|
||||
in
|
||||
View.toBrowserDocument viewLayout
|
||||
|
||||
|
||||
|
||||
-- SUBSCRIPTIONS
|
||||
|
||||
|
||||
subscriptions : Model -> Sub Msg
|
||||
subscriptions model =
|
||||
Sub.batch
|
||||
[ Pages.subscriptions model.page model.shared model.url model.key |> Sub.map Page
|
||||
, Shared.subscriptions (Request.create () model.url model.key) model.shared |> Sub.map Shared
|
||||
]
|
14
examples/08-layouts/src/Pages/Apps.elm
Normal file
14
examples/08-layouts/src/Pages/Apps.elm
Normal file
@ -0,0 +1,14 @@
|
||||
module Pages.Apps exposing (layout, view)
|
||||
|
||||
import Gen.Layouts
|
||||
import View exposing (View)
|
||||
|
||||
|
||||
layout : Gen.Layouts.Layout
|
||||
layout =
|
||||
Gen.Layouts.Sidebar
|
||||
|
||||
|
||||
view : View msg
|
||||
view =
|
||||
View.placeholder "Apps"
|
14
examples/08-layouts/src/Pages/Devices.elm
Normal file
14
examples/08-layouts/src/Pages/Devices.elm
Normal file
@ -0,0 +1,14 @@
|
||||
module Pages.Devices exposing (layout, view)
|
||||
|
||||
import Gen.Layouts
|
||||
import View exposing (View)
|
||||
|
||||
|
||||
layout : Gen.Layouts.Layout
|
||||
layout =
|
||||
Gen.Layouts.Sidebar
|
||||
|
||||
|
||||
view : View msg
|
||||
view =
|
||||
View.placeholder "Devices"
|
14
examples/08-layouts/src/Pages/Home_.elm
Normal file
14
examples/08-layouts/src/Pages/Home_.elm
Normal file
@ -0,0 +1,14 @@
|
||||
module Pages.Home_ exposing (layout, view)
|
||||
|
||||
import Gen.Layouts
|
||||
import View exposing (View)
|
||||
|
||||
|
||||
layout : Gen.Layouts.Layout
|
||||
layout =
|
||||
Gen.Layouts.Sidebar
|
||||
|
||||
|
||||
view : View msg
|
||||
view =
|
||||
View.placeholder "Dashboard"
|
14
examples/08-layouts/src/Pages/People.elm
Normal file
14
examples/08-layouts/src/Pages/People.elm
Normal file
@ -0,0 +1,14 @@
|
||||
module Pages.People exposing (layout, view)
|
||||
|
||||
import Gen.Layouts
|
||||
import View exposing (View)
|
||||
|
||||
|
||||
layout : Gen.Layouts.Layout
|
||||
layout =
|
||||
Gen.Layouts.Sidebar
|
||||
|
||||
|
||||
view : View msg
|
||||
view =
|
||||
View.placeholder "People"
|
14
examples/08-layouts/src/Pages/Settings/General.elm
Normal file
14
examples/08-layouts/src/Pages/Settings/General.elm
Normal file
@ -0,0 +1,14 @@
|
||||
module Pages.Settings.General exposing (layout, view)
|
||||
|
||||
import Gen.Layouts
|
||||
import View exposing (View)
|
||||
|
||||
|
||||
layout : Gen.Layouts.Layout
|
||||
layout =
|
||||
Gen.Layouts.Sidebar
|
||||
|
||||
|
||||
view : View msg
|
||||
view =
|
||||
View.placeholder "Settings.General"
|
14
examples/08-layouts/src/Pages/Settings/Profile.elm
Normal file
14
examples/08-layouts/src/Pages/Settings/Profile.elm
Normal file
@ -0,0 +1,14 @@
|
||||
module Pages.Settings.Profile exposing (layout, view)
|
||||
|
||||
import Gen.Layouts
|
||||
import View exposing (View)
|
||||
|
||||
|
||||
layout : Gen.Layouts.Layout
|
||||
layout =
|
||||
Gen.Layouts.Sidebar
|
||||
|
||||
|
||||
view : View msg
|
||||
view =
|
||||
View.placeholder "Settings.Profile"
|
18
examples/08-layouts/src/Pages/SignIn.elm
Normal file
18
examples/08-layouts/src/Pages/SignIn.elm
Normal file
@ -0,0 +1,18 @@
|
||||
module Pages.SignIn exposing (view)
|
||||
|
||||
import Gen.Route as Route
|
||||
import Html
|
||||
import Html.Attributes as Attr
|
||||
import View exposing (View)
|
||||
|
||||
|
||||
view : View msg
|
||||
view =
|
||||
{ title = "Sign in"
|
||||
, body =
|
||||
[ Html.div [ Attr.class "col center fill-y gap-lg" ]
|
||||
[ Html.h1 [ Attr.class "h1" ] [ Html.text "Welcome back!" ]
|
||||
, Html.a [ Attr.href (Route.toHref Route.Home_) ] [ Html.text "Sign in" ]
|
||||
]
|
||||
]
|
||||
}
|
24
examples/08-layouts/src/Transition.elm
Normal file
24
examples/08-layouts/src/Transition.elm
Normal file
@ -0,0 +1,24 @@
|
||||
module Transition exposing (Transition, layout, page)
|
||||
|
||||
|
||||
type alias Transition attr =
|
||||
{ duration : Int
|
||||
, invisible : List attr
|
||||
, visible : List attr
|
||||
}
|
||||
|
||||
|
||||
layout : Transition attr
|
||||
layout =
|
||||
{ duration = 0
|
||||
, invisible = []
|
||||
, visible = []
|
||||
}
|
||||
|
||||
|
||||
page : Transition attr
|
||||
page =
|
||||
{ duration = 0
|
||||
, invisible = []
|
||||
, visible = []
|
||||
}
|
37
examples/08-layouts/src/View.elm
Normal file
37
examples/08-layouts/src/View.elm
Normal file
@ -0,0 +1,37 @@
|
||||
module View exposing (View, map, none, placeholder, toBrowserDocument)
|
||||
|
||||
import Browser
|
||||
import Html exposing (Html)
|
||||
import Html.Attributes as Attr
|
||||
|
||||
|
||||
type alias View msg =
|
||||
{ title : String
|
||||
, body : List (Html msg)
|
||||
}
|
||||
|
||||
|
||||
placeholder : String -> View msg
|
||||
placeholder str =
|
||||
{ title = str
|
||||
, body = [ Html.h1 [ Attr.class "h1" ] [ Html.text str ] ]
|
||||
}
|
||||
|
||||
|
||||
none : View msg
|
||||
none =
|
||||
placeholder ""
|
||||
|
||||
|
||||
map : (a -> b) -> View a -> View b
|
||||
map fn view =
|
||||
{ title = view.title
|
||||
, body = List.map (Html.map fn) view.body
|
||||
}
|
||||
|
||||
|
||||
toBrowserDocument : View msg -> Browser.Document msg
|
||||
toBrowserDocument view =
|
||||
{ title = view.title
|
||||
, body = view.body
|
||||
}
|
@ -63,7 +63,7 @@ static :
|
||||
}
|
||||
-> Page shared route effect view () msg
|
||||
static none page =
|
||||
Page (\_ _ -> Ok (adapters.static none page))
|
||||
Page { toResult = \_ _ -> Ok (adapters.static none page) }
|
||||
|
||||
|
||||
{-| A page that can keep track of application state.
|
||||
@ -94,7 +94,7 @@ sandbox :
|
||||
}
|
||||
-> Page shared route effect view model msg
|
||||
sandbox none page =
|
||||
Page (\_ _ -> Ok (adapters.sandbox none page))
|
||||
Page { toResult = \_ _ -> Ok (adapters.sandbox none page) }
|
||||
|
||||
|
||||
{-| A page that can handle effects like [HTTP requests or subscriptions](https://guide.elm-lang.org/effects/).
|
||||
@ -128,7 +128,7 @@ element :
|
||||
}
|
||||
-> Page shared route effect view model msg
|
||||
element fromCmd page =
|
||||
Page (\_ _ -> Ok (adapters.element fromCmd page))
|
||||
Page { toResult = \_ _ -> Ok (adapters.element fromCmd page) }
|
||||
|
||||
|
||||
{-| A page that can handles **custom** effects like sending a `Shared.Msg` or other general user-defined effects.
|
||||
@ -159,7 +159,7 @@ advanced :
|
||||
}
|
||||
-> Page shared route effect view model msg
|
||||
advanced page =
|
||||
Page (\_ _ -> Ok (adapters.advanced page))
|
||||
Page { toResult = \_ _ -> Ok (adapters.advanced page) }
|
||||
|
||||
|
||||
{-| Actions to take when a user visits a `protected` page
|
||||
@ -252,14 +252,15 @@ protected options =
|
||||
let
|
||||
protect toPage toRecord =
|
||||
Page
|
||||
(\shared req ->
|
||||
case options.beforeInit shared req of
|
||||
Provide user ->
|
||||
Ok (user |> toRecord |> toPage)
|
||||
{ toResult =
|
||||
\shared req ->
|
||||
case options.beforeInit shared req of
|
||||
Provide user ->
|
||||
Ok (user |> toRecord |> toPage)
|
||||
|
||||
RedirectTo route ->
|
||||
Err route
|
||||
)
|
||||
RedirectTo route ->
|
||||
Err route
|
||||
}
|
||||
in
|
||||
{ static = protect (adapters.static options.effectNone)
|
||||
, sandbox = protect (adapters.sandbox options.effectNone)
|
||||
@ -366,10 +367,10 @@ toResult :
|
||||
-> Result route (PageRecord effect view model msg)
|
||||
toResult toPage shared req =
|
||||
let
|
||||
(Page toResult_) =
|
||||
(Page page) =
|
||||
toPage shared req
|
||||
in
|
||||
toResult_ shared (ElmSpa.Request.create req.route () req.url req.key)
|
||||
page.toResult shared (ElmSpa.Request.create req.route () req.url req.key)
|
||||
|
||||
|
||||
|
||||
@ -377,7 +378,8 @@ toResult toPage shared req =
|
||||
|
||||
|
||||
type alias Internals shared route effect view model msg =
|
||||
shared -> Request route () -> Result route (PageRecord effect view model msg)
|
||||
{ toResult : shared -> Request route () -> Result route (PageRecord effect view model msg)
|
||||
}
|
||||
|
||||
|
||||
type alias PageRecord effect view model msg =
|
||||
|
@ -3,8 +3,8 @@ module Main exposing (main)
|
||||
import Browser
|
||||
import Browser.Navigation as Nav exposing (Key)
|
||||
import Effect
|
||||
import Gen.Gen.Pages as Pages
|
||||
import Gen.Model
|
||||
import Gen.Pages as Pages
|
||||
import Gen.Route as Route
|
||||
import Request
|
||||
import Shared
|
||||
@ -43,7 +43,7 @@ init flags url key =
|
||||
Shared.init (Request.create () url key) flags
|
||||
|
||||
( page, effect ) =
|
||||
Pages.init (Route.fromUrl url) shared url key
|
||||
Gen.Pages.init (Route.fromUrl url) shared url key
|
||||
in
|
||||
( Model url key shared page
|
||||
, Cmd.batch
|
||||
@ -81,7 +81,7 @@ update msg model =
|
||||
if url.path /= model.url.path then
|
||||
let
|
||||
( page, effect ) =
|
||||
Pages.init (Route.fromUrl url) model.shared url model.key
|
||||
Gen.Pages.init (Route.fromUrl url) model.shared url model.key
|
||||
in
|
||||
( { model | url = url, page = page }
|
||||
, Effect.toCmd ( Shared, Page ) effect
|
||||
@ -96,7 +96,7 @@ update msg model =
|
||||
Shared.update (Request.create () model.url model.key) sharedMsg model.shared
|
||||
|
||||
( page, effect ) =
|
||||
Pages.init (Route.fromUrl model.url) shared model.url model.key
|
||||
Gen.Pages.init (Route.fromUrl model.url) shared model.url model.key
|
||||
in
|
||||
if page == Gen.Model.Redirecting_ then
|
||||
( { model | shared = shared, page = page }
|
||||
@ -114,7 +114,7 @@ update msg model =
|
||||
Page pageMsg ->
|
||||
let
|
||||
( page, effect ) =
|
||||
Pages.update pageMsg model.page model.shared model.url model.key
|
||||
Gen.Pages.update pageMsg model.page model.shared model.url model.key
|
||||
in
|
||||
( { model | page = page }
|
||||
, Effect.toCmd ( Shared, Page ) effect
|
||||
@ -127,7 +127,7 @@ update msg model =
|
||||
|
||||
view : Model -> Browser.Document Msg
|
||||
view model =
|
||||
Pages.view model.page model.shared model.url model.key
|
||||
Gen.Pages.view model.page model.shared model.url model.key
|
||||
|> View.map Page
|
||||
|> View.toBrowserDocument
|
||||
|
||||
@ -139,6 +139,6 @@ view model =
|
||||
subscriptions : Model -> Sub Msg
|
||||
subscriptions model =
|
||||
Sub.batch
|
||||
[ Pages.subscriptions model.page model.shared model.url model.key |> Sub.map Page
|
||||
[ Gen.Pages.subscriptions model.page model.shared model.url model.key |> Sub.map Page
|
||||
, Shared.subscriptions (Request.create () model.url model.key) model.shared |> Sub.map Shared
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user