Initial import
This commit is contained in:
commit
cf706be388
|
@ -0,0 +1,8 @@
|
||||||
|
# Folders to ignore
|
||||||
|
elm-stuff
|
||||||
|
node_modules
|
||||||
|
public/dist
|
||||||
|
src/Spa/Generated
|
||||||
|
|
||||||
|
# MacOS weird stuff
|
||||||
|
.DS_Store
|
|
@ -0,0 +1,30 @@
|
||||||
|
# abra-apps
|
||||||
|
|
||||||
|
Created using [`elm-spa`](https://elm-spa.dev)
|
||||||
|
|
||||||
|
## local development
|
||||||
|
|
||||||
|
You can get this site up and running with one command:
|
||||||
|
|
||||||
|
```
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
### other commands to know
|
||||||
|
|
||||||
|
There are a handful of commands in the [package.json](./package.json).
|
||||||
|
|
||||||
|
Command | Description
|
||||||
|
:-- | :--
|
||||||
|
`npm run dev` | Run a dev server and automatically build changes.
|
||||||
|
`npm run test:watch` | Run tests as you code.
|
||||||
|
`npm run build` | Build the site for production.
|
||||||
|
`npm run test` | Run the test suite once, great for CI
|
||||||
|
|
||||||
|
## deploying
|
||||||
|
|
||||||
|
After you run `npm run build`, the contents of the `public` folder can be hosted as a static site. If you haven't hosted a static site before, I'd recommend using [Netlify](https://netlify.com) (it's free!)
|
||||||
|
|
||||||
|
__Build command:__ `npm run build`
|
||||||
|
|
||||||
|
__Publish directory:__ `public`
|
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"type": "application",
|
||||||
|
"source-directories": [
|
||||||
|
"src"
|
||||||
|
],
|
||||||
|
"elm-version": "0.19.1",
|
||||||
|
"dependencies": {
|
||||||
|
"direct": {
|
||||||
|
"elm/browser": "1.0.2",
|
||||||
|
"elm/core": "1.0.5",
|
||||||
|
"elm/html": "1.0.0",
|
||||||
|
"elm/http": "2.0.0",
|
||||||
|
"elm/json": "1.1.3",
|
||||||
|
"elm/url": "1.0.0"
|
||||||
|
},
|
||||||
|
"indirect": {
|
||||||
|
"elm/time": "1.0.0",
|
||||||
|
"elm/virtual-dom": "1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test-dependencies": {
|
||||||
|
"direct": {
|
||||||
|
"avh4/elm-program-test": "3.2.0",
|
||||||
|
"elm-explorations/test": "1.2.2"
|
||||||
|
},
|
||||||
|
"indirect": {
|
||||||
|
"avh4/elm-fifo": "1.0.4",
|
||||||
|
"elm/bytes": "1.0.8",
|
||||||
|
"elm/file": "1.0.5",
|
||||||
|
"elm/random": "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"name": "our-elm-spa-app",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "A project created with elm-spa",
|
||||||
|
"scripts": {
|
||||||
|
"start": "npm install && npm run build:dev && npm run dev",
|
||||||
|
"test": "elm-test",
|
||||||
|
"test:watch": "elm-test --watch",
|
||||||
|
"build": "run-s build:elm-spa build:elm",
|
||||||
|
"build:dev": "run-s build:elm-spa build:dev:elm",
|
||||||
|
"dev": "run-p dev:elm-spa dev:elm",
|
||||||
|
"build:elm": "elm make src/Main.elm --optimize --output=public/dist/elm.compiled.js",
|
||||||
|
"build:dev:elm": "elm make src/Main.elm --debug --output=public/dist/elm.compiled.js || true",
|
||||||
|
"build:elm-spa": "elm-spa build .",
|
||||||
|
"dev:elm": "elm-live src/Main.elm -u -d public -- --debug --output=public/dist/elm.compiled.js",
|
||||||
|
"dev:elm-spa": "chokidar src/Pages -c \"elm-spa build .\""
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"chokidar-cli": "2.1.0",
|
||||||
|
"elm": "0.19.1-3",
|
||||||
|
"elm-live": "4.0.2",
|
||||||
|
"elm-spa": "5.0.4",
|
||||||
|
"elm-test": "0.19.1-revision2",
|
||||||
|
"npm-run-all": "4.1.5"
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,16 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<!-- CSS goes here -->
|
||||||
|
<link rel="stylesheet" href="/style.css">
|
||||||
|
<link rel="stylesheet"
|
||||||
|
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- JavaScript goes here -->
|
||||||
|
<script src="/dist/elm.compiled.js"></script>
|
||||||
|
<script src="/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,9 @@
|
||||||
|
// Initial data passed to Elm (should match `Flags` defined in `Shared.elm`)
|
||||||
|
// https://guide.elm-lang.org/interop/flags.html
|
||||||
|
var flags = null
|
||||||
|
|
||||||
|
// Start our Elm application
|
||||||
|
var app = Elm.Main.init({ flags: flags })
|
||||||
|
|
||||||
|
// Ports go here
|
||||||
|
// https://guide.elm-lang.org/interop/ports.html
|
|
@ -0,0 +1,12 @@
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
margin: 20px
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar > *:not(:last-child) { margin-right: 20px; }
|
||||||
|
|
||||||
|
.page { margin-top: 20px; }
|
|
@ -0,0 +1,148 @@
|
||||||
|
module Main exposing (main)
|
||||||
|
|
||||||
|
import Browser
|
||||||
|
import Browser.Navigation as Nav
|
||||||
|
import Shared exposing (Flags)
|
||||||
|
import Spa.Document as Document exposing (Document)
|
||||||
|
import Spa.Generated.Pages as Pages
|
||||||
|
import Spa.Generated.Route as Route exposing (Route)
|
||||||
|
import Url exposing (Url)
|
||||||
|
|
||||||
|
|
||||||
|
main : Program Flags Model Msg
|
||||||
|
main =
|
||||||
|
Browser.application
|
||||||
|
{ init = init
|
||||||
|
, update = update
|
||||||
|
, subscriptions = subscriptions
|
||||||
|
, view = view >> Document.toBrowserDocument
|
||||||
|
, onUrlRequest = LinkClicked
|
||||||
|
, onUrlChange = UrlChanged
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- INIT
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{ shared : Shared.Model
|
||||||
|
, page : Pages.Model
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init : Flags -> Url -> Nav.Key -> ( Model, Cmd Msg )
|
||||||
|
init flags url key =
|
||||||
|
let
|
||||||
|
( shared, sharedCmd ) =
|
||||||
|
Shared.init flags url key
|
||||||
|
|
||||||
|
( page, pageCmd ) =
|
||||||
|
Pages.init (fromUrl url) shared
|
||||||
|
|
||||||
|
savedShare =
|
||||||
|
Pages.save page shared
|
||||||
|
in
|
||||||
|
( Model savedShare page
|
||||||
|
, Cmd.batch
|
||||||
|
[ Cmd.map Shared sharedCmd
|
||||||
|
, Cmd.map Pages pageCmd
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- UPDATE
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= LinkClicked Browser.UrlRequest
|
||||||
|
| UrlChanged Url
|
||||||
|
| Shared Shared.Msg
|
||||||
|
| Pages Pages.Msg
|
||||||
|
|
||||||
|
|
||||||
|
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||||
|
update msg model =
|
||||||
|
case msg of
|
||||||
|
LinkClicked (Browser.Internal url) ->
|
||||||
|
( model
|
||||||
|
, Nav.pushUrl model.shared.key (Url.toString url)
|
||||||
|
)
|
||||||
|
|
||||||
|
LinkClicked (Browser.External href) ->
|
||||||
|
( model
|
||||||
|
, Nav.load href
|
||||||
|
)
|
||||||
|
|
||||||
|
UrlChanged url ->
|
||||||
|
let
|
||||||
|
original =
|
||||||
|
model.shared
|
||||||
|
|
||||||
|
shared =
|
||||||
|
{ original | url = url }
|
||||||
|
|
||||||
|
( page, pageCmd ) =
|
||||||
|
Pages.init (fromUrl url) shared
|
||||||
|
in
|
||||||
|
( { model | page = page, shared = Pages.save page shared }
|
||||||
|
, Cmd.map Pages pageCmd
|
||||||
|
)
|
||||||
|
|
||||||
|
Shared sharedMsg ->
|
||||||
|
let
|
||||||
|
( shared, sharedCmd ) =
|
||||||
|
Shared.update sharedMsg model.shared
|
||||||
|
|
||||||
|
( page, pageCmd ) =
|
||||||
|
Pages.load model.page shared
|
||||||
|
in
|
||||||
|
( { model | page = page, shared = shared }
|
||||||
|
, Cmd.batch
|
||||||
|
[ Cmd.map Shared sharedCmd
|
||||||
|
, Cmd.map Pages pageCmd
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
Pages pageMsg ->
|
||||||
|
let
|
||||||
|
( page, pageCmd ) =
|
||||||
|
Pages.update pageMsg model.page
|
||||||
|
|
||||||
|
shared =
|
||||||
|
Pages.save page model.shared
|
||||||
|
in
|
||||||
|
( { model | page = page, shared = shared }
|
||||||
|
, Cmd.map Pages pageCmd
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
view : Model -> Document Msg
|
||||||
|
view model =
|
||||||
|
Shared.view
|
||||||
|
{ page =
|
||||||
|
Pages.view model.page
|
||||||
|
|> Document.map Pages
|
||||||
|
, toMsg = Shared
|
||||||
|
}
|
||||||
|
model.shared
|
||||||
|
|
||||||
|
|
||||||
|
subscriptions : Model -> Sub Msg
|
||||||
|
subscriptions model =
|
||||||
|
Sub.batch
|
||||||
|
[ Shared.subscriptions model.shared
|
||||||
|
|> Sub.map Shared
|
||||||
|
, Pages.subscriptions model.page
|
||||||
|
|> Sub.map Pages
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- URL
|
||||||
|
|
||||||
|
|
||||||
|
fromUrl : Url -> Route
|
||||||
|
fromUrl =
|
||||||
|
Route.fromUrl >> Maybe.withDefault Route.NotFound
|
|
@ -0,0 +1,38 @@
|
||||||
|
module Pages.About exposing (Model, Msg, Params, page)
|
||||||
|
|
||||||
|
import Html exposing (..)
|
||||||
|
import Spa.Document exposing (Document)
|
||||||
|
import Spa.Page as Page exposing (Page)
|
||||||
|
import Spa.Url exposing (Url)
|
||||||
|
|
||||||
|
|
||||||
|
type alias Params =
|
||||||
|
()
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
Url Params
|
||||||
|
|
||||||
|
|
||||||
|
type alias Msg =
|
||||||
|
Never
|
||||||
|
|
||||||
|
|
||||||
|
page : Page Params Model Msg
|
||||||
|
page =
|
||||||
|
Page.static
|
||||||
|
{ view = view
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
|
view : Url Params -> Document Msg
|
||||||
|
view { params } =
|
||||||
|
{ title = "about"
|
||||||
|
, body =
|
||||||
|
[ text "about"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
module Pages.NotFound exposing (Model, Msg, Params, page)
|
||||||
|
|
||||||
|
import Html exposing (..)
|
||||||
|
import Spa.Document exposing (Document)
|
||||||
|
import Spa.Page as Page exposing (Page)
|
||||||
|
import Spa.Url exposing (Url)
|
||||||
|
|
||||||
|
|
||||||
|
type alias Params =
|
||||||
|
()
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
Url Params
|
||||||
|
|
||||||
|
|
||||||
|
type alias Msg =
|
||||||
|
Never
|
||||||
|
|
||||||
|
|
||||||
|
page : Page Params Model Msg
|
||||||
|
page =
|
||||||
|
Page.static
|
||||||
|
{ view = view
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
|
view : Url Params -> Document Msg
|
||||||
|
view { params } =
|
||||||
|
{ title = "404"
|
||||||
|
, body =
|
||||||
|
[ text "Not found"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
module Pages.Top exposing (Model, Msg, Params, page)
|
||||||
|
|
||||||
|
import Html exposing (Html, button, div, h2, h5, img, text, ul, li, a)
|
||||||
|
import Html.Attributes exposing (src, style, class)
|
||||||
|
import Html.Events exposing (onClick)
|
||||||
|
import Http
|
||||||
|
import Json.Decode as Decode
|
||||||
|
import Spa.Document exposing (Document)
|
||||||
|
import Spa.Page as Page exposing (Page)
|
||||||
|
import Spa.Url as Url exposing (Url)
|
||||||
|
|
||||||
|
|
||||||
|
page : Page Params Model Msg
|
||||||
|
page =
|
||||||
|
Page.element
|
||||||
|
{ init = init
|
||||||
|
, update = update
|
||||||
|
, view = view
|
||||||
|
, subscriptions = subscriptions
|
||||||
|
}
|
||||||
|
|
||||||
|
-- INIT
|
||||||
|
|
||||||
|
|
||||||
|
type alias Params =
|
||||||
|
()
|
||||||
|
|
||||||
|
type alias App =
|
||||||
|
{ name : String
|
||||||
|
, category : String
|
||||||
|
, repository : Maybe String
|
||||||
|
, versions : Maybe (List String)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type Model
|
||||||
|
= Failure
|
||||||
|
| Loading
|
||||||
|
| Success (List App)
|
||||||
|
|
||||||
|
|
||||||
|
init : Url Params -> ( Model, Cmd Msg )
|
||||||
|
init { params } =
|
||||||
|
( Loading, loadApps )
|
||||||
|
|
||||||
|
|
||||||
|
-- UPDATE
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= MorePlease
|
||||||
|
| GotApps (Result Http.Error (List App))
|
||||||
|
|
||||||
|
|
||||||
|
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||||
|
update msg model =
|
||||||
|
case msg of
|
||||||
|
MorePlease ->
|
||||||
|
( Loading, loadApps )
|
||||||
|
|
||||||
|
GotApps result ->
|
||||||
|
case result of
|
||||||
|
Ok apps ->
|
||||||
|
( Success apps, Cmd.none )
|
||||||
|
|
||||||
|
Err _ ->
|
||||||
|
( Failure, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
|
subscriptions : Model -> Sub Msg
|
||||||
|
subscriptions model =
|
||||||
|
Sub.none
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
|
view : Model -> Document Msg
|
||||||
|
view model =
|
||||||
|
{ title = "Examples.Cats"
|
||||||
|
, body = [ body model ]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
body : Model -> Html Msg
|
||||||
|
body model =
|
||||||
|
div []
|
||||||
|
[ viewApps model
|
||||||
|
]
|
||||||
|
|
||||||
|
viewAppName : App -> Html Msg
|
||||||
|
viewAppName app =
|
||||||
|
div [ class "col-4" ]
|
||||||
|
[ div [ class "card" ]
|
||||||
|
[ div [ class "card-body" ]
|
||||||
|
[ h5 [ class "card-title" ] [ text app.name ]
|
||||||
|
, div [ class "card-body" ]
|
||||||
|
[ text app.category
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
viewApps : Model -> Html Msg
|
||||||
|
viewApps model =
|
||||||
|
case model of
|
||||||
|
Failure ->
|
||||||
|
div [ ]
|
||||||
|
[ text "I could not load a random cat for some reason. "
|
||||||
|
, button [ onClick MorePlease ] [ text "Try Again!" ]
|
||||||
|
]
|
||||||
|
|
||||||
|
Loading ->
|
||||||
|
text "Loading..."
|
||||||
|
|
||||||
|
Success apps ->
|
||||||
|
div []
|
||||||
|
[ div [ class "row" ]
|
||||||
|
(List.map viewAppName (List.sortBy .name apps))
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- HTTP
|
||||||
|
|
||||||
|
|
||||||
|
loadApps : Cmd Msg
|
||||||
|
loadApps =
|
||||||
|
Http.get
|
||||||
|
{ url = "http://localhost:8000/abra-apps-list.json"
|
||||||
|
, expect = Http.expectJson GotApps appListDecoder
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
appDecoder : Decode.Decoder App
|
||||||
|
appDecoder =
|
||||||
|
Decode.map4
|
||||||
|
App
|
||||||
|
(Decode.field "name" Decode.string)
|
||||||
|
(Decode.field "category" Decode.string)
|
||||||
|
(Decode.succeed Nothing)
|
||||||
|
(Decode.succeed Nothing)
|
||||||
|
|
||||||
|
appListDecoder : Decode.Decoder (List App)
|
||||||
|
appListDecoder =
|
||||||
|
Decode.list appDecoder
|
|
@ -0,0 +1,81 @@
|
||||||
|
module Shared exposing
|
||||||
|
( Flags
|
||||||
|
, Model
|
||||||
|
, Msg
|
||||||
|
, init
|
||||||
|
, subscriptions
|
||||||
|
, update
|
||||||
|
, view
|
||||||
|
)
|
||||||
|
|
||||||
|
import Browser.Navigation exposing (Key)
|
||||||
|
import Html exposing (..)
|
||||||
|
import Html.Attributes exposing (class, href)
|
||||||
|
import Spa.Document exposing (Document)
|
||||||
|
import Spa.Generated.Route as Route
|
||||||
|
import Url exposing (Url)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- INIT
|
||||||
|
|
||||||
|
|
||||||
|
type alias Flags =
|
||||||
|
()
|
||||||
|
|
||||||
|
|
||||||
|
type alias Model =
|
||||||
|
{ url : Url
|
||||||
|
, key : Key
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init : Flags -> Url -> Key -> ( Model, Cmd Msg )
|
||||||
|
init flags url key =
|
||||||
|
( Model url key
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- UPDATE
|
||||||
|
|
||||||
|
|
||||||
|
type Msg
|
||||||
|
= ReplaceMe
|
||||||
|
|
||||||
|
|
||||||
|
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||||
|
update msg model =
|
||||||
|
case msg of
|
||||||
|
ReplaceMe ->
|
||||||
|
( model, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
|
subscriptions : Model -> Sub Msg
|
||||||
|
subscriptions model =
|
||||||
|
Sub.none
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
|
view :
|
||||||
|
{ page : Document msg, toMsg : Msg -> msg }
|
||||||
|
-> Model
|
||||||
|
-> Document msg
|
||||||
|
view { page, toMsg } model =
|
||||||
|
{ title = page.title
|
||||||
|
, body =
|
||||||
|
[ div []
|
||||||
|
[ nav [ class "navbar navbar-expand-lg navbar-dark bg-dark" ]
|
||||||
|
[ a [ class "navbar-brand", href (Route.toString Route.Top) ] [ text "abra apps" ]
|
||||||
|
, ul [ class "navbar-nav" ]
|
||||||
|
[ li [ class "nav-tem" ] [ a [ class "nav-link", href (Route.toString Route.About) ] [ text "about" ] ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, div [ class "container-fluid" ] page.body
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
module Spa.Document exposing
|
||||||
|
( Document
|
||||||
|
, map
|
||||||
|
, toBrowserDocument
|
||||||
|
)
|
||||||
|
|
||||||
|
import Browser
|
||||||
|
import Html exposing (Html)
|
||||||
|
|
||||||
|
|
||||||
|
type alias Document msg =
|
||||||
|
{ title : String
|
||||||
|
, body : List (Html msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
map : (msg1 -> msg2) -> Document msg1 -> Document msg2
|
||||||
|
map fn doc =
|
||||||
|
{ title = doc.title
|
||||||
|
, body = List.map (Html.map fn) doc.body
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
toBrowserDocument : Document msg -> Browser.Document msg
|
||||||
|
toBrowserDocument doc =
|
||||||
|
{ title = doc.title
|
||||||
|
, body = doc.body
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
module Spa.Page exposing
|
||||||
|
( Page
|
||||||
|
, static, sandbox, element, application
|
||||||
|
)
|
||||||
|
|
||||||
|
{-|
|
||||||
|
|
||||||
|
@docs Page
|
||||||
|
@docs static, sandbox, element, application
|
||||||
|
@docs Upgraded, Bundle, upgrade
|
||||||
|
|
||||||
|
-}
|
||||||
|
|
||||||
|
import Shared
|
||||||
|
import Spa.Document exposing (Document)
|
||||||
|
import Spa.Url exposing (Url)
|
||||||
|
|
||||||
|
|
||||||
|
type alias Page params model msg =
|
||||||
|
{ init : Shared.Model -> Url params -> ( model, Cmd msg )
|
||||||
|
, update : msg -> model -> ( model, Cmd msg )
|
||||||
|
, view : model -> Document msg
|
||||||
|
, subscriptions : model -> Sub msg
|
||||||
|
, save : model -> Shared.Model -> Shared.Model
|
||||||
|
, load : Shared.Model -> model -> ( model, Cmd msg )
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static :
|
||||||
|
{ view : Url params -> Document msg
|
||||||
|
}
|
||||||
|
-> Page params (Url params) msg
|
||||||
|
static page =
|
||||||
|
{ init = \_ url -> ( url, Cmd.none )
|
||||||
|
, update = \_ model -> ( model, Cmd.none )
|
||||||
|
, view = page.view
|
||||||
|
, subscriptions = \_ -> Sub.none
|
||||||
|
, save = always identity
|
||||||
|
, load = always (identity >> ignoreEffect)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sandbox :
|
||||||
|
{ init : Url params -> model
|
||||||
|
, update : msg -> model -> model
|
||||||
|
, view : model -> Document msg
|
||||||
|
}
|
||||||
|
-> Page params model msg
|
||||||
|
sandbox page =
|
||||||
|
{ init = \_ url -> ( page.init url, Cmd.none )
|
||||||
|
, update = \msg model -> ( page.update msg model, Cmd.none )
|
||||||
|
, view = page.view
|
||||||
|
, subscriptions = \_ -> Sub.none
|
||||||
|
, save = always identity
|
||||||
|
, load = always (identity >> ignoreEffect)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
element :
|
||||||
|
{ init : Url params -> ( model, Cmd msg )
|
||||||
|
, update : msg -> model -> ( model, Cmd msg )
|
||||||
|
, view : model -> Document msg
|
||||||
|
, subscriptions : model -> Sub msg
|
||||||
|
}
|
||||||
|
-> Page params model msg
|
||||||
|
element page =
|
||||||
|
{ init = \_ params -> page.init params
|
||||||
|
, update = \msg model -> page.update msg model
|
||||||
|
, view = page.view
|
||||||
|
, subscriptions = page.subscriptions
|
||||||
|
, save = always identity
|
||||||
|
, load = always (identity >> ignoreEffect)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
application :
|
||||||
|
{ init : Shared.Model -> Url params -> ( model, Cmd msg )
|
||||||
|
, update : msg -> model -> ( model, Cmd msg )
|
||||||
|
, view : model -> Document msg
|
||||||
|
, subscriptions : model -> Sub msg
|
||||||
|
, save : model -> Shared.Model -> Shared.Model
|
||||||
|
, load : Shared.Model -> model -> ( model, Cmd msg )
|
||||||
|
}
|
||||||
|
-> Page params model msg
|
||||||
|
application page =
|
||||||
|
page
|
||||||
|
|
||||||
|
|
||||||
|
ignoreEffect : model -> ( model, Cmd msg )
|
||||||
|
ignoreEffect model =
|
||||||
|
( model, Cmd.none )
|
|
@ -0,0 +1,53 @@
|
||||||
|
module Spa.Url exposing (Url, create)
|
||||||
|
|
||||||
|
import Browser.Navigation exposing (Key)
|
||||||
|
import Dict exposing (Dict)
|
||||||
|
import Url
|
||||||
|
|
||||||
|
|
||||||
|
type alias Url params =
|
||||||
|
{ key : Key
|
||||||
|
, params : params
|
||||||
|
, query : Dict String String
|
||||||
|
, rawUrl : Url.Url
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
create : params -> Key -> Url.Url -> Url params
|
||||||
|
create params key url =
|
||||||
|
{ key = key
|
||||||
|
, params = params
|
||||||
|
, rawUrl = url
|
||||||
|
, query =
|
||||||
|
url.query
|
||||||
|
|> Maybe.map toQueryDict
|
||||||
|
|> Maybe.withDefault Dict.empty
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
toQueryDict : String -> Dict String String
|
||||||
|
toQueryDict queryString =
|
||||||
|
let
|
||||||
|
second : List a -> Maybe a
|
||||||
|
second =
|
||||||
|
List.drop 1 >> List.head
|
||||||
|
|
||||||
|
toTuple : List String -> Maybe ( String, String )
|
||||||
|
toTuple list =
|
||||||
|
Maybe.map
|
||||||
|
(\first ->
|
||||||
|
( first
|
||||||
|
, second list |> Maybe.withDefault ""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(List.head list)
|
||||||
|
|
||||||
|
decode =
|
||||||
|
Url.percentDecode >> Maybe.withDefault ""
|
||||||
|
in
|
||||||
|
queryString
|
||||||
|
|> String.split "&"
|
||||||
|
|> List.map (String.split "=")
|
||||||
|
|> List.filterMap toTuple
|
||||||
|
|> List.map (Tuple.mapBoth decode decode)
|
||||||
|
|> Dict.fromList
|
Loading…
Reference in New Issue