mirror of
https://github.com/ryannhg/elm-spa.git
synced 2024-11-22 01:32:43 +03:00
transitions work!!
This commit is contained in:
parent
621167788f
commit
e2fc5433df
@ -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; }
|
||||
</style>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 }
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user