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