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

View File

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

View File

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

View File

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

View File

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

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