Aggregate View Transitions
What are Transitions?
Transitions are classes that control the switch between aggregate views (for example, pushing and popping screens in NavController
, or switching tabs in TabController
which internally uses a NavController
). Hosanna ships with two built-ins:
- SimpleTransition: completes immediately (no visual animation)
- FadeTransition: fades out, swaps view, fades in again; configurable
color
andduration
You can specify a transition per navigation action (push/pop/reset/replace) or define defaults in style JSON, so all NavController
instances use the same transition unless overridden.
Set a sensible default in style.json
(like a quick fade) and override per call only when needed. It keeps code clean and UX consistent.
Use a transition for a single push/pop (concise)
// inside a view that owns a NavController
import { NavControllerView } from 'hosanna-ui/views/aggregate-views/NavController';
// Push with a specific transition via style key (overrides default for this call)
(this.parent as NavControllerView).push(nextView, true, 'controls.NavController.transition.fadeQuickBlack');
// Pop with a specific transition
(this.parent as NavControllerView).pop(true, 'controls.NavController.transition.fade');
See the example rig (core action only):
// Inside a button click handler
const nextView = ScreenPusher({ viewIndex: this.viewIndex + 1 });
(this.parent as NavControllerView)
.push(nextView, true, this.viewIndex === 2 ? 'controls.NavController.transition.fadeQuickBlack' : undefined);
Define a default transition via style.json
You can set a default transition in your style config so every NavController
uses it unless a call overrides it with a transitionStyleKey
.
{
"controls": {
"NavController": {
"transition": {
"default": {
"transitionType": "Simple"
},
"fade": {
"transitionType": "Fade",
"transitionOptions": {
"duration": 500,
"color": "~theme.colors.grayscale900"
}
}
}
}
}
}
- Key format:
controls.NavController.transition.<name>
; the framework also looks upcontrols.NavController.transition.default
as a fallback. - Types:
Fade
andSimple
are built-in. Map toFadeTransition
orSimpleTransition
respectively.
Transition keys are part of your theme config under controls.NavController.transition
. Use default
as a safety net for all controllers.
You can create multiple named transitions and reference them by key. For example:
{
"controls": {
"NavController": {
"transition": {
"default": { "transitionType": "Simple" },
"fade": {
"transitionType": "Fade",
"transitionOptions": { "duration": 500, "color": "~theme.colors.grayscale900" }
},
"fadeQuickBlack": {
"transitionType": "Fade",
"transitionOptions": { "duration": 250, "color": "#000000" }
},
"fadeSlowGreen": {
"transitionType": "Fade",
"transitionOptions": { "duration": 1200, "color": "#003300" }
},
"fadeSlowYellow": {
"transitionType": "Fade",
"transitionOptions": { "duration": 1200, "color": "#333300" }
}
}
}
}
}
Built-ins (reference)
SimpleTransition
: completes instantly (no animation)FadeTransition
: fades to color and back; acceptscolor
andduration
options
Use either by specifying a transition style key or overriding per push/pop/reset/replace call.
Write the animation, then call finish()
. Avoid setting view state in the middle of a tick—let the transition switch views and focus cleanly.
Defaults for FadeTransition
if not provided: color: '#000000'
, duration: 1000
(ms).
Create your own transition (overview)
To implement a custom transition:
- Extend
AggregateViewTransition
- Override
begin()
to perform the animation (and callfinish()
when done) - Optionally override
finish()
to finalize focus/visibility and cleanup
Keep begin()
focused on: staging target view, animating in/out, switching current view, then calling finish()
.
See example rigs for usage patterns.
Prototype with SimpleTransition
to confirm lifecycle and focus are correct, then swap in a custom transition.
Transition style keys and config
Transitions are declared in style.json
and referenced by key. Params shape:
-
transitionType
:Simple
|Fade
-
transitionOptions
: e.g.,{ duration: 500, color: '#000000' }
-
Key format:
controls.NavController.transition.<name>
(withdefault
as fallback) -
Supported types:
Simple
,Fade
(extendable via custom transitions)
⚙️ Key Props & Concepts
- transitionStyleKey: A string key like
controls.NavController.transition.fade
applied onNavController
or passed per call. - AggregateViewTransitionType:
Simple
|Fade
. Extend via custom classes. - transitionOptions: For
FadeTransition
, supportscolor
andduration
(ms). - Per-call override:
push(view, animated, key)
,pop(animated, key)
,reset(..., key)
,replace(..., key)
. - Focus management:
finish()
handles visibility and focus; don’t fight it.
Live Example
Use the buttons to push screens with different fade presets, then pop to see how the default applies.
Use transitions with TabController
Each TabController
tab hosts its own NavController
. Set a tab-specific default transition via transitionStyleKey
on the tab definition. Under the hood, TabController
passes this key to the child NavController
.
NavController API (with transition override)
You can override the transition per call on all primary navigation APIs:
- push(view, animated = true, key?)
- pop(animated = true, key?)
- reset(endIndex = 0, newFirstScreen?, animated = false, key?)
- replace(newView?, animated = false, key?)
Give each tab a different transitionStyleKey
for distinct feel, while keeping navigation APIs the same.
Per-call style keys are converted internally to a temporary transition instance; if omitted, the controller's transitionStyleKey
(or the default
in style.json) is used.
How transition resolution works
When a navigation action occurs, the aggregate view picks a transition using:
- A temporary per-call transition (if provided)
- Otherwise, the configured
transitionStyleKey
- Otherwise, the fallback key
controls.NavController.transition.default
Transition Flow (Sequence)
- Per-call style key → 2) Controller
transitionStyleKey
→ 3)controls.NavController.transition.default
.
Class Relationships
API Surface Overview
Internally, resolution works like this:
- Build a temporary transition if a per-call style key is provided
- Else build from the controller’s
transitionStyleKey
- Else build from
controls.NavController.transition.default
If a referenced key is missing or invalid, a warning is logged and the default is used.
Minimal custom transition
- Extend
AggregateViewTransition
- Stage target → animate → call
finish()
If your transition adds overlay nodes (e.g., a fade rectangle), release them in finish()
to avoid leaks.