its not perfect yet, but im super happy. thx hayley williams for getting me through this absolute mind-🤬

This commit is contained in:
Ryan Haskell-Glatz 2019-11-18 20:30:37 -06:00
parent 92be461f6d
commit c77993f0d2
25 changed files with 348 additions and 59 deletions

View File

@ -231,16 +231,29 @@ type Item
| DynamicFolder Filepath
routeCaseTemplate : Item -> String
routeCaseTemplate item =
case item of
StaticFile filepath ->
staticRouteCase : String -> String
staticRouteCase word =
case word of
"Top" ->
"""
Top _ ->
"/"
"""
last_ ->
"""
{{name}} _ ->
"/{{slug}}"
"""
|> String.replace "{{name}}" (last filepath)
|> String.replace "{{slug}}" (sluggify (last filepath))
|> String.replace "{{name}}" last_
|> String.replace "{{slug}}" (sluggify last_)
routeCaseTemplate : Item -> String
routeCaseTemplate item =
case item of
StaticFile filepath ->
staticRouteCase (last filepath)
DynamicFile _ ->
"""
@ -321,6 +334,7 @@ module {{pagesModuleName}} exposing
)
import App.Page
import App.Pattern exposing (static, dynamic)
import {{layoutModuleName}} as Layout
import Utils.Spa as Spa
import {{paramsModuleName}} as Params
@ -339,7 +353,9 @@ import {{routeModuleName}} as Route exposing (Route)
page : Spa.Page Route Model Msg layoutModel layoutMsg appMsg
page =
Spa.layout
{ view = Layout.view
{ pattern = {{pagesLayoutPattern}}
, transition = Layout.transition
, view = Layout.view
, recipe =
{ init = init
, update = update
@ -400,6 +416,7 @@ bundle bigModel =
|> String.replace "{{pagesFolderPagesImports}}" (pagesFolderImports "Pages" details.folders)
|> String.replace "{{pagesModelTypes}}" (pagesCustomTypes "Model" details)
|> String.replace "{{pagesMsgTypes}}" (pagesCustomTypes "Msg" details)
|> String.replace "{{pagesLayoutPattern}}" (pagesLayoutPattern details)
|> String.replace "{{pagesRecipesTypeAliases}}" (pagesRecipesTypeAliases details)
|> String.replace "{{pagesRecipesFunctions}}" (pagesRecipesFunctions details)
|> String.replace "{{pagesInitFunction}}" (pagesInitFunction details)
@ -451,6 +468,27 @@ pagesFolderImports suffix folders =
|> asImports
pagesLayoutPattern : Details -> String
pagesLayoutPattern { moduleName } =
String.split "." moduleName
|> List.map
(\piece ->
case piece of
"Dynamic" ->
"dynamic"
_ ->
"static \"" ++ sluggify piece ++ "\""
)
|> (\pieces ->
if List.isEmpty pieces || pieces == [ "static \"\"" ] then
"[]"
else
"[ " ++ String.join ", " pieces ++ " ]"
)
pagesCustomTypes : String -> Details -> String
pagesCustomTypes type_ { files, folders } =
let

View File

@ -1,3 +1,4 @@
#!/usr/bin/env node
const path = require('path')
const cwd = process.cwd()
const { File, Elm, bold } = require('./utils.js')

View File

@ -5,6 +5,7 @@ module Generated.Docs.Pages exposing
)
import App.Page
import App.Pattern exposing (static, dynamic)
import Layouts.Docs as Layout
import Utils.Spa as Spa
import Generated.Docs.Params as Params
@ -28,7 +29,9 @@ type Msg
page : Spa.Page Route Model Msg layoutModel layoutMsg appMsg
page =
Spa.layout
{ view = Layout.view
{ pattern = [ static "docs" ]
, transition = Layout.transition
, view = Layout.view
, recipe =
{ init = init
, update = update

View File

@ -5,6 +5,7 @@ module Generated.Guide.Dynamic.Dynamic.Pages exposing
)
import App.Page
import App.Pattern exposing (static, dynamic)
import Layouts.Guide.Dynamic.Dynamic as Layout
import Utils.Spa as Spa
import Generated.Guide.Dynamic.Dynamic.Params as Params
@ -25,7 +26,9 @@ type Msg
page : Spa.Page Route Model Msg layoutModel layoutMsg appMsg
page =
Spa.layout
{ view = Layout.view
{ pattern = [ static "guide", dynamic, dynamic ]
, transition = Layout.transition
, view = Layout.view
, recipe =
{ init = init
, update = update

View File

@ -14,4 +14,4 @@ toPath : Route -> String
toPath route =
case route of
Top _ ->
"/top"
"/"

View File

@ -5,6 +5,7 @@ module Generated.Guide.Dynamic.Faq.Pages exposing
)
import App.Page
import App.Pattern exposing (static, dynamic)
import Layouts.Guide.Dynamic.Faq as Layout
import Utils.Spa as Spa
import Generated.Guide.Dynamic.Faq.Params as Params
@ -25,7 +26,9 @@ type Msg
page : Spa.Page Route Model Msg layoutModel layoutMsg appMsg
page =
Spa.layout
{ view = Layout.view
{ pattern = [ static "guide", dynamic, static "faq" ]
, transition = Layout.transition
, view = Layout.view
, recipe =
{ init = init
, update = update

View File

@ -14,4 +14,4 @@ toPath : Route -> String
toPath route =
case route of
Top _ ->
"/top"
"/"

View File

@ -5,6 +5,7 @@ module Generated.Guide.Dynamic.Pages exposing
)
import App.Page
import App.Pattern exposing (static, dynamic)
import Layouts.Guide.Dynamic as Layout
import Utils.Spa as Spa
import Generated.Guide.Dynamic.Params as Params
@ -34,7 +35,9 @@ type Msg
page : Spa.Page Route Model Msg layoutModel layoutMsg appMsg
page =
Spa.layout
{ view = Layout.view
{ pattern = [ static "guide", dynamic ]
, transition = Layout.transition
, view = Layout.view
, recipe =
{ init = init
, update = update

View File

@ -5,6 +5,7 @@ module Generated.Guide.Pages exposing
)
import App.Page
import App.Pattern exposing (static, dynamic)
import Layouts.Guide as Layout
import Utils.Spa as Spa
import Generated.Guide.Params as Params
@ -33,7 +34,9 @@ type Msg
page : Spa.Page Route Model Msg layoutModel layoutMsg appMsg
page =
Spa.layout
{ view = Layout.view
{ pattern = [ static "guide" ]
, transition = Layout.transition
, view = Layout.view
, recipe =
{ init = init
, update = update

View File

@ -5,6 +5,7 @@ module Generated.Pages exposing
)
import App.Page
import App.Pattern exposing (static, dynamic)
import Layout as Layout
import Utils.Spa as Spa
import Generated.Params as Params
@ -43,7 +44,9 @@ type Msg
page : Spa.Page Route Model Msg layoutModel layoutMsg appMsg
page =
Spa.layout
{ view = Layout.view
{ pattern = []
, transition = Layout.transition
, view = Layout.view
, recipe =
{ init = init
, update = update

View File

@ -38,7 +38,7 @@ toPath route =
Top _ ->
"/top"
"/"
Docs_Folder subRoute ->

View File

@ -1,6 +1,7 @@
module Layout exposing (view)
module Layout exposing (transition, view)
import App.Page
import App.Transition as Transition exposing (Transition)
import Components.Button
import Components.Styles as Styles
import Element exposing (..)
@ -19,6 +20,11 @@ type alias Context msg =
}
transition : Transition (Element msg)
transition =
Transition.fadeUi 300
view : Context msg -> Element msg
view { page, global, toMsg } =
column
@ -93,14 +99,3 @@ viewButtonLink ( label, url ) =
{ url = url
, label = text label
}
transition :
{ property : String, speed : Int }
-> Element.Attribute msg
transition { property, speed } =
Element.htmlAttribute
(Attr.style
"transition"
(property ++ " " ++ String.fromInt speed ++ "ms ease-in-out")
)

View File

@ -1,5 +1,6 @@
module Layouts.Docs exposing (view)
module Layouts.Docs exposing (transition, view)
import App.Transition as Transition exposing (Transition)
import Element exposing (..)
import Global
@ -11,6 +12,17 @@ type alias Context msg =
}
transition : Transition (Element msg)
transition =
Transition.fadeUi 200
view : Context msg -> Element msg
view { page } =
page
column [ width fill ]
[ row [ spacing 16 ]
[ link [] { label = text "apples", url = "/docs/apples" }
, link [] { label = text "bananas", url = "/docs/bananas" }
]
, page
]

View File

@ -1,5 +1,6 @@
module Layouts.Guide exposing (view)
module Layouts.Guide exposing (transition, view)
import App.Transition as Transition exposing (Transition)
import Components.Styles as Styles
import Element exposing (..)
import Global
@ -12,6 +13,11 @@ type alias Context msg =
}
transition : Transition (Element msg)
transition =
Transition.fadeUi 3000
view : Context msg -> Element msg
view { page } =
column

View File

@ -1,9 +1,15 @@
module Layouts.Guide.Dynamic exposing (view)
module Layouts.Guide.Dynamic exposing (transition, view)
import App.Transition as Transition exposing (Transition)
import Element exposing (..)
import Global
transition : Transition (Element msg)
transition =
Transition.optOut
type alias Context msg =
{ page : Element msg
, global : Global.Model

View File

@ -1,5 +1,6 @@
module Layouts.Guide.Dynamic.Dynamic exposing (view)
module Layouts.Guide.Dynamic.Dynamic exposing (transition, view)
import App.Transition as Transition exposing (Transition)
import Element exposing (..)
import Global
@ -11,6 +12,11 @@ type alias Context msg =
}
transition : Transition (Element msg)
transition =
Transition.optOut
view : Context msg -> Element msg
view { page } =
page

View File

@ -1,5 +1,6 @@
module Layouts.Guide.Dynamic.Faq exposing (view)
module Layouts.Guide.Dynamic.Faq exposing (transition, view)
import App.Transition as Transition exposing (Transition)
import Element exposing (..)
import Global
@ -11,6 +12,11 @@ type alias Context msg =
}
transition : Transition (Element msg)
transition =
Transition.optOut
view : Context msg -> Element msg
view { page } =
page

View File

@ -1,6 +1,7 @@
module Main exposing (main)
import App
import App.Pattern as Pattern
import App.Transition as Transition
import Element
import Generated.Pages as Pages
@ -17,6 +18,11 @@ main =
}
, routing =
{ transition = Transition.fadeUi 300
, patterns =
[ ( [], Transition.fadeUi 300 )
, ( [ Pattern.static "guide" ], Transition.fadeUi 3000 )
, ( [ Pattern.static "docs" ], Transition.fadeUi 200 )
]
, routes = Routes.parsers
, toPath = Routes.toPath
, notFound = Routes.routes.notFound

View File

@ -61,6 +61,7 @@ import Browser
import Browser.Navigation as Nav
import Html exposing (Html)
import Internals.Page as Page
import Internals.Pattern as Pattern exposing (Pattern)
import Internals.Transition as Transition exposing (Transition)
import Internals.Utils as Utils
import Url exposing (Url)
@ -117,6 +118,7 @@ create :
, routes : List (Parser (route -> route) route)
, toPath : route -> String
, notFound : route
, patterns : List ( Pattern, Transition ui_msg )
}
, global :
{ init :
@ -163,6 +165,7 @@ create config =
{ fromUrl = fromUrl config.routing
, toPath = config.routing.toPath
, routes = config.routing.routes
, patterns = config.routing.patterns
}
, init = page.init
, update =
@ -175,6 +178,7 @@ create config =
{ bundle = page.bundle
, map = config.ui.map
, global = config.global.subscriptions
, transition = config.routing.transition
}
, view =
view
@ -212,6 +216,7 @@ type alias Model flags globalModel model =
, key : Nav.Key
, global : globalModel
, page : model
, transitioningPattern : Pattern
, visibilities :
{ layout : Transition.Visibility
, page : Transition.Visibility
@ -256,6 +261,7 @@ init config flags url key =
, key = key
, global = globalModel
, page = pageModel
, transitioningPattern = []
, visibilities =
{ layout = Transition.invisible
, page = Transition.visible
@ -282,6 +288,7 @@ type Msg globalMsg msg
| Global globalMsg
| Page msg
| FadeInLayout
| FadeInPage Url
update :
@ -289,6 +296,7 @@ update :
{ fromUrl : Url -> route
, toPath : route -> String
, routes : Routes route a
, patterns : List ( Pattern, Transition ui_msg )
}
, init : route -> Page.Init layoutModel layoutMsg globalModel globalMsg
, update :
@ -318,6 +326,22 @@ update config msg model =
, Cmd.none
)
FadeInPage url ->
url
|> config.routing.fromUrl
|> (\route -> config.init route { global = model.global })
|> (\( pageModel, pageCmd, globalCmd ) ->
( { model
| visibilities = { layout = Transition.visible, page = Transition.visible }
, page = pageModel
}
, Cmd.batch
[ Cmd.map Page pageCmd
, Cmd.map Global globalCmd
]
)
)
ClickedLink (Browser.Internal url) ->
if url == model.url then
( model, Cmd.none )
@ -333,20 +357,32 @@ update config msg model =
)
ChangedUrl url ->
url
|> config.routing.fromUrl
|> (\route -> config.init route { global = model.global })
|> (\( pageModel, pageCmd, globalCmd ) ->
( { model
| url = url
, page = pageModel
}
, Cmd.batch
[ Cmd.map Page pageCmd
, Cmd.map Global globalCmd
]
)
)
let
( pattern, speed ) =
chooseFrom
{ patternTransitions = config.routing.patterns
, from = model.url
, to = url
}
|> Just
|> Maybe.withDefault (List.head config.routing.patterns)
|> Maybe.map (Tuple.mapSecond Transition.speed)
|> Maybe.withDefault ( [], 0 )
in
( { model
| url = url
, visibilities =
{ layout = Transition.visible
, page = Transition.invisible
}
, transitioningPattern = pattern
}
, Cmd.batch
[ Utils.delay
speed
(FadeInPage url)
]
)
Global globalMsg ->
config.update.global
@ -391,6 +427,7 @@ subscriptions :
layoutModel
-> Page.Bundle layoutMsg ui_layoutMsg globalModel globalMsg (Msg globalMsg layoutMsg) ui_msg
, global : globalModel -> Sub globalMsg
, transition : Transition ui_msg
}
-> Model flags globalModel layoutModel
-> Sub (Msg globalMsg layoutMsg)
@ -402,6 +439,8 @@ subscriptions config model =
, fromPageMsg = Page
, global = model.global
, map = config.map
, transitioningPattern = model.transitioningPattern
, visibility = model.visibilities.page
}
).subscriptions
, Sub.map Global (config.global model.global)
@ -431,6 +470,8 @@ view config model =
, fromPageMsg = Page
, global = model.global
, map = config.map
, transitioningPattern = model.transitioningPattern
, visibility = model.visibilities.page
}
in
{ title = bundle.title
@ -442,3 +483,35 @@ view config model =
{ layout = identity, page = bundle.view }
]
}
-- Transition magic
chooseFrom :
{ patternTransitions : List ( Pattern, Transition ui_msg )
, from : Url
, to : Url
}
-> Maybe ( Pattern, Transition ui_msg )
chooseFrom options =
let
( fromPath, toPath ) =
( options.from, options.to )
|> Tuple.mapBoth urlPath urlPath
in
options.patternTransitions
|> List.reverse
|> List.filter
(\( pattern, transition ) ->
Pattern.matches fromPath pattern
&& Pattern.matches toPath pattern
&& (transition /= Transition.optOut)
)
|> List.head
urlPath : Url -> List String
urlPath url =
url.path |> String.dropLeft 1 |> String.split "/"

View File

@ -119,6 +119,8 @@ You only need **one** case expression: (woohoo, less boilerplate!)
-}
import Internals.Page exposing (..)
import Internals.Pattern as Pattern exposing (Pattern)
import Internals.Transition as Transition exposing (Transition)
import Internals.Utils as Utils
@ -505,6 +507,21 @@ layout map options =
, bundle =
\model context ->
let
viewLayout page =
options.view
{ page = page
, global = context.global
, toMsg = context.fromGlobalMsg
}
myLayoutsVisibility : Transition.Visibility
myLayoutsVisibility =
if context.transitioningPattern == options.pattern then
context.visibility
else
Transition.visible
bundle : { title : String, view : ui_msg, subscriptions : Sub msg }
bundle =
options.recipe.bundle
@ -513,14 +530,17 @@ layout map options =
, fromPageMsg = toMsg >> context.fromPageMsg
, global = context.global
, map = map
, transitioningPattern = context.transitioningPattern
, visibility = context.visibility
}
in
{ title = bundle.title
, view =
options.view
{ page = bundle.view
, global = context.global
, toMsg = context.fromGlobalMsg
Transition.view
options.transition
myLayoutsVisibility
{ layout = viewLayout
, page = bundle.view
}
, subscriptions = bundle.subscriptions
}

21
src/App/Pattern.elm Normal file
View File

@ -0,0 +1,21 @@
module App.Pattern exposing
( Pattern
, dynamic
, static
)
import Internals.Pattern as Internals
type alias Pattern =
Internals.Pattern
static : String -> Internals.Piece
static =
Internals.static
dynamic : Internals.Piece
dynamic =
Internals.dynamic

View File

@ -1,12 +1,12 @@
module App.Transition exposing
( Transition
, optOut, none, fadeHtml, fadeUi
, optOut, none, fadeHtml, fadeUi, custom
)
{-|
@docs Transition
@docs optOut, none, fadeHtml, fadeUi
@docs optOut, none, fadeHtml, fadeUi, custom
-}
@ -41,3 +41,20 @@ fadeHtml =
fadeUi : Int -> Transition (Element msg)
fadeUi =
Internals.Transition.fadeUi
custom :
{ speed : Int
, invisible : View ui_msg
, visible : View ui_msg
}
-> Transition ui_msg
custom =
Internals.Transition.custom
type alias View ui_msg =
{ layout : ui_msg -> ui_msg
, page : ui_msg
}
-> ui_msg

View File

@ -9,6 +9,9 @@ module Internals.Page exposing
, upgrade
)
import Internals.Pattern exposing (Pattern)
import Internals.Transition as Transition exposing (Transition)
type Page pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg
= Page (Page_ pageParams pageModel pageMsg ui_pageMsg layoutModel layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg)
@ -67,6 +70,8 @@ type alias Bundle layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg =
, fromGlobalMsg : globalMsg -> msg
, fromPageMsg : layoutMsg -> msg
, map : (layoutMsg -> msg) -> ui_layoutMsg -> ui_msg
, visibility : Transition.Visibility
, transitioningPattern : Pattern
}
->
{ title : String
@ -76,7 +81,9 @@ type alias Bundle layoutMsg ui_layoutMsg globalModel globalMsg msg ui_msg =
type alias Layout pageParams pageModel pageMsg ui_pageMsg globalModel globalMsg msg ui_msg =
{ view :
{ pattern : Pattern
, transition : Transition ui_msg
, view :
{ page : ui_msg
, global : globalModel
, toMsg : globalMsg -> msg

48
src/Internals/Pattern.elm Normal file
View File

@ -0,0 +1,48 @@
module Internals.Pattern exposing
( Pattern
, Piece
, dynamic
, matches
, static
)
type alias Pattern =
List Piece
type Piece
= Static String
| Dynamic
static : String -> Piece
static =
Static
dynamic : Piece
dynamic =
Dynamic
matches : List String -> List Piece -> Bool
matches strings pieces =
List.length pieces
<= List.length strings
&& (List.map2
comparePiece
strings
pieces
|> List.all ((==) True)
)
comparePiece : String -> Piece -> Bool
comparePiece str piece =
case piece of
Static xyz ->
str == xyz
Dynamic ->
True

View File

@ -1,7 +1,7 @@
module Internals.Transition exposing
( Transition
, speed, view
, optOut, none, fadeHtml, fadeUi
, optOut, none, fadeHtml, fadeUi, custom
, Visibility
, visible, invisible
)
@ -9,7 +9,7 @@ module Internals.Transition exposing
{-|
@docs Transition
@docs speed, view
@docs speed, view, chooseFrom
@docs optOut, none, fadeHtml, fadeUi
@docs Visibility
@ -20,6 +20,7 @@ module Internals.Transition exposing
import Element exposing (Element)
import Html exposing (Html)
import Html.Attributes as Attr
import Url exposing (Url)
type Visibility
@ -40,10 +41,10 @@ invisible =
type Transition ui_msg
= OptOut
| None
| Transition (Transition_ ui_msg)
| Transition (Options ui_msg)
type alias Transition_ ui_msg =
type alias Options ui_msg =
{ speed : Int
, invisible : View ui_msg
, visible : View ui_msg
@ -160,3 +161,11 @@ fadeUi speed_ =
, invisible = withOpacity 0
, visible = withOpacity 1
}
custom :
{ speed : Int
, invisible : View ui_msg
, visible : View ui_msg
} -> Transition ui_msg
custom =
Transition