transitions work!!

This commit is contained in:
Ryan Haskell-Glatz 2021-08-04 21:03:31 -05:00
parent 621167788f
commit e2fc5433df
6 changed files with 184 additions and 76 deletions

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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 }

View File

@ -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)

View File

@ -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
]
}