diff --git a/examples/08-layouts/public/index.html b/examples/08-layouts/public/index.html
index 03e7f27..7a64c8e 100644
--- a/examples/08-layouts/public/index.html
+++ b/examples/08-layouts/public/index.html
@@ -11,7 +11,7 @@
a { text-decoration: underline;}
a, button { cursor: pointer;}
.border-right { border-right: solid 1px #ddd;}
- .tab { border-bottom: solid 2px white; padding: 1rem; text-decoration: none; }
+ .tab { border-bottom: solid 2px white; padding: 1rem; text-decoration: none; transition: color 200ms, border-color 200ms; }
.tab--active { border-bottom: solid 2px dodgerblue; padding: 0.75rem 1rem; color: dodgerblue; }
.active { color: dodgerblue; }
diff --git a/examples/08-layouts/src/Gen/Layout.elm b/examples/08-layouts/src/Gen/Layout.elm
index cd1798c..950d312 100644
--- a/examples/08-layouts/src/Gen/Layout.elm
+++ b/examples/08-layouts/src/Gen/Layout.elm
@@ -16,6 +16,7 @@ module Gen.Layout exposing
import Effect exposing (Effect)
import Request exposing (Request)
import Shared
+import Transition
import View exposing (View)
@@ -98,7 +99,7 @@ toBundle :
{ init : Shared.Model -> Request -> ( genModel, Effect genMsg )
, update : msg -> model -> Shared.Model -> Request -> ( genModel, Effect genMsg )
, subscriptions : model -> Shared.Model -> Request -> Sub genMsg
- , view : model -> { viewPage : View mainMsg, toMainMsg : genMsg -> mainMsg } -> Shared.Model -> Request -> View mainMsg
+ , view : List Transition.Attribute -> model -> { viewPage : View mainMsg, toMainMsg : genMsg -> mainMsg } -> Shared.Model -> Request -> View mainMsg
}
toBundle toModel toMsg toLayout =
let
@@ -120,9 +121,9 @@ toBundle toModel toMsg toLayout =
(toRecord shared req).subscriptions model
|> Sub.map toMsg
, view =
- \model options shared req ->
+ \attrs model options shared req ->
(toRecord shared req).view
- { viewPage = options.viewPage
+ { viewPage = Transition.apply attrs options.viewPage
, toMainMsg = toMsg >> options.toMainMsg
}
model
@@ -140,7 +141,7 @@ toBundle2 :
, update1 : model2 -> msg1 -> model1 -> Shared.Model -> Request -> ( genModel, Effect genMsg )
, update2 : model1 -> msg2 -> model2 -> Shared.Model -> Request -> ( genModel, Effect genMsg )
, subscriptions : model2 -> Shared.Model -> Request -> Sub genMsg
- , view : model2 -> { viewPage : View mainMsg, toMainMsg : genMsg -> mainMsg } -> Shared.Model -> Request -> View mainMsg
+ , view : List Transition.Attribute -> model2 -> { viewPage : View mainMsg, toMainMsg : genMsg -> mainMsg } -> Shared.Model -> Request -> View mainMsg
}
toBundle2 toModel toMsg1 toMsg2 toLayout1 toLayout2 =
let
@@ -182,9 +183,9 @@ toBundle2 toModel toMsg1 toMsg2 toLayout1 toLayout2 =
(toRecord2 shared req).subscriptions model
|> Sub.map toMsg2
, view =
- \model options shared req ->
+ \attrs model options shared req ->
(toRecord2 shared req).view
- { viewPage = options.viewPage
+ { viewPage = Transition.apply attrs options.viewPage
, toMainMsg = toMsg2 >> options.toMainMsg
}
model
diff --git a/examples/08-layouts/src/Gen/Layouts.elm b/examples/08-layouts/src/Gen/Layouts.elm
index 62d55e6..37c443b 100644
--- a/examples/08-layouts/src/Gen/Layouts.elm
+++ b/examples/08-layouts/src/Gen/Layouts.elm
@@ -22,6 +22,7 @@ import Layouts.Sidebar
import Layouts.Sidebar.Header
import Request exposing (Request)
import Shared
+import Transition
import View exposing (View)
@@ -116,15 +117,26 @@ subscriptions model_ shared req =
view :
- Model
+ { before : Maybe Layout, after : Maybe Layout }
+ -> { current : List Transition.Attribute }
+ -> Model
-> { viewPage : View mainMsg, toMainMsg : Msg -> mainMsg }
-> Shared.Model
-> Request
-> View mainMsg
-view model_ options shared req =
+view { before, after } { current } model_ options shared req =
case model_ of
Sidebar_Model model ->
- layouts.sidebar.view model options shared req
+ layouts.sidebar.view current model options shared req
Sidebar__Header_Model model1 model2 ->
- layouts.sidebar.view model1 { options | viewPage = layouts.sidebar__header.view model2 options shared req } shared req
+ let
+ ( transition1, transition2 ) =
+ case ( before, after ) of
+ ( Just Sidebar__Header, Just Sidebar__Header ) ->
+ ( Transition.visible, current )
+
+ _ ->
+ ( current, Transition.visible )
+ in
+ layouts.sidebar.view transition1 model1 { options | viewPage = layouts.sidebar__header.view transition2 model2 options shared req } shared req
diff --git a/examples/08-layouts/src/Layouts/Sidebar/Header.elm b/examples/08-layouts/src/Layouts/Sidebar/Header.elm
index 7197dde..f57878e 100644
--- a/examples/08-layouts/src/Layouts/Sidebar/Header.elm
+++ b/examples/08-layouts/src/Layouts/Sidebar/Header.elm
@@ -31,17 +31,17 @@ view route { viewPage } =
{ title = viewPage.title
, body =
[ Html.div [ Attr.class "col align-top gap-lg fill-y" ]
- [ viewHeader viewPage.title route
+ [ viewHeader route
, Html.div [ Attr.class "page" ] viewPage.body
]
]
}
-viewHeader : String -> Route -> Html msg
-viewHeader title route =
+viewHeader : Route -> Html msg
+viewHeader route =
Html.header [ Attr.class "col gap-md fill-y pad-right-lg" ]
- [ Html.span [ Attr.class "h1" ] [ Html.text title ]
+ [ Html.span [ Attr.class "h1" ] [ Html.text "Settings" ]
, Html.div [ Attr.class "row gap-md" ]
[ viewTab route { label = "General", route = Route.Settings__General }
, viewTab route { label = "Profile", route = Route.Settings__Profile }
diff --git a/examples/08-layouts/src/Main.elm b/examples/08-layouts/src/Main.elm
index 36a4bbf..a4dc623 100644
--- a/examples/08-layouts/src/Main.elm
+++ b/examples/08-layouts/src/Main.elm
@@ -7,8 +7,11 @@ import Gen.Layouts exposing (Layout)
import Gen.Model
import Gen.Pages_ as Pages
import Gen.Route as Route
+import Process
import Request exposing (Request)
import Shared
+import Task
+import Transition
import Url exposing (Url)
import View
@@ -35,9 +38,16 @@ type alias Model =
, shared : Shared.Model
, layout : Maybe { kind : Layout, model : Gen.Layouts.Model }
, page : Pages.Model
+ , transition : Transition
}
+type Transition
+ = InvisibleApp { before : Maybe Layout, after : Maybe Layout }
+ | FadingOutPage { before : Maybe Layout, after : Maybe Layout }
+ | VisiblePage { before : Maybe Layout, after : Maybe Layout }
+
+
init : Shared.Flags -> Url -> Key -> ( Model, Cmd Msg )
init flags url key =
let
@@ -53,16 +63,37 @@ init flags url key =
maybeLayout =
Pages.layout (Route.fromUrl url)
|> Maybe.map (initializeLayout Nothing { shared = shared, request = req })
+
+ transitionEffect =
+ if Transition.duration > 0 then
+ sendDelayedMsg Transition.duration FadeInApp
+
+ else
+ Cmd.none
in
- ( Model url key shared (Maybe.map toKindAndModel maybeLayout) page
+ ( { url = url
+ , key = key
+ , shared = shared
+ , layout = Maybe.map toKindAndModel maybeLayout
+ , page = page
+ , transition = InvisibleApp { before = Nothing, after = Maybe.map .kind maybeLayout }
+ }
, Cmd.batch
[ Cmd.map Shared sharedCmd
, Effect.toCmd ( Shared, Page ) pageEffect
, Effect.toCmd ( Shared, Layout ) (toLayoutEffect maybeLayout)
+ , transitionEffect
]
)
+sendDelayedMsg : Int -> Msg -> Cmd Msg
+sendDelayedMsg ms msg =
+ Process.sleep (toFloat ms)
+ |> Task.map (\_ -> msg)
+ |> Task.perform identity
+
+
initializeLayout : Maybe Gen.Layouts.Model -> { shared : Shared.Model, request : Request } -> Layout -> { kind : Layout, model : Gen.Layouts.Model, effect : Effect Gen.Layouts.Msg }
initializeLayout maybeModel { shared, request } layoutKind =
let
@@ -97,6 +128,8 @@ type Msg
| Shared Shared.Msg
| Layout Gen.Layouts.Msg
| Page Pages.Msg
+ | FadeInApp
+ | FadeInPage Url
update : Msg -> Model -> ( Model, Cmd Msg )
@@ -114,49 +147,29 @@ update msg model =
ChangedUrl url ->
if url.path /= model.url.path then
- let
- route =
- Route.fromUrl url
-
- ( page, effect ) =
- Pages.init route model.shared url model.key
-
- currentLayout =
- model.layout
-
- newLayoutKind =
- Pages.layout route
- in
- if Maybe.map .kind currentLayout == newLayoutKind then
- ( { model | url = url, page = page }
- , Effect.toCmd ( Shared, Page ) effect
+ if Transition.duration > 0 then
+ ( { model
+ | transition =
+ FadingOutPage
+ { before = Maybe.map .kind model.layout
+ , after = Pages.layout (Route.fromUrl url)
+ }
+ }
+ , sendDelayedMsg Transition.duration (FadeInPage url)
)
else
- let
- maybeLayout =
- newLayoutKind
- |> Maybe.map
- (initializeLayout (Maybe.map .model currentLayout)
- { shared = model.shared
- , request = Request.create () url model.key
- }
- )
- in
- ( { model
- | url = url
- , page = page
- , layout = Maybe.map toKindAndModel maybeLayout
- }
- , Cmd.batch
- [ Effect.toCmd ( Shared, Page ) effect
- , Effect.toCmd ( Shared, Layout ) (toLayoutEffect maybeLayout)
- ]
- )
+ updateModelFromUrl url model
else
( { model | url = url }, Cmd.none )
+ FadeInApp ->
+ ( { model | transition = VisiblePage { before = Nothing, after = Maybe.map .kind model.layout } }, Cmd.none )
+
+ FadeInPage url ->
+ updateModelFromUrl url model
+
Shared sharedMsg ->
let
( shared, sharedCmd ) =
@@ -205,6 +218,54 @@ update msg model =
( model, Cmd.none )
+updateModelFromUrl : Url -> Model -> ( Model, Cmd Msg )
+updateModelFromUrl url model =
+ let
+ route =
+ Route.fromUrl url
+
+ ( page, effect ) =
+ Pages.init route model.shared url model.key
+
+ currentLayout =
+ model.layout
+
+ newLayoutKind =
+ Pages.layout route
+ in
+ if Maybe.map .kind currentLayout == newLayoutKind then
+ ( { model
+ | url = url
+ , page = page
+ , transition = VisiblePage { before = Maybe.map .kind currentLayout, after = newLayoutKind }
+ }
+ , Effect.toCmd ( Shared, Page ) effect
+ )
+
+ else
+ let
+ maybeLayout =
+ newLayoutKind
+ |> Maybe.map
+ (initializeLayout (Maybe.map .model currentLayout)
+ { shared = model.shared
+ , request = Request.create () url model.key
+ }
+ )
+ in
+ ( { model
+ | url = url
+ , page = page
+ , layout = Maybe.map toKindAndModel maybeLayout
+ , transition = VisiblePage { before = Maybe.map .kind currentLayout, after = newLayoutKind }
+ }
+ , Cmd.batch
+ [ Effect.toCmd ( Shared, Page ) effect
+ , Effect.toCmd ( Shared, Layout ) (toLayoutEffect maybeLayout)
+ ]
+ )
+
+
-- VIEW
@@ -216,10 +277,24 @@ view model =
Pages.view model.page model.shared model.url model.key
|> View.map Page
+ ( attrs, layoutKinds ) =
+ case model.transition of
+ InvisibleApp kinds ->
+ ( Transition.invisible, kinds )
+
+ FadingOutPage kinds ->
+ ( Transition.invisible, kinds )
+
+ VisiblePage kinds ->
+ ( Transition.visible, kinds )
+
viewLayout =
case model.layout of
Just layout ->
- Gen.Layouts.view layout.model
+ Gen.Layouts.view
+ layoutKinds
+ { current = attrs }
+ layout.model
{ viewPage = viewPage
, toMainMsg = Layout
}
@@ -227,9 +302,14 @@ view model =
(Request.create () model.url model.key)
Nothing ->
- viewPage
+ Transition.apply attrs viewPage
in
- View.toBrowserDocument viewLayout
+ case ( layoutKinds.before, layoutKinds.after ) of
+ ( Just _, Just _ ) ->
+ View.toBrowserDocument (Transition.apply Transition.visible viewLayout)
+
+ _ ->
+ View.toBrowserDocument (Transition.apply attrs viewLayout)
diff --git a/examples/08-layouts/src/Transition.elm b/examples/08-layouts/src/Transition.elm
index d09e29e..5af4dea 100644
--- a/examples/08-layouts/src/Transition.elm
+++ b/examples/08-layouts/src/Transition.elm
@@ -1,24 +1,39 @@
-module Transition exposing (Transition, layout, page)
+module Transition exposing
+ ( Attribute
+ , apply
+ , duration
+ , invisible
+ , visible
+ )
+
+import Html
+import Html.Attributes as Attr
+import View exposing (View)
-type alias Transition attr =
- { duration : Int
- , invisible : List attr
- , visible : List attr
- }
-
-
-layout : Transition attr
-layout =
- { duration = 0
- , invisible = []
- , visible = []
- }
-
-
-page : Transition attr
-page =
- { duration = 0
- , invisible = []
- , visible = []
+type alias Attribute =
+ Html.Attribute Never
+
+
+duration : number
+duration =
+ 500
+
+
+invisible : List Attribute
+invisible =
+ [ Attr.class "fill-y", Attr.style "opacity" "0", Attr.style "transition" "opacity 500ms ease-in-out" ]
+
+
+visible : List Attribute
+visible =
+ [ Attr.class "fill-y", Attr.style "opacity" "1", Attr.style "transition" "opacity 500ms ease-in-out" ]
+
+
+apply : List Attribute -> View msg -> View msg
+apply attrs view =
+ { title = view.title
+ , body =
+ [ Html.div (List.map (Attr.map never) attrs) view.body
+ ]
}