reorganize docs site, simplify type alias with 6.0.8--beta

This commit is contained in:
Ryan Haskell-Glatz 2021-02-21 15:38:21 -06:00
parent 19df55bd85
commit c2c77e9abf
52 changed files with 351 additions and 7588 deletions

View File

@ -1,4 +1,4 @@
# [![elm-spa](https://v6-elm-spa.netlify.app/images/outlined-to-edge.png)](https://elm-spa.dev) # [![elm-spa](https://elm-spa.dev/images/outlined-to-edge.png)](https://elm-spa.dev)
# **Installation** # **Installation**

View File

@ -2,6 +2,13 @@
publish = "public" publish = "public"
command = "npm i elm-spa@beta && node scripts/generate-index.js && npx elm-spa build" command = "npm i elm-spa@beta && node scripts/generate-index.js && npx elm-spa build"
# Prevents missing markdown files from redirecting to index.html
[[redirects]]
from = "/content/*"
to = "/content/:splat"
status = 200
force = true
[[redirects]] [[redirects]]
from = "/*" from = "/*"
to = "/index.html" to = "/index.html"

View File

@ -1,4 +1,4 @@
# Guide # Docs
Welcome to __elm-spa__, a framework for building web applications with [Elm](https://elm-lang.org)! Welcome to __elm-spa__, a framework for building web applications with [Elm](https://elm-lang.org)!
If you are new to Elm, you should check out [the official guide](https://guide.elm-lang.org), which If you are new to Elm, you should check out [the official guide](https://guide.elm-lang.org), which
@ -44,7 +44,7 @@ npx elm-spa server
So far, we've used [npx](https://www.npmjs.com/package/npx) so we don't need to install __elm-spa__ directly. If you'd like to run commands from the terminal, without the `npx` prefix, you can install __elm-spa__ like this: So far, we've used [npx](https://www.npmjs.com/package/npx) so we don't need to install __elm-spa__ directly. If you'd like to run commands from the terminal, without the `npx` prefix, you can install __elm-spa__ like this:
```terminal ```terminal
npm install -g elm-spa@next npm install -g elm-spa@latest
``` ```
You can verify the install succeeded by running `elm-spa help` from your terminal: You can verify the install succeeded by running `elm-spa help` from your terminal:
@ -61,11 +61,11 @@ elm-spa build . . . . . one-time production build
elm-spa watch . . . . . . runs build as you code elm-spa watch . . . . . . runs build as you code
elm-spa server . . . . . start a live dev server elm-spa server . . . . . start a live dev server
Visit https://next.elm-spa.dev for more! Visit https://elm-spa.dev for more!
``` ```
--- ---
__Ready for more?__ __Ready for more?__
Let's check out [the CLI](/guide/cli) to learn more about those five commands! Let's check out [the CLI](/docs/cli) to learn more about those five commands!

View File

@ -3,16 +3,16 @@
At the end of the last section, we installed the __elm-spa__ CLI using [npm](https://npmjs.org) like this: At the end of the last section, we installed the __elm-spa__ CLI using [npm](https://npmjs.org) like this:
```terminal ```terminal
npm install -g elm-spa@next npm install -g elm-spa@latest
``` ```
This gave us the ability to run a few commands: This gave us the ability to run a few commands:
1. __[`elm-spa new`](#elm-spa-new)__ - creates a new project 1. [__elm-spa new__](#elm-spa-new) - creates a new project
1. __[`elm-spa server`](#elm-spa-server)__ - runs a dev server as you code 1. [__elm-spa server__](#elm-spa-server) - runs a dev server as you code
1. __[`elm-spa watch`](#elm-spa-watch)__ - builds as you code 1. [__elm-spa watch__](#elm-spa-watch) - builds as you code
1. __[`elm-spa build`](#elm-spa-build)__ - one-time production build 1. [__elm-spa build__](#elm-spa-build) - one-time production build
1. __[`elm-spa add`](#elm-spa-add)__ - adds a page to an existing project 1. [__elm-spa add__](#elm-spa-add) - adds a page to an existing project
What do these do? This section of the guide dives into more detail on each command! What do these do? This section of the guide dives into more detail on each command!
@ -57,7 +57,7 @@ If you want the automatic compilation on change, but don't need a HTTP server, y
elm-spa watch elm-spa watch
``` ```
This will automatically generate and compile on save, but without the server. This is a great choice when combining __elm-spa__ with another tool like [Parcel](https://parceljs.org/elm.html) This will automatically generate code and compile your Elm files on save, but without the server. This is a great command to combine __elm-spa__ with another tool like [Parcel](https://parceljs.org/elm.html).
## elm-spa build ## elm-spa build

View File

@ -13,11 +13,11 @@ view =
} }
``` ```
This homepage renders the tab `title`, and a HTML `body` onto the page. This is great when you have a static page that just needs to render some elements. This homepage renders the tab `title`, and a HTML `body` onto the page. This is great when you have a static page that just needs to render some HTML.
Because the file is named `Home_.elm`, we know it's the homepage. These 8 lines of code are all we need to tell __elm-spa__ we'd like to render this when users visit the homepage. Because the file is named `Home_.elm`, we know it's the homepage. These 8 lines of code are all we need to tell __elm-spa__ we'd like to render this when users visit the homepage.
For real world applications, we'll need pages that can do more. That's where the `Page` module comes in handy. For real world applications, our pages will need to do more than print "Hello, world!". Let's upgrade!
### Upgrading "Hello World!" ### Upgrading "Hello World!"
@ -26,13 +26,12 @@ Let's start by introducing the `page` function, marking the start of our journey
```elm ```elm
module Pages.Home_ exposing (page) module Pages.Home_ exposing (page)
-- our other imports import Html
import Page exposing (Page) import Page exposing (Page)
import Request exposing (Request)
import Shared
page : page : Shared.Model -> Request -> Page
Shared.Model
-> Request Params
-> Page () Never
page shared req = page shared req =
Page.static Page.static
{ view = view { view = view
@ -46,13 +45,13 @@ view =
Here, our code hasn't changed very much- except now we have this new `page` function that: Here, our code hasn't changed very much- except now we have this new `page` function that:
1. Takes in two inputs: `Shared.Model` and `Request Params` 1. Accepts two inputs: `Shared.Model` and `Request`
2. Returns a `Page () Never` value. 2. Returns a `Page` value
3. Is exposed at the top of our `Pages.Home_` module. 3. Has been __exposed__ at the top of the file
> Without exposing `page`, __elm-spa__ will not understand how to compile your application. Make sure to _always_ expose `page` from modules within the `src/Pages` folder. > Exposing `page` from this module lets __elm-spa__ know to use it instead of the plain `view` function from before.
This new `page` will always get the latest `Shared.Model` and URL information, which means you don't have to worry about tracking that stuff yourself. This new `page` will always get the latest `Shared.Model` and a `Request` value (that contains URL information).
This is great, but there is still more that our `page` function can do other than render a view! This is great, but there is still more that our `page` function can do other than render a view!
@ -152,28 +151,50 @@ Notice how the type annotations of `init` and `update` changed to accept their i
## Page.static ## Page.static
```elm
module Pages.Example exposing (page)
```
```elm ```elm
Page.static Page.static
{ view : View Never { view = view
} }
``` ```
```elm
view : View Never
```
( video introducing concept ) ( video introducing concept )
## Page.sandbox ## Page.sandbox
```elm
module Pages.Example exposing (Model, Msg, page)
```
```elm ```elm
Page.sandbox Page.sandbox
{ init : Model { init = init
, update : Msg -> Model -> Model , update = update
, view : Model -> View Msg , view = view
} }
``` ```
```elm
init : Model
update : Msg -> Model -> Model
view : Model -> View Msg
```
( video introducing concept ) ( video introducing concept )
## Page.element ## Page.element
```elm
module Pages.Example exposing (Model, Msg, page)
```
```elm ```elm
Page.element Page.element
{ init : ( Model, Cmd Msg ) { init : ( Model, Cmd Msg )
@ -187,6 +208,10 @@ Page.element
## Page.advanced ## Page.advanced
```elm
module Pages.Example exposing (Model, Msg, page)
```
```elm ```elm
Page.advanced Page.advanced
{ init : ( Model, Effect Msg ) { init : ( Model, Effect Msg )

View File

@ -1,6 +1,6 @@
# Requests # Requests
Every URL that a user visits in your application contains useful information. When __elm-spa__ gets an updated URL, it passes that information to every [Page](/guide/pages) as a `Request` value. Every URL that a user visits in your application contains useful information. When __elm-spa__ gets an updated URL, it passes that information to every [Page](/docs/pages) as a `Request` value.
This section of the guide breaks down the [Request](https://package.elm-lang.org/packages/ryannhg/elm-spa/latest/ElmSpa-Request) type exposed by the official Elm package: This section of the guide breaks down the [Request](https://package.elm-lang.org/packages/ryannhg/elm-spa/latest/ElmSpa-Request) type exposed by the official Elm package:
@ -17,7 +17,7 @@ type alias Request params =
## URL Parameters ## URL Parameters
Every request has parameters that you can rely on. If you are on a [dynamic route](/guide/routing#dynamic-routes), you have access to that route's URL parameters: Every request has parameters that you can rely on. If you are on a [dynamic route](/docs/routing#dynamic-routes), you have access to that route's URL parameters:
URL | Params URL | Params
--- | --- --- | ---
@ -36,7 +36,7 @@ greet req =
"Hello, " ++ req.params.name ++ "!" "Hello, " ++ req.params.name ++ "!"
``` ```
__Note:__ When working with [shared state](/guide/shared-state), all requests are `Request ()`. __Note:__ When working with [shared state](/docs/shared-state), all requests are `Request ()`.
## Query Parameters ## Query Parameters

View File

@ -16,7 +16,7 @@ In this section, we'll cover the 3 kinds of routes you can find in an __elm-spa_
## The homepage ## The homepage
The `src/Pages/Home_.elm` is a reserved filename that handles requests to /. The easiest way to make a new homepage is with the [`add` command](/guide/cli#adding-a-homepage) covered in the CLI section: The `src/Pages/Home_.elm` is a reserved filename that handles requests to /. The easiest way to make a new homepage is with the [`add` command](/docs/cli#adding-a-homepage) covered in the CLI section:
```terminal ```terminal
elm-spa add / elm-spa add /
@ -78,7 +78,7 @@ The __trailing underscore__ at the end of the filename (`Name_.elm`) indicates t
The name of the route parameter variable (`name` in this example) is determined by the name of the file! If we named the file `Id_.elm`, the dynamic value would be available at `params.id` instead. The name of the route parameter variable (`name` in this example) is determined by the name of the file! If we named the file `Id_.elm`, the dynamic value would be available at `params.id` instead.
Every page gets access to these dynamic parameters, via the [`Request params`](/guide/pages#requests) value passed in. We'll cover that in the next section! Every page gets access to these dynamic parameters, via the [`Request params`](/docs/pages#requests) value passed in. We'll cover that in the next section!
### Nested dynamic routes ### Nested dynamic routes

View File

@ -1,4 +1,4 @@
# Shared State # Shared state
With __elm-spa__, every time you navigate from one page to another, the `init` function for that page is called. This means that the `Model` for the page you we're previously looking at has been cleared out. Most of the time, that's a good thing! With __elm-spa__, every time you navigate from one page to another, the `init` function for that page is called. This means that the `Model` for the page you we're previously looking at has been cleared out. Most of the time, that's a good thing!
@ -22,7 +22,7 @@ init : Flags -> Request () -> Model -> ( Model, Effect Msg )
The `init` function is called when your page loads for the first time. It takes in two inputs: The `init` function is called when your page loads for the first time. It takes in two inputs:
- `Flags` - initial JSON value passed in from `public/main.js - `Flags` - initial JSON value passed in from `public/main.js
- `Request ()` - a [Request](/guide/request) value with the current URL information - `Request ()` - a [Request](/docs/request) value with the current URL information
The `init` function returns the initial `Model`, as well as any `Effect`s you'd like to run (like initial HTTP requests, etc) The `init` function returns the initial `Model`, as well as any `Effect`s you'd like to run (like initial HTTP requests, etc)

View File

@ -1 +1,7 @@
# Examples # Examples
Here are real-world applications using __elm-spa__:
- This site
- Realworld example app
- User featured projects

View File

@ -1 +0,0 @@
# User Authentication

View File

@ -0,0 +1 @@
# Guides

View File

@ -0,0 +1 @@
# Getting started

View File

@ -0,0 +1 @@
# Page transitions

View File

@ -0,0 +1,3 @@
# Local storage
TODO

View File

@ -0,0 +1,3 @@
# User authentication
TODO

View File

@ -0,0 +1 @@
# Using Elm UI

View File

@ -0,0 +1 @@
# Testing

View File

@ -16,7 +16,7 @@
--color--green: #407742; --color--green: #407742;
--color--green-light: #d7ead8; --color--green-light: #d7ead8;
--size--h1: 4em; --size--h1: 3em;
--size--h2: 2em; --size--h2: 2em;
--size--h3: 1.5em; --size--h3: 1.5em;
--size--h4: 1.2em; --size--h4: 1.2em;
@ -64,6 +64,16 @@ pre {
background-color: white; background-color: white;
} }
.aside {
white-space: nowrap;
}
.table-of-contents {
min-width: 14em;
max-width: 14em;
white-space: nowrap;
}
main { main {
animation: fadeIn 200ms 400ms ease-in forwards; animation: fadeIn 200ms 400ms ease-in forwards;
opacity: 0; opacity: 0;
@ -163,12 +173,14 @@ hr { border: 0; }
line-height: 1.4; line-height: 1.4;
} }
.markdown p code { .markdown p code,
.markdown li code {
font-size: 0.92em;
color: var(--color--green); color: var(--color--green);
} }
.markdown p code::before { content: '`'; opacity: 0.75; pointer-events: none; user-select: none; } .markdown p code::before, .markdown li code::before { content: '`'; opacity: 0.75; pointer-events: none; user-select: none; }
.markdown p code::after { content: '`'; opacity: 0.75; pointer-events: none; user-select: none; } .markdown p code::after, .markdown li code::after { content: '`'; opacity: 0.75; pointer-events: none; user-select: none; }
.markdown blockquote { .markdown blockquote {
padding-left: 1rem; padding-left: 1rem;
@ -243,9 +255,7 @@ hr { border: 0; }
} }
.markdown th, .markdown th,
.markdown td { .markdown td { padding: 0.75em }
padding: 0.75em;
}
.markdown tbody tr:nth-child(2n + 1) { .markdown tbody tr:nth-child(2n + 1) {
background-color: var(--color--grey-100); background-color: var(--color--grey-100);

View File

@ -6,6 +6,10 @@ const config = {
output: path.join(__dirname, '..', 'public', 'dist') output: path.join(__dirname, '..', 'public', 'dist')
} }
// Terminal color output
const green = ``
const reset = ``
// Recursively lists all files in the given folder // Recursively lists all files in the given folder
const listContainedFiles = async (folder) => { const listContainedFiles = async (folder) => {
let files = [] let files = []
@ -54,7 +58,7 @@ const main = () =>
await fs.mkdir(config.output, { recursive: true }) await fs.mkdir(config.output, { recursive: true })
return fs.writeFile(path.join(config.output, 'flags.js'), contents, { encoding: 'utf-8' }) return fs.writeFile(path.join(config.output, 'flags.js'), contents, { encoding: 'utf-8' })
}) })
.then(_ => console.info(`\n ✓ Indexed the content folder\n`)) .then(_ => console.info(`\n ${green}${reset} Indexed the content folder\n`))
.catch(console.error) .catch(console.error)
// Run the program // Run the program

View File

@ -106,7 +106,8 @@ sections : Index -> List Section
sections index = sections index =
let let
sectionOrder = sectionOrder =
[ "Guide" [ "Docs"
, "Guides"
, "Examples" , "Examples"
] ]

View File

@ -145,6 +145,6 @@ subscriptions model =
-- REQUESTS -- REQUESTS
request : { model | url : Url, key : Key } -> Request () request : { model | url : Url, key : Key } -> Request
request model = request model =
Request.create () model.url model.key Request.create () model.url model.key

19
docs/src/Pages/Docs.elm Normal file
View File

@ -0,0 +1,19 @@
module Pages.Docs exposing (Model, Msg, page)
import Page
import Request
import Shared
import UI.Docs
page : Shared.Model -> Request.With params -> Page.With Model Msg
page =
UI.Docs.page
type alias Model =
UI.Docs.Model
type alias Msg =
UI.Docs.Msg

View File

@ -0,0 +1,19 @@
module Pages.Docs.Section_ exposing (Model, Msg, page)
import Page
import Request
import Shared
import UI.Docs
page : Shared.Model -> Request.With params -> Page.With Model Msg
page =
UI.Docs.page
type alias Model =
UI.Docs.Model
type alias Msg =
UI.Docs.Msg

View File

@ -1,12 +1,12 @@
module Pages.Examples exposing (Model, Msg, page) module Pages.Examples exposing (Model, Msg, page)
import Page exposing (Page) import Page
import Request exposing (Request) import Request
import Shared import Shared
import UI.Docs import UI.Docs
page : Shared.Model -> Request params -> Page Model Msg page : Shared.Model -> Request.With params -> Page.With Model Msg
page = page =
UI.Docs.page UI.Docs.page

View File

@ -1,19 +0,0 @@
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

View File

@ -1,19 +0,0 @@
module Pages.Guide 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

View File

@ -1,19 +0,0 @@
module Pages.Guide.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

19
docs/src/Pages/Guides.elm Normal file
View File

@ -0,0 +1,19 @@
module Pages.Guides exposing (Model, Msg, page)
import Page
import Request
import Shared
import UI.Docs
page : Shared.Model -> Request.With params -> Page.With Model Msg
page =
UI.Docs.page
type alias Model =
UI.Docs.Model
type alias Msg =
UI.Docs.Msg

View File

@ -0,0 +1,19 @@
module Pages.Guides.Section_ exposing (Model, Msg, page)
import Page
import Request
import Shared
import UI.Docs
page : Shared.Model -> Request.With params -> Page.With Model Msg
page =
UI.Docs.page
type alias Model =
UI.Docs.Model
type alias Msg =
UI.Docs.Msg

View File

@ -1,15 +1,15 @@
module Pages.Home_ exposing (Model, Msg, page) module Pages.Home_ exposing (Model, Msg, page)
import Gen.Params.Home_ exposing (Params) import Gen.Params.Home_ exposing (Params)
import Page exposing (Page) import Page
import Request exposing (Request) import Request
import Shared import Shared
import UI import UI
import UI.Layout import UI.Layout
import View exposing (View) import View exposing (View)
page : Shared.Model -> Request Params -> Page Model Msg page : Shared.Model -> Request.With Params -> Page.With Model Msg
page = page =
UI.Layout.page UI.Layout.page
{ view = view { view = view
@ -36,7 +36,7 @@ view =
## Build reliable applications. ## Build reliable applications.
I need to verify that the line height for paragraphs is reasonable, because if it isn't then I'll need to tweak it a bit until it's actually readable. I need to verify that the line height for paragraphs is reasonable, because if it isn't then I'll need to tweak it a bit until it's actually readable.
Only the most readable lines should be included in the __official__ [guide](/guide), ya dig? Only the most readable lines should be included in the __official__ [guide](/docs), ya dig?
Bippity boppity, my guy. Bippity boppity, my guy.

View File

@ -1,15 +1,15 @@
module Pages.NotFound exposing (Model, Msg, page) module Pages.NotFound exposing (Model, Msg, page)
import Gen.Params.NotFound exposing (Params) import Gen.Params.NotFound exposing (Params)
import Page exposing (Page) import Page
import Request exposing (Request) import Request
import Shared import Shared
import UI import UI
import UI.Layout import UI.Layout
import View exposing (View) import View exposing (View)
page : Shared.Model -> Request Params -> Page Model Msg page : Shared.Model -> Request.With Params -> Page.With Model Msg
page = page =
UI.Layout.page UI.Layout.page
{ view = view { view = view

View File

@ -36,7 +36,7 @@ type Msg
-- INIT -- INIT
init : Request () -> Flags -> ( Model, Cmd Msg ) init : Request -> Flags -> ( Model, Cmd Msg )
init _ flags = init _ flags =
( Model ( Model
(flags (flags
@ -51,7 +51,7 @@ init _ flags =
-- UPDATE -- UPDATE
update : Request () -> Msg -> Model -> ( Model, Cmd Msg ) update : Request -> Msg -> Model -> ( Model, Cmd Msg )
update request msg model = update request msg model =
case msg of case msg of
NoOp -> NoOp ->
@ -62,6 +62,6 @@ update request msg model =
-- SUBSCRIPTIONS -- SUBSCRIPTIONS
subscriptions : Request () -> Model -> Sub Msg subscriptions : Request -> Model -> Sub Msg
subscriptions request model = subscriptions request model =
Sub.none Sub.none

View File

@ -1,8 +1,8 @@
module UI.Docs exposing (Model, Msg, page) module UI.Docs exposing (Model, Msg, page)
import Http import Http
import Page exposing (Page) import Page
import Request exposing (Request) import Request
import Shared import Shared
import UI import UI
import UI.Layout import UI.Layout
@ -10,7 +10,7 @@ import Url exposing (Url)
import View exposing (View) import View exposing (View)
page : Shared.Model -> Request params -> Page Model Msg page : Shared.Model -> Request.With params -> Page.With Model Msg
page shared req = page shared req =
Page.element Page.element
{ init = init req.url { init = init req.url

View File

@ -13,6 +13,7 @@ module UI.Layout exposing
-} -}
import Gen.Route as Route exposing (Route)
import Html exposing (Html) import Html exposing (Html)
import Html.Attributes as Attr import Html.Attributes as Attr
import Page exposing (Page) import Page exposing (Page)
@ -72,9 +73,9 @@ viewDocumentation :
-> List (Html msg) -> List (Html msg)
viewDocumentation options markdownContent view = viewDocumentation options markdownContent view =
[ navbar options [ navbar options
, Html.div [ Attr.class "container pad-lg" ] , Html.div [ Attr.class "container pad-md" ]
[ UI.row.lg [ UI.align.top, UI.padY.lg ] [ UI.row.xl [ UI.align.top, UI.padY.lg ]
[ Html.aside [ Attr.class "only-desktop sticky pad-y-lg", Attr.style "width" "13em" ] [ Html.aside [ Attr.class "only-desktop sticky pad-y-lg aside" ]
[ UI.Sidebar.viewSidebar [ UI.Sidebar.viewSidebar
{ index = options.shared.index { index = options.shared.index
, url = options.url , url = options.url
@ -83,7 +84,7 @@ viewDocumentation options markdownContent view =
, Html.main_ [ Attr.class "flex" ] , Html.main_ [ Attr.class "flex" ]
[ UI.row.lg [ UI.align.top ] [ UI.row.lg [ UI.align.top ]
[ Html.div [ Attr.class "col flex" ] view [ Html.div [ Attr.class "col flex" ] view
, Html.div [ Attr.class "hidden-mobile sticky pad-y-lg", Attr.style "width" "16em" ] , Html.div [ Attr.class "hidden-mobile sticky pad-y-lg table-of-contents" ]
[ UI.Sidebar.viewTableOfContents [ UI.Sidebar.viewTableOfContents
{ content = markdownContent { content = markdownContent
, url = options.url , url = options.url
@ -105,12 +106,12 @@ navbar :
-> Html msg -> Html msg
navbar { onMsg, model, shared, url } = navbar { onMsg, model, shared, url } =
let let
navLink : { text : String, url : String } -> Html msg navLink : { text : String, route : Route } -> Html msg
navLink options = navLink options =
Html.a Html.a
[ Attr.class "link" [ Attr.class "link"
, Attr.href options.url , Attr.href (Route.toHref options.route)
, Attr.classList [ ( "bold text-blue", String.startsWith options.url url.path ) ] , Attr.classList [ ( "bold text-blue", String.startsWith (Route.toHref options.route) url.path ) ]
] ]
[ Html.text options.text ] [ Html.text options.text ]
in in
@ -119,9 +120,9 @@ navbar { onMsg, model, shared, url } =
[ Html.div [ Attr.class "row align-center gap-md" ] [ Html.div [ Attr.class "row align-center gap-md" ]
[ Html.a [ Attr.href "/" ] [ UI.logo ] [ Html.a [ Attr.href "/" ] [ UI.logo ]
, Html.nav [ Attr.class "row gap-md hidden-mobile pad-left-xs" ] , Html.nav [ Attr.class "row gap-md hidden-mobile pad-left-xs" ]
[ navLink { text = "guide", url = "/guide" } [ navLink { text = "docs", route = Route.Docs }
, navLink { text = "docs", url = "/docs" } , navLink { text = "guides ", route = Route.Guides }
, navLink { text = "examples", url = "/examples" } , navLink { text = "examples", route = Route.Examples }
] ]
] ]
, Html.div [ Attr.class "row gap-md spread" ] , Html.div [ Attr.class "row gap-md spread" ]
@ -144,7 +145,7 @@ navbar { onMsg, model, shared, url } =
-- PAGE -- PAGE
page : { view : View Msg } -> Shared.Model -> Request params -> Page Model Msg page : { view : View Msg } -> Shared.Model -> Request.With params -> Page.With Model Msg
page options shared req = page options shared req =
Page.sandbox Page.sandbox
{ init = init { init = init

View File

@ -6,7 +6,7 @@
This project requires the latest LTS version of [Node.js](https://nodejs.org/) This project requires the latest LTS version of [Node.js](https://nodejs.org/)
```bash ```bash
npm install -g elm elm-spa npm install -g elm-spa@latest
``` ```
## running locally ## running locally

View File

@ -9,7 +9,7 @@ module ElmSpa.Page exposing
# **( These docs are for CLI contributors )** # **( These docs are for CLI contributors )**
### If you are using **elm-spa**, check out [the official guide](https://v6.elm-spa.dev/guide) instead! ### If you are using **elm-spa**, check out [the official guide](https://elm-spa.dev/guide) instead!
--- ---

View File

@ -6,7 +6,7 @@ module ElmSpa.Request exposing (Request, create)
# **( These docs are for CLI contributors )** # **( These docs are for CLI contributors )**
### If you are using **elm-spa**, check out [the official guide](https://v6.elm-spa.dev/guide) instead! ### If you are using **elm-spa**, check out [the official guide](https://elm-spa.dev/guide) instead!
--- ---

7373
src/cli/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "elm-spa", "name": "elm-spa",
"version": "6.0.7--beta", "version": "6.0.8--beta",
"description": "single page apps made easy", "description": "single page apps made easy",
"bin": "dist/src/index.js", "bin": "dist/src/index.js",
"scripts": { "scripts": {
@ -45,4 +45,4 @@
"terser": "5.3.8", "terser": "5.3.8",
"websocket": "1.0.32" "websocket": "1.0.32"
} }
} }

View File

@ -19,5 +19,5 @@ ${bold(`elm-spa ${cyan(`build`)}`)} . . . . . . one-time production build
${bold(`elm-spa ${cyan(`watch`)}`)} . . . . . . . runs build as you code ${bold(`elm-spa ${cyan(`watch`)}`)} . . . . . . . runs build as you code
${bold(`elm-spa ${cyan(`server`)}`)} . . . . . . start a live dev server ${bold(`elm-spa ${cyan(`server`)}`)} . . . . . . start a live dev server
Visit ${green(`https://next.elm-spa.dev`)} for more! Visit ${green(`https://elm-spa.dev`)} for more!
` `

View File

@ -1,13 +1,14 @@
module Page exposing module Page exposing
( Page ( Page, With
, static, sandbox, element, advanced , static, sandbox, element, advanced
, protected , protected
) )
{-| {-|
@docs Page @docs Page, With
@docs static, sandbox, element, advanced @docs static, sandbox, element, advanced
@docs protected
-} -}
@ -20,6 +21,58 @@ import View exposing (View)
-- PAGES
type alias Page =
With () Never
type alias With model msg =
ElmSpa.Page Shared.Model Route (Effect msg) (View msg) model msg
static :
{ view : View Never
}
-> Page
static =
ElmSpa.static Effect.none
sandbox :
{ init : model
, update : msg -> model -> model
, view : model -> View msg
}
-> With model msg
sandbox =
ElmSpa.sandbox Effect.none
element :
{ init : ( model, Cmd msg )
, update : msg -> model -> ( model, Cmd msg )
, view : model -> View msg
, subscriptions : model -> Sub msg
}
-> With model msg
element =
ElmSpa.element Effect.fromCmd
advanced :
{ init : ( model, Effect msg )
, update : msg -> model -> ( model, Effect msg )
, view : model -> View msg
, subscriptions : model -> Sub msg
}
-> With model msg
advanced =
ElmSpa.advanced
-- PROTECTED PAGES -- PROTECTED PAGES
@ -41,84 +94,36 @@ Here, you can provide logic on where to redirect if a user is not signed in. Her
ElmSpa.RedirectTo Gen.Route.SignIn ElmSpa.RedirectTo Gen.Route.SignIn
-} -}
beforeProtectedInit : Shared.Model -> Request () -> ElmSpa.Protected User Route beforeProtectedInit : Shared.Model -> Request -> ElmSpa.Protected User Route
beforeProtectedInit shared req = beforeProtectedInit shared req =
ElmSpa.RedirectTo Gen.Route.NotFound ElmSpa.RedirectTo Gen.Route.NotFound
-- PAGES
type alias Page model msg =
ElmSpa.Page Shared.Model Route (Effect msg) (View msg) model msg
static :
{ view : View Never
}
-> Page () Never
static =
ElmSpa.static Effect.none
sandbox :
{ init : model
, update : msg -> model -> model
, view : model -> View msg
}
-> Page model msg
sandbox =
ElmSpa.sandbox Effect.none
element :
{ init : ( model, Cmd msg )
, update : msg -> model -> ( model, Cmd msg )
, view : model -> View msg
, subscriptions : model -> Sub msg
}
-> Page model msg
element =
ElmSpa.element Effect.fromCmd
advanced :
{ init : ( model, Effect msg )
, update : msg -> model -> ( model, Effect msg )
, view : model -> View msg
, subscriptions : model -> Sub msg
}
-> Page model msg
advanced =
ElmSpa.advanced
protected : protected :
{ static : { static :
{ view : User -> View msg { view : User -> View msg
} }
-> Page () msg -> With () msg
, sandbox : , sandbox :
{ init : User -> model { init : User -> model
, update : User -> msg -> model -> model , update : User -> msg -> model -> model
, view : User -> model -> View msg , view : User -> model -> View msg
} }
-> Page model msg -> With model msg
, element : , element :
{ init : User -> ( model, Cmd msg ) { init : User -> ( model, Cmd msg )
, update : User -> msg -> model -> ( model, Cmd msg ) , update : User -> msg -> model -> ( model, Cmd msg )
, view : User -> model -> View msg , view : User -> model -> View msg
, subscriptions : User -> model -> Sub msg , subscriptions : User -> model -> Sub msg
} }
-> Page model msg -> With model msg
, advanced : , advanced :
{ init : User -> ( model, Effect msg ) { init : User -> ( model, Effect msg )
, update : User -> msg -> model -> ( model, Effect msg ) , update : User -> msg -> model -> ( model, Effect msg )
, view : User -> model -> View msg , view : User -> model -> View msg
, subscriptions : User -> model -> Sub msg , subscriptions : User -> model -> Sub msg
} }
-> Page model msg -> With model msg
} }
protected = protected =
ElmSpa.protected2 ElmSpa.protected2

View File

@ -3,6 +3,6 @@ module Pages.NotFound exposing (view)
import View exposing (View) import View exposing (View)
view : View Never view : View msg
view = view =
View.placeholder "Page not found." View.placeholder "Page not found."

View File

@ -1,4 +1,16 @@
module Request exposing (Request, create, pushRoute, replaceRoute) module Request exposing
( Request, With
, create
, pushRoute, replaceRoute
)
{-|
@docs Request, With
@docs create
@docs pushRoute, replaceRoute
-}
import Browser.Navigation exposing (Key) import Browser.Navigation exposing (Key)
import ElmSpa.Request as ElmSpa import ElmSpa.Request as ElmSpa
@ -6,20 +18,24 @@ import Gen.Route as Route exposing (Route)
import Url exposing (Url) import Url exposing (Url)
type alias Request params = type alias Request =
With ()
type alias With params =
ElmSpa.Request Route params ElmSpa.Request Route params
create : params -> Url -> Key -> Request params create : params -> Url -> Key -> With params
create params url key = create params url key =
ElmSpa.create (Route.fromUrl url) params url key ElmSpa.create (Route.fromUrl url) params url key
pushRoute : Route -> Request params -> Cmd msg pushRoute : Route -> With params -> Cmd msg
pushRoute route req = pushRoute route req =
Browser.Navigation.pushUrl req.key (Route.toHref route) Browser.Navigation.pushUrl req.key (Route.toHref route)
replaceRoute : Route -> Request params -> Cmd msg replaceRoute : Route -> With params -> Cmd msg
replaceRoute route req = replaceRoute route req =
Browser.Navigation.replaceUrl req.key (Route.toHref route) Browser.Navigation.replaceUrl req.key (Route.toHref route)

View File

@ -23,18 +23,18 @@ type Msg
= NoOp = NoOp
init : Request () -> Flags -> ( Model, Cmd Msg ) init : Request -> Flags -> ( Model, Cmd Msg )
init _ _ = init _ _ =
( {}, Cmd.none ) ( {}, Cmd.none )
update : Request () -> Msg -> Model -> ( Model, Cmd Msg ) update : Request -> Msg -> Model -> ( Model, Cmd Msg )
update _ msg model = update _ msg model =
case msg of case msg of
NoOp -> NoOp ->
( model, Cmd.none ) ( model, Cmd.none )
subscriptions : Request () -> Model -> Sub Msg subscriptions : Request -> Model -> Sub Msg
subscriptions _ _ = subscriptions _ _ =
Sub.none Sub.none

View File

@ -4,7 +4,7 @@ import Html
import View exposing (View) import View exposing (View)
view : View Never view : View msg
view = view =
{ title = "Homepage" { title = "Homepage"
, body = [ Html.text "Hello, world!" ] , body = [ Html.text "Hello, world!" ]

View File

@ -1,10 +1,10 @@
export default (page : string[]) : string => ` export default (page: string[]): string => `
module Pages.${page.join('.')} exposing (view) module Pages.${page.join('.')} exposing (view)
import View exposing (View) import View exposing (View)
view : View Never view : View msg
view = view =
View.placeholder "${page.join('.')}" View.placeholder "${page.join('.')}"

View File

@ -2,13 +2,14 @@ module Pages.{{module}} exposing (Model, Msg, page)
import Effect exposing (Effect) import Effect exposing (Effect)
import Gen.Params.{{module}} exposing (Params) import Gen.Params.{{module}} exposing (Params)
import Page exposing (Page) import Page
import Request exposing (Request) import Request
import Shared import Shared
import View exposing (View) import View exposing (View)
import Page
page : Shared.Model -> Request Params -> Page Model Msg page : Shared.Model -> Request.With Params -> Page.With Model Msg
page shared req = page shared req =
Page.advanced Page.advanced
{ init = init { init = init

View File

@ -1,13 +1,13 @@
module Pages.{{module}} exposing (Model, Msg, page) module Pages.{{module}} exposing (Model, Msg, page)
import Gen.Params.{{module}} exposing (Params) import Gen.Params.{{module}} exposing (Params)
import Page exposing (Page) import Page
import Request exposing (Request) import Request
import Shared import Shared
import View exposing (View) import View exposing (View)
page : Shared.Model -> Request Params -> Page Model Msg page : Shared.Model -> Request.With Params -> Page.With Model Msg
page shared req = page shared req =
Page.element Page.element
{ init = init { init = init

View File

@ -1,13 +1,13 @@
module Pages.{{module}} exposing (Model, Msg, page) module Pages.{{module}} exposing (Model, Msg, page)
import Gen.Params.{{module}} exposing (Params) import Gen.Params.{{module}} exposing (Params)
import Page exposing (Page) import Page
import Request exposing (Request) import Request
import Shared import Shared
import View exposing (View) import View exposing (View)
page : Shared.Model -> Request Params -> Page Model Msg page : Shared.Model -> Request.With Params -> Page.With Model Msg
page shared req = page shared req =
Page.sandbox Page.sandbox
{ init = init { init = init

View File

@ -2,18 +2,18 @@ module Pages.{{module}} exposing (page)
import Gen.Params.{{module}} exposing (Params) import Gen.Params.{{module}} exposing (Params)
import Page exposing (Page) import Page exposing (Page)
import Request exposing (Request) import Request
import Shared import Shared
import View exposing (View) import View exposing (View)
page : Shared.Model -> Request Params -> Page () Never page : Shared.Model -> Request.With Params -> Page
page shared req = page shared req =
Page.static Page.static
{ view = view { view = view
} }
view : View Never view : View msg
view = view =
View.placeholder "{{module}}" View.placeholder "{{module}}"

View File

@ -1,6 +1,31 @@
import config from "../config"
import { routeTypeDefinition, indent, routeParserList, paramsImports, Options, routeToHref } from "./utils" import { routeTypeDefinition, indent, routeParserList, paramsImports, Options, routeToHref } from "./utils"
export default (pages : string[][], _options : Options) : string => ` const routeParserOrder = (pages: string[][]) =>
[...pages].sort(sorter)
const isHomepage = (list: string[]) => list.join('.') === config.reserved.homepage
const isDynamic = (piece: string) => piece.endsWith('_')
const alphaSorter = (a: string, b: string) => a < b ? -1 : b < a ? 1 : 0
const sorter = (a: string[], b: string[]): (-1 | 1 | 0) => {
if (isHomepage(a)) return -1
if (isHomepage(b)) return 1
if (a.length < b.length) return -1
if (a.length > b.length) return 1
for (let i in a) {
const [isA, isB] = [isDynamic(a[i]), isDynamic(b[i])]
if (isA && isB) return alphaSorter(a[i], b[i])
if (isA) return 1
if (isB) return -1
}
return 0
}
export default (pages: string[][], _options: Options): string => `
module Gen.Route exposing module Gen.Route exposing
( Route(..) ( Route(..)
, fromUrl , fromUrl
@ -22,7 +47,7 @@ fromUrl =
routes : List (Parser (Route -> a) a) routes : List (Parser (Route -> a) a)
routes = routes =
${indent(routeParserList(pages), 1)} ${indent(routeParserList(routeParserOrder(pages)), 1)}
toHref : Route -> String toHref : Route -> String