mirror of
https://github.com/ryannhg/elm-spa.git
synced 2024-11-22 01:32:43 +03:00
more examples never hurt nobody
This commit is contained in:
parent
099e910865
commit
0e39d0375e
6
examples/transitions/.gitignore
vendored
Normal file
6
examples/transitions/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
.DS_Store
|
||||
dist
|
||||
elm-stuff
|
||||
node_modules
|
||||
|
||||
src/Generated
|
148
examples/transitions/README.md
Normal file
148
examples/transitions/README.md
Normal file
@ -0,0 +1,148 @@
|
||||
# examples/transition
|
||||
|
||||
```
|
||||
npm start
|
||||
```
|
||||
|
||||
## how i added transitions
|
||||
|
||||
```
|
||||
npm install -g elm-spa
|
||||
elm-spa init transitions
|
||||
cd transitions
|
||||
npm start
|
||||
```
|
||||
|
||||
### 1. add more info to the model
|
||||
|
||||
__`src/Main.elm`__
|
||||
|
||||
```elm
|
||||
type alias Model =
|
||||
{ key : Key
|
||||
, url : Url
|
||||
, isTransitioning : Bool
|
||||
, global : Global.Model
|
||||
, page : Pages.Model
|
||||
}
|
||||
```
|
||||
|
||||
### 2. handle updates to that state
|
||||
|
||||
Added a new message called `PageLoaded` msg to delay the effects of `UrlChanged`:
|
||||
|
||||
__`src/Main.elm`__
|
||||
|
||||
```elm
|
||||
type Msg
|
||||
= LinkClicked Browser.UrlRequest
|
||||
| UrlChanged Url
|
||||
| PageLoaded Url
|
||||
| Global Global.Msg
|
||||
| Page Pages.Msg
|
||||
```
|
||||
|
||||
```elm
|
||||
import Process
|
||||
import Task
|
||||
|
||||
delay : Float -> msg -> Cmd msg
|
||||
delay ms msg =
|
||||
Process.sleep ms
|
||||
|> Task.andThen (\_ -> Task.succeed msg)
|
||||
|> Task.perform identity
|
||||
```
|
||||
|
||||
```elm
|
||||
update msg model =
|
||||
case msg of
|
||||
|
||||
-- ...
|
||||
|
||||
UrlChanged url ->
|
||||
( { model | isTransitioning = True }
|
||||
, delay 300 (PageLoaded url)
|
||||
)
|
||||
|
||||
PageLoaded url ->
|
||||
let
|
||||
( page, pageCmd, globalCmd ) =
|
||||
Pages.init (fromUrl url) model.global
|
||||
in
|
||||
( { model
|
||||
| isTransitioning = False
|
||||
, url = url
|
||||
, page = page
|
||||
}
|
||||
, Cmd.batch
|
||||
[ Cmd.map Page pageCmd
|
||||
, Cmd.map Global globalCmd
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
### 3. pass that info to the layout component
|
||||
|
||||
__`src/Main.elm`__
|
||||
|
||||
```elm
|
||||
Global.view
|
||||
{ page = Pages.view model.page model.global |> documentMap Page
|
||||
, global = model.global
|
||||
, toMsg = Global
|
||||
, isTransitioning = model.isTransitioning
|
||||
}
|
||||
```
|
||||
|
||||
__`src/Global.elm`__
|
||||
|
||||
```elm
|
||||
view :
|
||||
{ page : Document msg
|
||||
, global : Model
|
||||
, toMsg : Msg -> msg
|
||||
, isTransitioning : Bool
|
||||
}
|
||||
-> Document msg
|
||||
view { page, global, toMsg, isTransitioning } =
|
||||
Components.layout
|
||||
{ page = page
|
||||
, isTransitioning = isTransitioning
|
||||
}
|
||||
```
|
||||
|
||||
__`src/Components.elm`__
|
||||
|
||||
```elm
|
||||
layout :
|
||||
{ page : Document msg
|
||||
, isTransitioning : Bool
|
||||
}
|
||||
-> Document msg
|
||||
layout { page, isTransitioning } =
|
||||
{ title = page.title
|
||||
, body =
|
||||
[ div [ class "column spacing--large pad--medium container h--fill" ]
|
||||
[ navbar
|
||||
, div
|
||||
[ class "column"
|
||||
, style "flex" "1 0 auto"
|
||||
, style "transition" "opacity 300ms ease-in-out"
|
||||
, style "opacity"
|
||||
(if isTransitioning then
|
||||
"0"
|
||||
|
||||
else
|
||||
"1"
|
||||
)
|
||||
]
|
||||
page.body
|
||||
, footer
|
||||
]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### works with elm-ui too!
|
||||
|
||||
this example is using `elm/html`, but a similar strategy would work for `elm-ui` (just use `alpha` instead!)
|
25
examples/transitions/elm.json
Normal file
25
examples/transitions/elm.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"type": "application",
|
||||
"source-directories": [
|
||||
"src",
|
||||
"../../src"
|
||||
],
|
||||
"elm-version": "0.19.1",
|
||||
"dependencies": {
|
||||
"direct": {
|
||||
"elm/browser": "1.0.2",
|
||||
"elm/core": "1.0.5",
|
||||
"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": {}
|
||||
}
|
||||
}
|
1846
examples/transitions/package-lock.json
generated
Normal file
1846
examples/transitions/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
examples/transitions/package.json
Normal file
28
examples/transitions/package.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "elm-spa-app",
|
||||
"version": "1.0.0",
|
||||
"description": "my new elm-spa application",
|
||||
"main": "public/index.html",
|
||||
"scripts": {
|
||||
"start": "npm install && npm run build && npm run dev",
|
||||
"build": "npm run build:elm-spa && npm run build:elm",
|
||||
"build:elm-spa": "elm-spa build .",
|
||||
"build:elm": "elm make src/Main.elm --optimize --output public/dist/elm.js",
|
||||
"dev": "concurrently --raw --kill-others \"npm run dev:elm-spa\" \"npm run dev:elm\"",
|
||||
"dev:elm-spa": "chokidar src/Pages -c \"npm run build:elm-spa\"",
|
||||
"dev:elm": "elm-live src/Main.elm -u -d public -- --debug --output public/dist/elm.js"
|
||||
},
|
||||
"keywords": [
|
||||
"elm",
|
||||
"spa"
|
||||
],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"chokidar-cli": "2.1.0",
|
||||
"concurrently": "5.0.0",
|
||||
"elm": "0.19.1-3",
|
||||
"elm-live": "4.0.2",
|
||||
"elm-spa": "4.0.5"
|
||||
}
|
||||
}
|
15
examples/transitions/public/index.html
Normal file
15
examples/transitions/public/index.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!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://not-much-css.netlify.com/not-much.css" />
|
||||
<title>elm-spa</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="/dist/elm.js"></script>
|
||||
<script>
|
||||
Elm.Main.init()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
52
examples/transitions/src/Components.elm
Normal file
52
examples/transitions/src/Components.elm
Normal file
@ -0,0 +1,52 @@
|
||||
module Components exposing (layout)
|
||||
|
||||
import Browser exposing (Document)
|
||||
import Generated.Route as Route exposing (Route)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes as Attr exposing (class, href, style)
|
||||
|
||||
|
||||
layout :
|
||||
{ page : Document msg
|
||||
, isTransitioning : Bool
|
||||
}
|
||||
-> Document msg
|
||||
layout { page, isTransitioning } =
|
||||
{ title = page.title
|
||||
, body =
|
||||
[ div [ class "column spacing--large pad--medium container h--fill" ]
|
||||
[ navbar
|
||||
, div
|
||||
[ class "column"
|
||||
, style "flex" "1 0 auto"
|
||||
, style "transition" "opacity 300ms ease-in-out"
|
||||
, style "opacity"
|
||||
(if isTransitioning then
|
||||
"0"
|
||||
|
||||
else
|
||||
"1"
|
||||
)
|
||||
]
|
||||
page.body
|
||||
, footer
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
navbar : Html msg
|
||||
navbar =
|
||||
header [ class "row center-y spacing--between" ]
|
||||
[ a [ class "link font--h5", href (Route.toHref Route.Top) ] [ text "home" ]
|
||||
, div [ class "row center-y spacing--medium" ]
|
||||
[ a [ class "link", href (Route.toHref Route.Docs) ] [ text "docs" ]
|
||||
, a [ class "link", href (Route.toHref Route.NotFound) ] [ text "a broken link" ]
|
||||
, a [ class "button", href "https://twitter.com/intent/tweet?text=elm-spa is ez pz" ] [ text "tweet about it" ]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
footer : Html msg
|
||||
footer =
|
||||
Html.footer [] [ text "built with elm ❤" ]
|
100
examples/transitions/src/Global.elm
Normal file
100
examples/transitions/src/Global.elm
Normal file
@ -0,0 +1,100 @@
|
||||
module Global exposing
|
||||
( Flags
|
||||
, Model
|
||||
, Msg
|
||||
, init
|
||||
, navigate
|
||||
, subscriptions
|
||||
, update
|
||||
, view
|
||||
)
|
||||
|
||||
import Browser exposing (Document)
|
||||
import Browser.Navigation as Nav
|
||||
import Components
|
||||
import Generated.Route as Route exposing (Route)
|
||||
import Task
|
||||
import Url exposing (Url)
|
||||
|
||||
|
||||
|
||||
-- INIT
|
||||
|
||||
|
||||
type alias Flags =
|
||||
()
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ flags : Flags
|
||||
, url : Url
|
||||
, key : Nav.Key
|
||||
}
|
||||
|
||||
|
||||
init : Flags -> Url -> Nav.Key -> ( Model, Cmd Msg )
|
||||
init flags url key =
|
||||
( Model
|
||||
flags
|
||||
url
|
||||
key
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
|
||||
|
||||
-- UPDATE
|
||||
|
||||
|
||||
type Msg
|
||||
= Navigate Route
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update msg model =
|
||||
case msg of
|
||||
Navigate route ->
|
||||
( model
|
||||
, Nav.pushUrl model.key (Route.toHref route)
|
||||
)
|
||||
|
||||
|
||||
|
||||
-- SUBSCRIPTIONS
|
||||
|
||||
|
||||
subscriptions : Model -> Sub Msg
|
||||
subscriptions model =
|
||||
Sub.none
|
||||
|
||||
|
||||
|
||||
-- VIEW
|
||||
|
||||
|
||||
view :
|
||||
{ page : Document msg
|
||||
, global : Model
|
||||
, toMsg : Msg -> msg
|
||||
, isTransitioning : Bool
|
||||
}
|
||||
-> Document msg
|
||||
view { page, global, toMsg, isTransitioning } =
|
||||
Components.layout
|
||||
{ page = page
|
||||
, isTransitioning = isTransitioning
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- COMMANDS
|
||||
|
||||
|
||||
send : msg -> Cmd msg
|
||||
send =
|
||||
Task.succeed >> Task.perform identity
|
||||
|
||||
|
||||
navigate : Route -> Cmd Msg
|
||||
navigate route =
|
||||
send (Navigate route)
|
162
examples/transitions/src/Main.elm
Normal file
162
examples/transitions/src/Main.elm
Normal file
@ -0,0 +1,162 @@
|
||||
module Main exposing (main)
|
||||
|
||||
import Browser exposing (Document)
|
||||
import Browser.Navigation as Nav exposing (Key)
|
||||
import Generated.Pages as Pages
|
||||
import Generated.Route as Route exposing (Route)
|
||||
import Global
|
||||
import Html
|
||||
import Process
|
||||
import Task
|
||||
import Url exposing (Url)
|
||||
|
||||
|
||||
main : Program Flags Model Msg
|
||||
main =
|
||||
Browser.application
|
||||
{ init = init
|
||||
, view = view
|
||||
, update = update
|
||||
, subscriptions = subscriptions
|
||||
, onUrlRequest = LinkClicked
|
||||
, onUrlChange = UrlChanged
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- INIT
|
||||
|
||||
|
||||
type alias Flags =
|
||||
()
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ key : Key
|
||||
, url : Url
|
||||
, isTransitioning : Bool
|
||||
, global : Global.Model
|
||||
, page : Pages.Model
|
||||
}
|
||||
|
||||
|
||||
init : Flags -> Url -> Key -> ( Model, Cmd Msg )
|
||||
init flags url key =
|
||||
let
|
||||
( global, globalCmd ) =
|
||||
Global.init flags url key
|
||||
|
||||
( page, pageCmd, pageGlobalCmd ) =
|
||||
Pages.init (fromUrl url) global
|
||||
in
|
||||
( Model key url False global page
|
||||
, Cmd.batch
|
||||
[ Cmd.map Global globalCmd
|
||||
, Cmd.map Global pageGlobalCmd
|
||||
, Cmd.map Page pageCmd
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
type Msg
|
||||
= LinkClicked Browser.UrlRequest
|
||||
| UrlChanged Url
|
||||
| PageLoaded Url
|
||||
| Global Global.Msg
|
||||
| Page Pages.Msg
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update msg model =
|
||||
case msg of
|
||||
LinkClicked (Browser.Internal url) ->
|
||||
( model, Nav.pushUrl model.key (Url.toString url) )
|
||||
|
||||
LinkClicked (Browser.External href) ->
|
||||
( model, Nav.load href )
|
||||
|
||||
UrlChanged url ->
|
||||
( { model | isTransitioning = True }
|
||||
, delay 300 (PageLoaded url)
|
||||
)
|
||||
|
||||
PageLoaded url ->
|
||||
let
|
||||
( page, pageCmd, globalCmd ) =
|
||||
Pages.init (fromUrl url) model.global
|
||||
in
|
||||
( { model
|
||||
| isTransitioning = False
|
||||
, url = url
|
||||
, page = page
|
||||
}
|
||||
, Cmd.batch
|
||||
[ Cmd.map Page pageCmd
|
||||
, Cmd.map Global globalCmd
|
||||
]
|
||||
)
|
||||
|
||||
Global globalMsg ->
|
||||
let
|
||||
( global, globalCmd ) =
|
||||
Global.update globalMsg model.global
|
||||
in
|
||||
( { model | global = global }
|
||||
, Cmd.map Global globalCmd
|
||||
)
|
||||
|
||||
Page pageMsg ->
|
||||
let
|
||||
( page, pageCmd, globalCmd ) =
|
||||
Pages.update pageMsg model.page model.global
|
||||
in
|
||||
( { model | page = page }
|
||||
, Cmd.batch
|
||||
[ Cmd.map Page pageCmd
|
||||
, Cmd.map Global globalCmd
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
delay : Float -> msg -> Cmd msg
|
||||
delay ms msg =
|
||||
Process.sleep ms
|
||||
|> Task.andThen (\_ -> Task.succeed msg)
|
||||
|> Task.perform identity
|
||||
|
||||
|
||||
subscriptions : Model -> Sub Msg
|
||||
subscriptions model =
|
||||
Sub.batch
|
||||
[ model.global
|
||||
|> Global.subscriptions
|
||||
|> Sub.map Global
|
||||
, model.page
|
||||
|> (\page -> Pages.subscriptions page model.global)
|
||||
|> Sub.map Page
|
||||
]
|
||||
|
||||
|
||||
view : Model -> Browser.Document Msg
|
||||
view model =
|
||||
let
|
||||
documentMap :
|
||||
(msg1 -> msg2)
|
||||
-> Document msg1
|
||||
-> Document msg2
|
||||
documentMap fn doc =
|
||||
{ title = doc.title
|
||||
, body = List.map (Html.map fn) doc.body
|
||||
}
|
||||
in
|
||||
Global.view
|
||||
{ page = Pages.view model.page model.global |> documentMap Page
|
||||
, isTransitioning = model.isTransitioning
|
||||
, global = model.global
|
||||
, toMsg = Global
|
||||
}
|
||||
|
||||
|
||||
fromUrl : Url -> Route
|
||||
fromUrl =
|
||||
Route.fromUrl >> Maybe.withDefault Route.NotFound
|
81
examples/transitions/src/Page.elm
Normal file
81
examples/transitions/src/Page.elm
Normal file
@ -0,0 +1,81 @@
|
||||
module Page exposing
|
||||
( Page, Document, Bundle
|
||||
, upgrade
|
||||
, static, sandbox, element, component
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
@docs Page, Document, Bundle
|
||||
|
||||
@docs upgrade
|
||||
|
||||
@docs static, sandbox, element, component
|
||||
|
||||
-}
|
||||
|
||||
import Browser
|
||||
import Global
|
||||
import Spa
|
||||
|
||||
|
||||
type alias Document msg =
|
||||
Browser.Document msg
|
||||
|
||||
|
||||
type alias Page flags model msg =
|
||||
Spa.Page flags model msg Global.Model Global.Msg
|
||||
|
||||
|
||||
type alias Bundle msg =
|
||||
Spa.Bundle msg
|
||||
|
||||
|
||||
upgrade :
|
||||
(pageModel -> model)
|
||||
-> (pageMsg -> msg)
|
||||
-> Page pageFlags pageModel pageMsg
|
||||
->
|
||||
{ init : pageFlags -> Global.Model -> ( model, Cmd msg, Cmd Global.Msg )
|
||||
, update : pageMsg -> pageModel -> Global.Model -> ( model, Cmd msg, Cmd Global.Msg )
|
||||
, bundle : pageModel -> Global.Model -> Bundle msg
|
||||
}
|
||||
upgrade =
|
||||
Spa.upgrade
|
||||
|
||||
|
||||
static : { view : Document msg } -> Page flags () msg
|
||||
static =
|
||||
Spa.static
|
||||
|
||||
|
||||
sandbox :
|
||||
{ init : model
|
||||
, update : msg -> model -> model
|
||||
, view : model -> Document msg
|
||||
}
|
||||
-> Page flags model msg
|
||||
sandbox =
|
||||
Spa.sandbox
|
||||
|
||||
|
||||
element :
|
||||
{ init : flags -> ( model, Cmd msg )
|
||||
, update : msg -> model -> ( model, Cmd msg )
|
||||
, subscriptions : model -> Sub msg
|
||||
, view : model -> Document msg
|
||||
}
|
||||
-> Page flags model msg
|
||||
element =
|
||||
Spa.element
|
||||
|
||||
|
||||
component :
|
||||
{ init : Global.Model -> flags -> ( model, Cmd msg, Cmd Global.Msg )
|
||||
, update : Global.Model -> msg -> model -> ( model, Cmd msg, Cmd Global.Msg )
|
||||
, subscriptions : Global.Model -> model -> Sub msg
|
||||
, view : Global.Model -> model -> Document msg
|
||||
}
|
||||
-> Page flags model msg
|
||||
component =
|
||||
Spa.component
|
30
examples/transitions/src/Pages/Docs.elm
Normal file
30
examples/transitions/src/Pages/Docs.elm
Normal file
@ -0,0 +1,30 @@
|
||||
module Pages.Docs exposing (Flags, Model, Msg, page)
|
||||
|
||||
import Html
|
||||
import Page exposing (Document, Page)
|
||||
|
||||
|
||||
type alias Flags =
|
||||
()
|
||||
|
||||
|
||||
type alias Model =
|
||||
()
|
||||
|
||||
|
||||
type alias Msg =
|
||||
Never
|
||||
|
||||
|
||||
page : Page Flags Model Msg
|
||||
page =
|
||||
Page.static
|
||||
{ view = view
|
||||
}
|
||||
|
||||
|
||||
view : Document Msg
|
||||
view =
|
||||
{ title = "Docs"
|
||||
, body = [ Html.text "Docs" ]
|
||||
}
|
30
examples/transitions/src/Pages/NotFound.elm
Normal file
30
examples/transitions/src/Pages/NotFound.elm
Normal file
@ -0,0 +1,30 @@
|
||||
module Pages.NotFound exposing (Flags, Model, Msg, page)
|
||||
|
||||
import Html
|
||||
import Page exposing (Document, Page)
|
||||
|
||||
|
||||
type alias Flags =
|
||||
()
|
||||
|
||||
|
||||
type alias Model =
|
||||
()
|
||||
|
||||
|
||||
type alias Msg =
|
||||
Never
|
||||
|
||||
|
||||
page : Page Flags Model Msg
|
||||
page =
|
||||
Page.static
|
||||
{ view = view
|
||||
}
|
||||
|
||||
|
||||
view : Document Msg
|
||||
view =
|
||||
{ title = "NotFound"
|
||||
, body = [ Html.text "NotFound" ]
|
||||
}
|
30
examples/transitions/src/Pages/Top.elm
Normal file
30
examples/transitions/src/Pages/Top.elm
Normal file
@ -0,0 +1,30 @@
|
||||
module Pages.Top exposing (Flags, Model, Msg, page)
|
||||
|
||||
import Html
|
||||
import Page exposing (Document, Page)
|
||||
|
||||
|
||||
type alias Flags =
|
||||
()
|
||||
|
||||
|
||||
type alias Model =
|
||||
()
|
||||
|
||||
|
||||
type alias Msg =
|
||||
Never
|
||||
|
||||
|
||||
page : Page Flags Model Msg
|
||||
page =
|
||||
Page.static
|
||||
{ view = view
|
||||
}
|
||||
|
||||
|
||||
view : Document Msg
|
||||
view =
|
||||
{ title = "Top"
|
||||
, body = [ Html.text "Top" ]
|
||||
}
|
Loading…
Reference in New Issue
Block a user