Fragment Styles
Fragments are reusable view trees defined in AppConfig. DynamicCell uses them for CollectionView cells, and FragmentView can mount the same fragment model directly in a screen or control.
A fragment style has three main parts:
views.base: the SG child nodes created when the fragment is allocated.- Status styles such as
views.normal,views.focused,views.selected, andviews.disabled: field updates applied to existing child nodes when the fragment status changes. - Optional
$supportsDataMap, data bindings, function bindings, constraints, and callbacks: runtime behavior that connects row data or host view data to the fragment. - Optional computed values: declarative primitive values that can drive fragment fields and numeric constraint arguments.
Fragments are resolved through AppConfig.get(styleKey), then created and pooled by ViewFragmentProvider. The provider keeps fragments by styleKey, reuses released fragments, tracks child views by id, and falls back to cells.missingCell when an unknown cell style is requested and that fallback exists.
AppConfig turns authored fragment metadata into static SG fields plus runtime data-map, computed, constraint, status, and callback behavior.
Fragment Shape
{
"cells": {
"hero": {
"$supportsDataMap": true,
"id": "heroCell",
"views": {
"base": [
{
"id": "poster",
"subType": "Poster",
"width": 384,
"height": 216,
"uri": "${data.imageSet.$RES$}"
},
{
"id": "title",
"subType": "Label",
"fontKey": "~theme.fonts.heading-32",
"text": "${data.title}",
"translation": [0, 230],
"width": 384
}
],
"normal": {
"poster": { "opacity": 0.85 },
"title": { "color": "~theme.colors.white" }
},
"focused": {
"poster": { "opacity": 1, "scale": [1.05, 1.05] },
"title": { "color": "~theme.colors.accent" }
},
"selected": {
"poster": { "opacity": 0.95 }
},
"disabled": {
"poster": { "opacity": 0.4 },
"title": { "color": "~theme.colors.disabledText" }
}
}
}
}
}
Every child in views.base that should receive status updates, data updates, or callback-driven updates needs a stable id. ViewFragmentProvider builds viewsById from those IDs; duplicate IDs make later updates ambiguous.
views.base is applied once when the fragment view is created. Status sections are maps from child ID to SG field updates. When applyViewStatus(fragment, status) runs, the provider resolves ${styleKey}.views.${status} and updates only the fields declared for that status.
Dynamic Bindings
$supportsDataMap enables ${...} bindings inside fragment field values. During AppConfig resolution, those bindings are removed from the static views.base fields and stored in the fragment's internal data map. At runtime, DynamicCell and FragmentView call applyDataMap(fragment, data) to update the target child fields from the current content.
Use data bindings for direct field updates:
{
"id": "poster",
"subType": "Poster",
"uri": "${data.imageSet.$RES$}",
"width": 384,
"height": 216
}
The path after data. is read from the row item or FragmentView data object. Array-style paths are normalized into dot paths, so ${data.images[0].url} is tracked as images.0.url. When $RES$ appears inside a data binding path, AppConfig replaces it with the current resolution suffix, such as fhd, hd, or sd.
Use fn bindings when a field must be computed rather than copied directly:
{
"id": "progress",
"subType": "Rectangle",
"height": 8,
"width": "${fn.progressWidth(320)}"
}
The function name must be registered as an onSpecificViewDataChange callback. AppConfig parses the function name and arguments, and applyDataMap calls the registered function with (data, childView, field, args). The return value is assigned to that field.
Function arguments support booleans, null, undefined, quoted strings, and numbers:
{
"width": "${fn.metricWidth(320, 'compact', true)}"
}
Fragment Constraints
Fragment constraints let AppConfig reposition and resize fragment children after the fragment is mounted or its data changes. The usual authoring form is an inline {{constraint.*(...)}} binding on width, height, x, y, or translation; AppConfig compiles those bindings into _dataMap.constraints.
Use Fragment Constraints for inline constraint syntax, explicit rule shape, priorities, supported functions, and runtime semantics.
Fragment Callbacks
Fragments can declare lifecycle, status, and data callbacks in AppConfig. Register callback names with ViewFragmentProvider, then reference those names from the fragment style.
import {
ViewFragmentCallbackType,
type ViewFragmentCallbackHandler,
type ISpecificViewFragmentCallbackHandler,
} from 'hosanna-ui/views/lib/AppConfig';
import type { IViewFragmentProvider } from 'hosanna-ui/views/lib/ViewFragmentProvider';
import { AppUtils } from 'hosanna-ui/hosanna-bridge-lib/AppUtils';
const provider = AppUtils.resolve<IViewFragmentProvider>('viewFragmentProvider');
provider.registerViewFragmentCallback<ViewFragmentCallbackHandler>(
ViewFragmentCallbackType.OnDataChange,
'layoutMetadata',
(host, fragment, data, status) => {
const title = fragment.viewsById.title;
const background = fragment.viewsById.background;
background.width = title.localBoundingRect().width + 40;
}
);
provider.registerViewFragmentCallback<ISpecificViewFragmentCallbackHandler>(
ViewFragmentCallbackType.OnSpecificViewDataChange,
'progressWidth',
(data, child, field, args) => {
const fullWidth = args[0] as number;
return fullWidth * Number(data.progress ?? 0);
}
);
Supported callback groups:
| Callback | Handler arguments | When it runs |
|---|---|---|
onMount | (host, fragment, data, status) | After a fragment view is attached to a DynamicCell or FragmentView host. |
onUnmount | (host, fragment, data, status) | Before a fragment is released back to the provider pool. |
onApplyViewStatus | (host, fragment, data, status) | After a status style such as normal, focused, selected, or disabled is applied. |
onDataChange | (host, fragment, data, status) | After applyDataMap updates data-bound fields. |
onSpecificViewDataChange | (data, childView, field, args) | Registered on the provider and invoked by ${fn.name(...)} bindings during applyDataMap. The return value becomes the field value. |
Use direct ${data.*} bindings for copied values such as text, image URI, opacity, or dimensions. Use onDataChange when the update needs multiple child views, measurement, host cell dimensions, or layout side effects. Use onSpecificViewDataChange when a single field should be computed from item data.
Use Fragment Computed Values when a single field can be derived declaratively from item data, cell size, focus state, screen size, or safe-area insets.
Callback arrays participate in $extends merging. If an extending fragment defines a new callback group, it is added. If it defines the same callback group as the base style, that group is replaced.
Fragment Lifecycle
For CollectionView cells, DynamicCell manages fragment lifecycle:
- Choose the style key from the row, loading item, or item-level
cellSettingsKey. - Release the previous fragment and run
onUnmountif the style or row container changed. - Get a fragment from
ViewFragmentProvider, reusing a pooled fragment when available. - Append the fragment view to the row container and run
onMount. - Apply the data map from the current item and run
onDataChange. - Apply the current view status and run
onApplyViewStatuswhen the status changes.
For FragmentView, the host view owns the fragment lifecycle but uses the same provider, status application, data map, constraints, and callback system.
Fragment Provider Behavior
ViewFragmentProvider is responsible for:
- Creating fragment SG groups from
views.base. - Applying fonts from
fontKeyorfontStyleKeybefore child nodes are created. - Scaling
maskSizeby device resolution when a fragment is first resolved. - Assigning the fragment ID from the style
id, or generatingfragment_#when no ID is provided. - Pooling released fragments by
styleKeyfor reuse. - Tracking all child nodes in
viewsById. - Tracking data-bound child nodes in
dataMapViewsById. - Resolving callback names into functions from the callback registry.
- Tracking fragments for debugger and cleanup workflows.
Keep fragment styles deterministic. Do not rely on fragment creation order, generated IDs, or callback side effects for basic field values that can be represented directly in AppConfig.