@onekeyfe/react-native-native-list 6.7.0-alpha.375 → 6.7.0-alpha.382
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/onekey/nativelist/HybridNativeList.kt +14 -1
- package/android/src/main/java/com/onekey/nativelist/NativeListAccessoryStack.kt +240 -11
- package/android/src/main/java/com/onekey/nativelist/NativeListActivityDetailsView.kt +7 -4
- package/android/src/main/java/com/onekey/nativelist/NativeListActivityRenderer.kt +6 -3
- package/android/src/main/java/com/onekey/nativelist/NativeListButtonGroupRenderer.kt +102 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListContainerSlotAdapter.kt +12 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListDataRowRenderer.kt +87 -7
- package/android/src/main/java/com/onekey/nativelist/NativeListIdentityRenderer.kt +177 -1
- package/android/src/main/java/com/onekey/nativelist/NativeListLeadingVisual.kt +43 -14
- package/android/src/main/java/com/onekey/nativelist/NativeListMessageRenderer.kt +25 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListModels.kt +33 -2
- package/android/src/main/java/com/onekey/nativelist/NativeListPrimitives.kt +22 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListRendererRegistry.kt +2 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListRendererRowView.kt +2 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListResolvedText.kt +3 -2
- package/android/src/main/java/com/onekey/nativelist/NativeListSectionHeaderRenderer.kt +69 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListTextView.kt +55 -1
- package/android/src/main/java/com/onekey/nativelist/NativeListView.kt +1912 -107
- package/android/src/test/java/com/margelo/nitro/nativelist/NativeListPressPolicyTest.kt +1 -0
- package/docs/ROW_TEMPLATES.md +60 -9
- package/docs/SPEC.md +450 -5
- package/docs/STYLE_SPEC.md +6 -0
- package/ios/HybridNativeList.swift +21 -2
- package/ios/NativeListAccessoryStack.swift +227 -39
- package/ios/NativeListActivityDetailsView.swift +18 -8
- package/ios/NativeListActivityRenderer.swift +8 -2
- package/ios/NativeListButtonGroupRenderer.swift +89 -0
- package/ios/NativeListDataRowRenderer.swift +155 -27
- package/ios/NativeListDesignAssets.swift +3 -0
- package/ios/NativeListIdentityRenderer.swift +166 -30
- package/ios/NativeListLeadingVisual.swift +49 -14
- package/ios/NativeListMessageRenderer.swift +34 -1
- package/ios/NativeListModels.swift +42 -2
- package/ios/NativeListRendererRegistry.swift +1 -0
- package/ios/NativeListResolvedText.swift +2 -1
- package/ios/NativeListRowHost.swift +14 -0
- package/ios/NativeListSectionHeaderRenderer.swift +88 -0
- package/ios/RNCNativeListView.swift +1013 -55
- package/ios/Resources/NativeListIcons.xcassets/onekey_crypto_coin_outline.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_crypto_coin_outline.imageset/icon.svg +4 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_gas_solid.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_gas_solid.imageset/icon.svg +3 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_refresh_ccw_outline.imageset/Contents.json +15 -0
- package/ios/Resources/NativeListIcons.xcassets/onekey_refresh_ccw_outline.imageset/icon.svg +3 -0
- package/ios/tests/NativeListActivityPresentationTests.swift +56 -0
- package/ios/tests/NativeListLifecycleTests.swift +2720 -3
- package/lib/module/NativeList.js +175 -28
- package/lib/module/NativeList.web.js +129 -30
- package/lib/module/index.js +1 -1
- package/lib/module/models.js +1 -0
- package/lib/module/selection.js +1 -1
- package/lib/module/validation.js +421 -8
- package/lib/module/web/NativeListWebEngine.js +507 -19
- package/lib/module/web/templates/ActivityRowRenderer.js +8 -7
- package/lib/module/web/templates/ButtonGroupRowRenderer.js +47 -0
- package/lib/module/web/templates/DataRowRenderer.js +40 -5
- package/lib/module/web/templates/IdentityRowRenderer.js +90 -4
- package/lib/module/web/templates/MessageRowRenderer.js +18 -2
- package/lib/module/web/templates/RowContainerStyle.js +1 -0
- package/lib/module/web/templates/RowRendererRegistry.js +3 -0
- package/lib/module/web/templates/SectionHeaderRowRenderer.js +36 -2
- package/lib/nitrogen/generated/android/c++/JHybridNativeListSpec.cpp +26 -0
- package/lib/nitrogen/generated/android/c++/JHybridNativeListSpec.hpp +4 -0
- package/lib/nitrogen/generated/android/c++/views/JHybridNativeListStateUpdater.cpp +10 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/HybridNativeListSpec.kt +20 -0
- package/lib/nitrogen/generated/ios/c++/HybridNativeListSpecSwift.hpp +14 -0
- package/lib/nitrogen/generated/ios/c++/views/HybridNativeListComponent.mm +12 -0
- package/lib/nitrogen/generated/ios/swift/HybridNativeListSpec.swift +2 -0
- package/lib/nitrogen/generated/ios/swift/HybridNativeListSpec_cxx.swift +43 -0
- package/lib/nitrogen/generated/shared/c++/HybridNativeListSpec.cpp +4 -0
- package/lib/nitrogen/generated/shared/c++/HybridNativeListSpec.hpp +4 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridNativeListComponent.cpp +4 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridNativeListComponent.hpp +6 -0
- package/lib/nitrogen/generated/shared/json/NativeListConfig.json +2 -0
- package/lib/typescript/src/NativeList.d.ts +1 -1
- package/lib/typescript/src/NativeList.nitro.d.ts +2 -0
- package/lib/typescript/src/NativeList.types.d.ts +18 -0
- package/lib/typescript/src/index.d.ts +2 -2
- package/lib/typescript/src/models.d.ts +105 -9
- package/lib/typescript/src/validation.d.ts +12 -0
- package/lib/typescript/src/web/NativeListWebEngine.d.ts +32 -0
- package/lib/typescript/src/web/templates/ButtonGroupRowRenderer.d.ts +14 -0
- package/lib/typescript/src/web/templates/IdentityRowRenderer.d.ts +1 -1
- package/lib/typescript/src/web/templates/MessageRowRenderer.d.ts +9 -3
- package/nitrogen/generated/android/c++/JHybridNativeListSpec.cpp +26 -0
- package/nitrogen/generated/android/c++/JHybridNativeListSpec.hpp +4 -0
- package/nitrogen/generated/android/c++/views/JHybridNativeListStateUpdater.cpp +10 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nativelist/HybridNativeListSpec.kt +20 -0
- package/nitrogen/generated/ios/c++/HybridNativeListSpecSwift.hpp +14 -0
- package/nitrogen/generated/ios/c++/views/HybridNativeListComponent.mm +12 -0
- package/nitrogen/generated/ios/swift/HybridNativeListSpec.swift +2 -0
- package/nitrogen/generated/ios/swift/HybridNativeListSpec_cxx.swift +43 -0
- package/nitrogen/generated/shared/c++/HybridNativeListSpec.cpp +4 -0
- package/nitrogen/generated/shared/c++/HybridNativeListSpec.hpp +4 -0
- package/nitrogen/generated/shared/c++/views/HybridNativeListComponent.cpp +4 -0
- package/nitrogen/generated/shared/c++/views/HybridNativeListComponent.hpp +6 -0
- package/nitrogen/generated/shared/json/NativeListConfig.json +2 -0
- package/package.json +3 -3
- package/react-native-native-list.podspec +1 -0
- package/src/NativeList.nitro.ts +2 -0
- package/src/NativeList.tsx +313 -44
- package/src/NativeList.types.ts +22 -0
- package/src/NativeList.web.tsx +204 -48
- package/src/index.ts +2 -0
- package/src/models.ts +117 -3
- package/src/selection.ts +1 -0
- package/src/validation.ts +852 -6
- package/src/web/NativeListWebEngine.ts +803 -20
- package/src/web/templates/ActivityRowRenderer.ts +14 -6
- package/src/web/templates/ButtonGroupRowRenderer.ts +58 -0
- package/src/web/templates/DataRowRenderer.ts +93 -20
- package/src/web/templates/IdentityRowRenderer.ts +150 -5
- package/src/web/templates/MessageRowRenderer.ts +23 -7
- package/src/web/templates/RowContainerStyle.ts +2 -0
- package/src/web/templates/RowRendererRegistry.ts +11 -1
- package/src/web/templates/SectionHeaderRowRenderer.ts +47 -4
package/docs/SPEC.md
CHANGED
|
@@ -26,6 +26,232 @@ prevention; there is no automatic fitting or shrinking pass.
|
|
|
26
26
|
|
|
27
27
|
## Public API and defaults
|
|
28
28
|
|
|
29
|
+
### Recipient identity and inline warning rows
|
|
30
|
+
|
|
31
|
+
Status: implemented in local source for the Select Address and Swap selectors;
|
|
32
|
+
native device and Web runtime verification remain pending. These fields travel in snapshot/patch JSON
|
|
33
|
+
and do not change the Nitro ABI.
|
|
34
|
+
|
|
35
|
+
An `identity` row may opt into `subtitleLines: 0`, meaning the complete subtitle
|
|
36
|
+
wraps at its measured text-column width without a line cap. Existing omitted,
|
|
37
|
+
one-line and two-line behavior stays unchanged. An identity row with
|
|
38
|
+
`contentHeight: 'auto'` measures its actual title, subtitle, leading visual,
|
|
39
|
+
badges and trailing accessories and grows to fit; this is opt-in and does not
|
|
40
|
+
alter selector or wallet-sidebar presets. An explicit `style.container.height`
|
|
41
|
+
or `row.height` still wins over content measurement, in that order. Content
|
|
42
|
+
height changes with row width, text, style or configured font and may not retain
|
|
43
|
+
a stale prior width's measurement. The existing 4096 UTF-16-code-unit bound per
|
|
44
|
+
text field still applies; zero is not an unlimited data-size allowance.
|
|
45
|
+
|
|
46
|
+
An `identity` row may set `longPressActionKey` to a nonempty action key. A valid
|
|
47
|
+
touch long press on an enabled row emits one `onRowAction` with its current row key,
|
|
48
|
+
action key, `row` anchor source and window press point. An accessibility long-click
|
|
49
|
+
without a physical touch retains the row anchor without a press point. It suppresses the
|
|
50
|
+
ordinary press for that gesture. Motion cancellation, row rebind/recycle,
|
|
51
|
+
disabled state and disposal cancel pending recognition. Trailing accessory
|
|
52
|
+
actions remain independent. Omission preserves existing tap behavior.
|
|
53
|
+
|
|
54
|
+
A `message` row may opt into `bodyLines: 0`; its complete body wraps within its
|
|
55
|
+
existing text area and determines natural height unless an explicit row or
|
|
56
|
+
container height overrides it. Existing omitted and one-to-three-line behavior
|
|
57
|
+
stays unchanged. `style.body.lines`, when supplied, retains its existing
|
|
58
|
+
one-to-three-line precedence. `presentation: 'inlineWarning'` changes only the
|
|
59
|
+
message container border: requested `style.container.borderWidth` and
|
|
60
|
+
`borderColor` draw top and bottom edges at full row width, with no side edges or
|
|
61
|
+
corner radius. Omitted border width draws no border; omitted presentation keeps
|
|
62
|
+
the existing all-edge container style. The caller supplies warning text, icon,
|
|
63
|
+
font, padding, colors and list-width layout; the SDK does not infer risk status.
|
|
64
|
+
All optional fields must clear on rebind/recycle and patch replacement. The
|
|
65
|
+
existing 4096-code-unit body limit applies.
|
|
66
|
+
|
|
67
|
+
An image-backed `LeadingVisual` may set `fallbackTextColor` to a hex
|
|
68
|
+
`#RRGGBB` or `#RRGGBBAA` color. It colors only the visible initials/fallback
|
|
69
|
+
text, including a failed image's fallback state; omission retains the existing
|
|
70
|
+
platform default. It does not alter the visual background, bitmap, icon, or
|
|
71
|
+
overlay colors. Validation rejects malformed colors. Rebind and recycle must
|
|
72
|
+
clear the previous text color so dark/light theme updates cannot leak between
|
|
73
|
+
rows. Consumers own the semantic theme choice (for example inverse text over
|
|
74
|
+
inverse background).
|
|
75
|
+
|
|
76
|
+
iOS measures opt-in identity and unlimited message content before collection
|
|
77
|
+
placement. Android binds text with unlimited native lines and allows its row
|
|
78
|
+
view to measure that content. Web uses wrapping DOM text and measured row
|
|
79
|
+
height. All platforms retain existing explicit-height precedence and existing
|
|
80
|
+
default rows. Focused source tests must cover validation, patching, old-to-new
|
|
81
|
+
reuse, action isolation and height changing with width/text; real iOS, Android
|
|
82
|
+
and Web acceptance must cover narrow addresses, dynamic type/fonts, long press
|
|
83
|
+
versus tap, keyboard interaction, and the horizontal warning border. Source or
|
|
84
|
+
build success alone is not runtime evidence.
|
|
85
|
+
|
|
86
|
+
### DeFi position row controls
|
|
87
|
+
|
|
88
|
+
Status: implemented in local source, unreleased. Focused validation and Web
|
|
89
|
+
DOM tests pass; native source compilation passes, while target-app compilation
|
|
90
|
+
and device acceptance remain separate gates.
|
|
91
|
+
`buttonGroup` is a vertical data row with `RowBase` identity and a nonempty
|
|
92
|
+
`buttons` array. Each button has a unique nonempty `key`, localized nonempty
|
|
93
|
+
`label`, nonempty `actionKey`, and optional `disabled`, `loading`, `testID` and
|
|
94
|
+
`accessibilityLabel`. It renders the existing DeFi medium block info-outline
|
|
95
|
+
controls: equal-width buttons, 10-point gap, 12-point vertical padding and
|
|
96
|
+
12-point radius. Every declared action remains visible; the renderer must not
|
|
97
|
+
truncate the array. Loading retains the button's width, disables that button,
|
|
98
|
+
and displays an activity indicator. A disabled row disables every button.
|
|
99
|
+
Activation emits one existing row action with the button's `actionKey` and
|
|
100
|
+
its own source/slot, without activating the row. A full rebind or recycle
|
|
101
|
+
removes prior buttons, loading indicators, labels, test IDs and handlers.
|
|
102
|
+
The SDK does not infer which DeFi actions are allowed or perform transactions.
|
|
103
|
+
|
|
104
|
+
`sectionHeader` may include `leadingBadge` (the existing badge shape),
|
|
105
|
+
with an optional `NativeListTheme.successBackground` used only for its
|
|
106
|
+
`success` tone. Omitting that theme color keeps the old badge default.
|
|
107
|
+
`style.valuePlacement: 'afterTitle'`, and
|
|
108
|
+
`style.titleDecoration: 'none' | 'dashed'`. Missing fields retain the current
|
|
109
|
+
heading layout and decoration. `valueHelp` and a `valuePair` trailing
|
|
110
|
+
accessory's `primaryHelp` share
|
|
111
|
+
`{ actionKey, presentation: 'tooltip' | 'popover', underline?: 'dashed' }`.
|
|
112
|
+
Presentation and underline are independent: the former selects trigger
|
|
113
|
+
semantics, the latter only draws a dashed underline. `tooltip` on Web opens on
|
|
114
|
+
mouse/pen entry and eligible focus, closes on departure/blur, and mouse/pen
|
|
115
|
+
press closes it until departure; touch toggles. It has no native touch target
|
|
116
|
+
or event. `popover` on all platforms toggles only on click/press and does not
|
|
117
|
+
open from hover. These help targets do not activate their row. Presentation
|
|
118
|
+
change, departure, scroll, rebind, replacement by another anchor and disposal
|
|
119
|
+
invalidate the old anchor.
|
|
120
|
+
On Web, the value-pair primary help target is keyboard focusable; focus opens
|
|
121
|
+
tooltip help and Enter/Space activates popover help without pressing the row.
|
|
122
|
+
The consuming app owns Tooltip/Popover content. Missing help fields keep
|
|
123
|
+
the original noninteractive text. All additions use snapshot JSON and do not
|
|
124
|
+
change the Nitro ABI.
|
|
125
|
+
|
|
126
|
+
`valuePair.primaryFontWeight?: 'medium'` applies only to the primary text
|
|
127
|
+
line. The secondary line keeps its existing regular weight. Omitting the field
|
|
128
|
+
preserves each platform's existing value-pair typography; full rebind and
|
|
129
|
+
recycle remove any previous primary-only override. The field travels in the
|
|
130
|
+
existing snapshot JSON and does not change the Nitro ABI.
|
|
131
|
+
|
|
132
|
+
Validation rejects empty or duplicate button keys, empty labels/action keys,
|
|
133
|
+
invalid booleans and help descriptors, and unknown style fields. The list's
|
|
134
|
+
existing row, text and snapshot bounds apply; no
|
|
135
|
+
silent action truncation is permitted. Conformance includes Web DOM events,
|
|
136
|
+
iOS/Android renderer bind/reuse, TypeScript validation, native builds, and
|
|
137
|
+
later real UI checks for help anchors, loading, disabled actions and geometry.
|
|
138
|
+
|
|
139
|
+
### Segmented value-pair accessory
|
|
140
|
+
|
|
141
|
+
Status: implemented in local source, unreleased. Focused validation and Web DOM
|
|
142
|
+
tests pass; production iOS target compilation passes. Native instrumentation,
|
|
143
|
+
full App builds and device acceptance remain separate gates. A `valuePair`
|
|
144
|
+
trailing accessory retains its required `primary` and `secondary` fallback
|
|
145
|
+
strings. It may also supply optional readonly `primaryTextSegments` and
|
|
146
|
+
`secondaryTextSegments` arrays of existing `ValueTextSegment` objects. Missing
|
|
147
|
+
or empty arrays display the corresponding fallback; nonempty arrays display
|
|
148
|
+
their text runs. Callers provide matching fallback text, but validation does
|
|
149
|
+
not enforce that match.
|
|
150
|
+
|
|
151
|
+
Each array has at most 64 segments, each with string `text` of at most 4096
|
|
152
|
+
characters and optional `style: 'subscript'`. Total text per array is at most
|
|
153
|
+
4096 characters. Full snapshots and explicit patch replacements reject null,
|
|
154
|
+
malformed arrays, unknown segment keys and other styles. An omitted patch field
|
|
155
|
+
leaves the existing accessory unchanged; replacement without segments clears
|
|
156
|
+
old runs. Subscript retains the existing 60-percent smaller-font convention
|
|
157
|
+
after semantic text styling. Both lines retain their default size, tone, line
|
|
158
|
+
height, right alignment and accessibility text. These fields add no action or
|
|
159
|
+
hit target and travel in existing snapshot JSON without a Nitro ABI change.
|
|
160
|
+
|
|
161
|
+
### Identity row risk-management controls
|
|
162
|
+
|
|
163
|
+
Status: implemented in source for local consumers, unreleased. Focused validation,
|
|
164
|
+
Web DOM tests, native builds and device acceptance are tracked separately below.
|
|
165
|
+
The optional `identity.titleIcon` is a static, non-actionable icon after the
|
|
166
|
+
title: a 20-point glyph with a 4-point gap, optional tint and accessibility
|
|
167
|
+
label. Its optional Web-only `hoverActionKey` emits an anchored help action
|
|
168
|
+
from the icon alone on mouse/pen hover or touch press; the consumer owns the
|
|
169
|
+
tooltip. Mouse/pen press closes the anchor until pointer leave. Touch press
|
|
170
|
+
toggles the icon anchor. The icon adds no tab stop or separate click action;
|
|
171
|
+
row activation remains unchanged. Departure, scroll, full rebind and destroy
|
|
172
|
+
invalidate the anchor. Omission removes the icon in a full snapshot; omission
|
|
173
|
+
in a patch leaves the existing value unchanged. An explicit replacement icon
|
|
174
|
+
without `hoverActionKey` clears the old hover action. Removing the icon
|
|
175
|
+
requires a full snapshot.
|
|
176
|
+
|
|
177
|
+
On Web, `identity.secondaryTitleIcon` accepts the same descriptor and appears
|
|
178
|
+
after `titleIcon`. Both icons retain independent hover/touch anchors at their
|
|
179
|
+
own glyph rectangles; entering the other invalidates the previous anchor.
|
|
180
|
+
Native renderers ignore this optional Web-only field. Full rebinding to one
|
|
181
|
+
or no icons clears the removed glyph and anchor; explicit icon replacement
|
|
182
|
+
without `hoverActionKey` clears that icon's old hover action. Neither icon
|
|
183
|
+
creates a tab stop or separate click action.
|
|
184
|
+
On Web, clicking either help icon (including its SVG) does not activate the row;
|
|
185
|
+
clicking elsewhere on the row keeps the normal row action.
|
|
186
|
+
|
|
187
|
+
`identity.style.leadingContentMaxWidthRatio`, when given,
|
|
188
|
+
must be finite and within `(0, 1]`; it caps the combined leading visual, gap
|
|
189
|
+
and title content relative to the row's available inner width. Omission keeps
|
|
190
|
+
the existing unrestricted layout. Long titles truncate inside the cap, and
|
|
191
|
+
unused width remains available to trailing controls. The ratio is layout-only,
|
|
192
|
+
never a reuse identity or row-height input.
|
|
193
|
+
|
|
194
|
+
`trailing` accepts an optional `button` accessory with required nonempty
|
|
195
|
+
`label` and `actionKey`, optional `disabled`, `testID`,
|
|
196
|
+
`accessibilityLabel`, and optional hex colors `textColor`,
|
|
197
|
+
`backgroundColor`, `pressedBackgroundColor`, `hoverBackgroundColor`, and
|
|
198
|
+
`focusRingColor`. This single variant has the existing small secondary button
|
|
199
|
+
geometry: medium 14/20 text, 10-point horizontal and 4-point vertical
|
|
200
|
+
padding, a one-point transparent border and a pill radius. Omitted colors use
|
|
201
|
+
the theme's corresponding text and strong-background colors. The button does
|
|
202
|
+
not carry arbitrary React content, a size or variant parameter, or loading
|
|
203
|
+
state. Row or accessory disabled state prevents its action and dims the control
|
|
204
|
+
to 0.4 opacity.
|
|
205
|
+
|
|
206
|
+
Activation emits one existing row-action event with the current row key,
|
|
207
|
+
`trailingAccessory` source and zero-based descriptor slot, without also activating the row.
|
|
208
|
+
The host's existing binding epoch guards recycled controls. Full rebinding
|
|
209
|
+
clears omitted title icons and old button text, visual states, test IDs,
|
|
210
|
+
accessibility values and callbacks, including button-to-icon-to-absent reuse.
|
|
211
|
+
Patches replace fields they explicitly change and leave omitted fields intact.
|
|
212
|
+
iOS and Android use native controls; Web uses a semantic `button` with native
|
|
213
|
+
keyboard activation and disabled semantics. The new fields travel inside the
|
|
214
|
+
existing snapshot JSON and do not alter the Nitro bridge ABI. Consumers own
|
|
215
|
+
localized labels, state updates and any tooltip outside the data row.
|
|
216
|
+
|
|
217
|
+
Acceptance requires snapshot and patch validation, Web action/isolation and
|
|
218
|
+
reuse checks, native renderer tests on production classes, native builds and
|
|
219
|
+
device checks for title placement, cap/truncation, colors, press and recycle.
|
|
220
|
+
No source-only check counts as native runtime evidence.
|
|
221
|
+
|
|
222
|
+
`theme.rowHoverBackground` is an optional Web-only hex color for an enabled
|
|
223
|
+
row's hover background. Omission keeps the existing pressed-background hover
|
|
224
|
+
color. Pressed/active rows continue to use `rowPressedBackground`; native
|
|
225
|
+
renderers ignore the new field. The existing `loadingStrategy: 'skeleton'`
|
|
226
|
+
on a Web leading image displays an animated skeleton while loading, then
|
|
227
|
+
clears it on success, failure, rebind or recycle; other strategies keep their
|
|
228
|
+
existing appearance.
|
|
229
|
+
|
|
230
|
+
Identity leading visuals consume the existing `borderColor` as a one-point/dp
|
|
231
|
+
solid bitmap border when supplied without `borderStyle: 'dashed'`; omission or
|
|
232
|
+
a dashed style clears the solid border on every bind.
|
|
233
|
+
The image is inset by that border width. An overlay may optionally set
|
|
234
|
+
`fontWeight` to one of the existing text weight names
|
|
235
|
+
(`regular | medium | semibold | bold`). This changes only overlay text;
|
|
236
|
+
omission retains the current platform default. Setting and clearing the field
|
|
237
|
+
must survive patching and recycled-view rebinding on all three platforms.
|
|
238
|
+
The existing `leading.fallbackIcon` may optionally set a positive `size` for
|
|
239
|
+
its glyph and a hex `backgroundColor` for the visual's no-image and image-load-
|
|
240
|
+
failure states. A successfully loaded bitmap continues to use
|
|
241
|
+
`leading.backgroundColor`. Omitted fallback fields preserve current rendering.
|
|
242
|
+
Image success, failure and full rebinding must restore the matching background
|
|
243
|
+
and glyph size without carrying values from the previous visual.
|
|
244
|
+
An image overlay may optionally declare `fallbackIcon` with a required glyph
|
|
245
|
+
name and optional tint. It shows only after that overlay image fails, while
|
|
246
|
+
loading retains the overlay's empty background and success shows the bitmap.
|
|
247
|
+
The existing icon accessory accepts only one optional `buttonStyle` value,
|
|
248
|
+
`tertiary`, for the source icon button's 38-point press frame, 24-point glyph,
|
|
249
|
+
six-point padding, one-point border, seven-point visual overhang and pressed
|
|
250
|
+
background. Omission preserves every existing icon accessory layout and action.
|
|
251
|
+
The non-action `RefreshCcwOutline` accessory keeps the source's 16-point glyph
|
|
252
|
+
and frame, with no independent press target. Rebinding it to an actionable
|
|
253
|
+
icon restores the existing icon size and event behavior.
|
|
254
|
+
|
|
29
255
|
### iOS 26 fixed top scroll-edge overlay
|
|
30
256
|
|
|
31
257
|
Standalone `NativeList` accepts optional
|
|
@@ -192,6 +418,16 @@ section index titles retain their separate development-only
|
|
|
192
418
|
no-surrounding-whitespace assertion.
|
|
193
419
|
The 256-code-unit key limit is a development assertion, not a production limit.
|
|
194
420
|
|
|
421
|
+
Each iOS and Android host retains an index from accepted logical row keys to
|
|
422
|
+
row positions. An accepted complete snapshot reuses that index only when its
|
|
423
|
+
generation and ordered `(key, type)` sequence match the prior accepted
|
|
424
|
+
snapshot; otherwise it replaces the index. Reorder commits rebuild it and
|
|
425
|
+
disposal clears it. Patch batches reuse the index and never include retiring
|
|
426
|
+
presentation rows in it. They validate the whole batch against the current
|
|
427
|
+
accepted row models before changing config or presentation; invalid batches
|
|
428
|
+
leave both unchanged. iOS updates only changed accepted entries in its
|
|
429
|
+
presentation lookup after a valid patch, preserving any retiring rows.
|
|
430
|
+
|
|
195
431
|
### Validation environments and rejected input
|
|
196
432
|
|
|
197
433
|
Development builds (`__DEV__ === true`) run the complete snapshot and patch
|
|
@@ -208,6 +444,34 @@ arguments; this protects the runtime's argument limit.
|
|
|
208
444
|
The component and Web engine contain input validation failures in both modes:
|
|
209
445
|
initial invalid input renders an empty list; rejected later snapshots or patch
|
|
210
446
|
batches preserve the last accepted state and are not dispatched to native.
|
|
447
|
+
On iOS and Android, the React wrapper captures caller data before validation
|
|
448
|
+
and owns an immutable accepted snapshot. Later caller mutation cannot rewrite
|
|
449
|
+
that accepted state. Imperative selection keys are captured on entry. Eligible
|
|
450
|
+
value-only `identity` and `dataRow` patches validate their complete changed
|
|
451
|
+
rows; other patches and snapshots without this private ownership use the
|
|
452
|
+
existing complete-snapshot validation path. This does not change exported
|
|
453
|
+
validators or Web behavior.
|
|
454
|
+
An `undefined` value in an eligible field stays on the changed-row path only
|
|
455
|
+
when that field was already `undefined`; clearing a present value uses the
|
|
456
|
+
complete-snapshot path.
|
|
457
|
+
|
|
458
|
+
`planNativeListPatches(previous, next)` is an explicit, pure public helper for
|
|
459
|
+
callers that own two valid, immutable snapshots. It returns a readonly empty
|
|
460
|
+
batch for equal snapshots, value-only `identity` or `dataRow` row patches when
|
|
461
|
+
only eligible values change, and `undefined` when the caller must deliver a
|
|
462
|
+
complete snapshot. It never dispatches, accepts or validates an update, and
|
|
463
|
+
does not deep-copy, freeze or cache caller input. Returned patches may refer
|
|
464
|
+
to `next`'s immutable values; the caller retains baseline and commit ownership.
|
|
465
|
+
The component does not automatically invoke this planner for prop snapshots. Any
|
|
466
|
+
change outside `rows`, including generation, selection, layout, list style, empty state or
|
|
467
|
+
footer, requires a complete snapshot. Row keys, order and types must match.
|
|
468
|
+
`identity` may patch `subtitle`, `subtitleSegments` and `trailing`; `dataRow`
|
|
469
|
+
may patch non-asset columns and `trailing` while preserving the first asset
|
|
470
|
+
column. Changes to other row fields or to the presence of optional patch
|
|
471
|
+
fields require a complete snapshot. Comparison is by JSON value, ignores
|
|
472
|
+
object property order and properties whose value is `undefined`, and keeps
|
|
473
|
+
array order significant.
|
|
474
|
+
|
|
211
475
|
Rejection emits a diagnostic without including row keys, text or image URLs.
|
|
212
476
|
Explicit calls to the exported validation functions still throw on rejected
|
|
213
477
|
input, so development tooling can inspect the full contract failure. Rejection
|
|
@@ -721,6 +985,49 @@ leaked audio/player resources. Static API checks do not prove these cases.
|
|
|
721
985
|
|
|
722
986
|
## Native container slots (source implementation, runtime verification pending)
|
|
723
987
|
|
|
988
|
+
### Optional scrolling sidecar (source implemented; native runtime verification pending)
|
|
989
|
+
|
|
990
|
+
`listSidecar` and `sidecarLayout: { width, gap, maxContentWidth }` are an
|
|
991
|
+
optional pair for a vertical linear list. Omission preserves the existing
|
|
992
|
+
single-column layout. All three values are finite non-negative logical pixels;
|
|
993
|
+
`width` and `maxContentWidth` must be positive and `width + gap` must be less
|
|
994
|
+
than `maxContentWidth`. The list remains the sole scroll owner. At viewport
|
|
995
|
+
width `V`, the content band is centered with width `min(V, maxContentWidth)`;
|
|
996
|
+
the sidecar occupies the logical end `width`, separated from the logical start
|
|
997
|
+
data/slot column by `gap`. In RTL the sidecar is physically left and the list
|
|
998
|
+
is physically right. Header, rows, empty slot and footer use the start column. The sidecar
|
|
999
|
+
starts at the content top, scrolls on the same native frame as rows, and may
|
|
1000
|
+
contain interactive React content. Its root is retained until Fabric unmount;
|
|
1001
|
+
it does not become a row, affect row keys, indices, selection or prefetch. The
|
|
1002
|
+
left RecyclerView/UICollectionView still lays out only viewport-near rows.
|
|
1003
|
+
|
|
1004
|
+
The scrolling extent is at least the greater of the left column including its
|
|
1005
|
+
footer and the sidecar's measured bottom. A taller sidecar extends the tail
|
|
1006
|
+
after the left footer; it must not push that footer below its normal position.
|
|
1007
|
+
`scrollToEnd`, refresh and structural transition offset clamping use this
|
|
1008
|
+
combined extent. A sidecar height or breakpoint change preserves an in-range
|
|
1009
|
+
offset; an out-of-range offset is clamped by the existing shrink correction.
|
|
1010
|
+
Rapid row transitions retain their existing one-shot id and anchor semantics.
|
|
1011
|
+
Native sidecar position follows the native scroll offset directly, without a
|
|
1012
|
+
per-frame JS bridge. A drag beginning over interactive sidecar content must
|
|
1013
|
+
still scroll the same list; taps retain the content's own actions.
|
|
1014
|
+
On iOS, React container slots are clipped below the enclosing Pager's fixed overlay and sticky region using its existing owner-scoped native viewport callback. Scrolling, rotation, owner removal, and recycling update or release those masks without JS frames. On iOS the sidecar host is a narrow UICollectionView subview in content
|
|
1015
|
+
coordinates. On Android the narrow overlay follows the RecyclerView's measured
|
|
1016
|
+
logical offset; after a vertical touch slop it cancels the React press and
|
|
1017
|
+
forwards the original touch stream to the list's SwipeRefreshLayout. The
|
|
1018
|
+
Android tail spacer derives from explicit/measured row and slot extents, not
|
|
1019
|
+
RecyclerView's variable-height scrollbar estimate.
|
|
1020
|
+
|
|
1021
|
+
Web uses the same measured two-column geometry inside its current owned or
|
|
1022
|
+
explicit external scroll host, with DOM virtualization limited to the left
|
|
1023
|
+
rows; the sidecar is a single React slot. Callers already placing a Web sidebar
|
|
1024
|
+
as a sibling may omit the new pair there. Horizontal/grid configurations with
|
|
1025
|
+
a sidecar are rejected. Acceptance requires iOS and Android wide Home scrolling,
|
|
1026
|
+
right-column drag and action, short/empty assets, collapse animation while
|
|
1027
|
+
scrolled, refresh, resize across the breakpoint without list remount, and
|
|
1028
|
+
thousands of rows remaining bounded to the native recycler viewport. Source,
|
|
1029
|
+
type and unit checks alone do not establish device acceptance.
|
|
1030
|
+
|
|
724
1031
|
`listHeader`, `listEmpty` and `listFooter` accept React nodes on iOS and Android
|
|
725
1032
|
vertical lists only. They are optional and default to absent. They are common
|
|
726
1033
|
container capabilities, not row templates, and MUST NOT be used for per-item
|
|
@@ -747,9 +1054,10 @@ width React lays it out at. Android's narrow-screen row scale and section-index
|
|
|
747
1054
|
apply to data rows only. Slot React trees own their internal layout and
|
|
748
1055
|
business actions; NativeList owns their scroll placement and lifecycle.
|
|
749
1056
|
|
|
750
|
-
The wrapper mounts
|
|
751
|
-
heights through `containerSlotHeightsJson` in header/empty/footer order.
|
|
752
|
-
|
|
1057
|
+
The wrapper mounts four non-collapsible slot roots and reports measured
|
|
1058
|
+
heights through `containerSlotHeightsJson` in header/empty/footer/sidecar order.
|
|
1059
|
+
The sidecar root has zero reserved height when absent. Native validates four
|
|
1060
|
+
finite, non-negative heights. Slot updates MUST NOT alter data-row
|
|
753
1061
|
keys or public row indices. No per-scroll-frame JS position updates are used.
|
|
754
1062
|
iOS mounts each slot root inside a list-owned host view in UICollectionView and
|
|
755
1063
|
reserves native layout space. The host owns the slot's vertical position and clips to
|
|
@@ -760,6 +1068,102 @@ data adapter; adapter binding indices stay local while layout-manager positions
|
|
|
760
1068
|
are explicitly translated. Slots span all grid columns, do not select/reorder,
|
|
761
1069
|
and do not emit row actions, visible-row indices, or image-prefetch work.
|
|
762
1070
|
|
|
1071
|
+
### Disclosure slots and settlement (source implemented; limited runtime verification)
|
|
1072
|
+
|
|
1073
|
+
Native callers may opt in with `snapshot.disclosure: { expanded: boolean,
|
|
1074
|
+
expandedTrailingInset?: number }`, `listExpandedContent`, and
|
|
1075
|
+
`listStructureToggle`. `expanded` is the accepted target state independent of
|
|
1076
|
+
row count, the expanded slot's measured height, and the one-shot structural
|
|
1077
|
+
transition command. `expandedTrailingInset` defaults to zero and must be a
|
|
1078
|
+
finite, non-negative number of logical pixels. Without `disclosure`, the
|
|
1079
|
+
existing four-slot and structural-transition behavior is unchanged. On Web,
|
|
1080
|
+
`C` and `T` occupy the existing single React footer band after the list rows,
|
|
1081
|
+
followed by the expanded trailing inset and the caller's footer `F`. `C` is
|
|
1082
|
+
mounted on first expansion and may remain mounted for that scope after collapse.
|
|
1083
|
+
Without an accepted `disclosure`, supplied `C` and `T` are hidden and inert.
|
|
1084
|
+
At the collapsed endpoint `C` has zero layout height and is hidden from pointer,
|
|
1085
|
+
keyboard, and accessibility interaction; `T` remains mounted and interactive
|
|
1086
|
+
through rapid reversals. Web does not use the native settlement callback to
|
|
1087
|
+
release `C`. The Web engine continues to measure that one footer band and to
|
|
1088
|
+
own its existing structural-tail animation and external scroll-host correction;
|
|
1089
|
+
its per-frame presentation need not match the native clip geometry. Desktop
|
|
1090
|
+
and extension retain their existing structural presentation.
|
|
1091
|
+
|
|
1092
|
+
The native slot order is 0 header, 1 empty, 2 existing footer `F`, 3 sidecar,
|
|
1093
|
+
4 expanded content `C`, and 5 stable structure toggle `T`; the optional iOS
|
|
1094
|
+
top overlay follows at index 6. The native wrapper reports six measured slot
|
|
1095
|
+
heights in that order. This slot-index ABI requires the package's JavaScript
|
|
1096
|
+
wrapper and native binary to be integrated from the same version; the new
|
|
1097
|
+
native host does not reinterpret an old four-slot wrapper's index 4 as an
|
|
1098
|
+
overlay. The disclosure layout in the left list column is data
|
|
1099
|
+
rows `R`, then `C`, then `T`, then a native trailing inset `I`, then the one
|
|
1100
|
+
existing `F`. `F` is never duplicated as retiring presentation. `T` retains
|
|
1101
|
+
one React instance and a stable measured height while its text changes, and
|
|
1102
|
+
remains interactive during a transition so a tap can reverse it. `C` is
|
|
1103
|
+
measured after mounting for expansion. On collapse the caller retains that
|
|
1104
|
+
same React instance until the newest collapsed settlement callback, after
|
|
1105
|
+
which it may unmount it. Its measured root keeps its natural height; native
|
|
1106
|
+
clipping, not a React height animation, controls the visible portion. `I` is
|
|
1107
|
+
`expandedTrailingInset` at the expanded endpoint and zero at the collapsed
|
|
1108
|
+
endpoint. The native display-link/animator updates the visible `R` suffix,
|
|
1109
|
+
`C`, `T` position, `I`, `F` position, scroll offset, and sidecar-aware extent
|
|
1110
|
+
on one UI-thread timeline. No per-frame JavaScript command is required.
|
|
1111
|
+
The wrapper may mount `C` and `T` before delivering a new structural snapshot;
|
|
1112
|
+
the previous accepted snapshot continues to own the list during this short
|
|
1113
|
+
preparation. For that new command only, the wrapper waits for the current
|
|
1114
|
+
instances of both slot roots to report their first layout, including a genuine
|
|
1115
|
+
zero-height layout, then delivers the complete snapshot once. A numeric zero
|
|
1116
|
+
height without a layout event is not proof that a newly mounted `C` is ready.
|
|
1117
|
+
A newer pending id replaces an older one; no stale prepared command may be
|
|
1118
|
+
delivered later. Initial snapshots and same-id content updates do not wait.
|
|
1119
|
+
Native applies the measured slot heights and the complete pending snapshot in
|
|
1120
|
+
one ordered main-thread update, with heights first, regardless of which Nitro
|
|
1121
|
+
property setter ran first in that React commit. It must not reveal `C` or emit
|
|
1122
|
+
a settlement callback between those two values.
|
|
1123
|
+
|
|
1124
|
+
All retiring or newly revealed row suffix is inert and excluded from
|
|
1125
|
+
accessibility, selection, row actions, visible-range, and end-reached events.
|
|
1126
|
+
The whole `C` slot is inert and accessibility-hidden while preparing,
|
|
1127
|
+
animating, or settled collapsed; it becomes interactive and accessible only
|
|
1128
|
+
after the expanded endpoint settles. The visible part of `T` remains
|
|
1129
|
+
hit-testable throughout so a tap can reverse the transition. Native
|
|
1130
|
+
must not infer the target from product-specific row counts or toggle text.
|
|
1131
|
+
An initially expanded snapshot and a same-transition-id content or price
|
|
1132
|
+
update use `disclosure.expanded` without starting or re-acknowledging a
|
|
1133
|
+
structural transition. An initial collapsed snapshot may omit or mount an
|
|
1134
|
+
empty `C` root; later collapse keeps its mounted instance through settlement.
|
|
1135
|
+
|
|
1136
|
+
For each newly consumed `structuralTransition.id` with `disclosure`, native
|
|
1137
|
+
emits `onStructuralTransitionSettled` once for the latest non-superseded id
|
|
1138
|
+
when a listener is registered. Without a listener, native still settles the
|
|
1139
|
+
command and releases its pending transition state, but does not serialize or
|
|
1140
|
+
cross the callback boundary. Adding or replacing a listener does not replay a
|
|
1141
|
+
settled id; removing it stops delivery for subsequent settlements. The caller
|
|
1142
|
+
may keep `C` mounted after settlement; native still controls its visibility,
|
|
1143
|
+
interaction, and accessibility. The callback carries JSON
|
|
1144
|
+
`{ transitionId: string, status: 'completed' | 'interrupted',
|
|
1145
|
+
settledExpanded: boolean }`. `settledExpanded` describes the accepted target
|
|
1146
|
+
after native geometry and scroll ownership have reached that state, never a
|
|
1147
|
+
guess from measured `C` height. Natural animation completion, reduced motion,
|
|
1148
|
+
zero duration, and a new id with unchanged or empty row keys that settles
|
|
1149
|
+
directly report `completed`. Drag, explicit scrolling, resize, or unsupported
|
|
1150
|
+
geometry atomically commit the accepted target and report `interrupted` after
|
|
1151
|
+
temporary presentation and scroll ownership have been released. A newer id
|
|
1152
|
+
replaces the previous command from current presentation geometry: the older
|
|
1153
|
+
id must not complete late or force its former target before the newer one.
|
|
1154
|
+
The caller guards the callback by its latest id and current list/account/network
|
|
1155
|
+
owner; a matching numeric id from a prior owner must not unmount current `C`.
|
|
1156
|
+
An accepted row patch while a direct disclosure commit is pending MUST preserve
|
|
1157
|
+
that command's settlement ownership. If the patch supersedes the pending list
|
|
1158
|
+
submission, its committed callback settles the current non-superseded id after
|
|
1159
|
+
layout and any pending collapsed-toggle positioning, exactly once. It MUST NOT
|
|
1160
|
+
leave callers waiting for a discarded list callback.
|
|
1161
|
+
An already settled id
|
|
1162
|
+
does not re-emit on a same-id patch. Disposal drops the callback with its
|
|
1163
|
+
owner. An unsupported or immediately settled new id must still acknowledge
|
|
1164
|
+
when a listener is registered; otherwise a caller could retain `C`
|
|
1165
|
+
indefinitely after collapse.
|
|
1166
|
+
|
|
763
1167
|
On Android, mounting/unmounting a slot or changing its actual rounded native
|
|
764
1168
|
height MUST request one coalesced RecyclerView measurement/layout repair. The
|
|
765
1169
|
repair runs after the current layout completes and only with a positive viewport
|
|
@@ -770,7 +1174,8 @@ request another repair. This leaves Fabric-owned slot content and row indices
|
|
|
770
1174
|
unchanged; source checks alone do not establish first-layout visibility.
|
|
771
1175
|
|
|
772
1176
|
Fabric mount/unmount ownership remains explicit even when RecyclerView detaches a
|
|
773
|
-
slot holder. The native
|
|
1177
|
+
slot holder. The new native wrapper retains six scrolling roots until Fabric unmount
|
|
1178
|
+
(plus the independent optional fixed top overlay);
|
|
774
1179
|
recycling detaches slot content without disposing its React tree. Unmount removes
|
|
775
1180
|
that exact view. Native list disposal MUST NOT dispatch pending row/slot events.
|
|
776
1181
|
The generated Nitro 0.37 leaf managers require deterministic child-forwarding
|
|
@@ -819,8 +1224,28 @@ applied inset and remains valid while that inset is retained across detach.
|
|
|
819
1224
|
|
|
820
1225
|
## Native rich activity presentation
|
|
821
1226
|
|
|
1227
|
+
Status: Source implemented for `ActivityRow.hideLeading`. Web DOM tests cover
|
|
1228
|
+
both Activity paths, Android production renderer instrumentation covers
|
|
1229
|
+
same-view rebinds, and the iOS NativeListModule arm64 Simulator target builds.
|
|
1230
|
+
iOS runtime acceptance remains pending. Optional `hideLeading: true` suppresses
|
|
1231
|
+
the activity row's main leading visual, its accessibility content, and the entire visual width and
|
|
1232
|
+
following gap. The title, description, status, amounts, fee, and footer actions
|
|
1233
|
+
retain their normal semantics; footer actions align with the title region rather
|
|
1234
|
+
than a reserved visual slot. `leading` remains required in the row model and is
|
|
1235
|
+
still validated. Omitted or `false` preserves the existing layout and image
|
|
1236
|
+
behavior. The field must be a boolean in snapshots and patches; an omitted patch
|
|
1237
|
+
field preserves the current value. A full snapshot or patch changing
|
|
1238
|
+
`hideLeading` rebinds the visual and layout in both legacy and rich Activity
|
|
1239
|
+
presentations, including key reuse. Hidden visuals do not start image requests;
|
|
1240
|
+
rebinding to `false` may load the current image. Recycling and late image
|
|
1241
|
+
callbacks must not restore a hidden visual or leave hidden layout/accessibility
|
|
1242
|
+
state on a later visible row. This contract applies on iOS, Android, and Web.
|
|
1243
|
+
The Android instrumentation uses icon visuals to verify layout, action alignment
|
|
1244
|
+
and amount preservation. Late image completion is guarded by the existing
|
|
1245
|
+
image-slot recycle epoch; a hide-during-image-load device case remains unverified.
|
|
1246
|
+
|
|
822
1247
|
Status: Source implemented; native runtime acceptance pending. Optional `amounts`
|
|
823
|
-
supersedes legacy `primaryAmount`/`secondaryAmount`
|
|
1248
|
+
supersedes legacy `primaryAmount`/`secondaryAmount` in rich Activity rows. It accepts at
|
|
824
1249
|
most 32 keyed lines, each with formatted text, optional leading visual, tone,
|
|
825
1250
|
secondary text and subscript text segments. Segments are caller-formatted and
|
|
826
1251
|
never interpreted as numbers by NativeList. Omission preserves the legacy tree.
|
|
@@ -1298,6 +1723,26 @@ Presentation wrapper changes move the existing nodes and preserve asynchronous
|
|
|
1298
1723
|
loading/error visibility; they do not recreate or reload an unchanged source.
|
|
1299
1724
|
Changing the source set retains the existing rebuild/disposal behavior.
|
|
1300
1725
|
|
|
1726
|
+
## Opt-in structural snapshot transition (Home assets)
|
|
1727
|
+
|
|
1728
|
+
`NativeListSnapshot.structuralTransition?: { id: string; durationMs?: number; anchorKey?: string }` is an update command carried by a validated complete snapshot. It applies only after the initial mount, when `id` differs from the last consumed transition id and the row-key sequence changes while both the previous and next layouts are vertical, single-column `linear` or `table`. Grid, sectioned, and horizontal source or target layouts apply the snapshot without this animation. The default duration is 220 ms; explicit values must be finite and in the 0–2000 ms range. Reusing an id on price, balance, selection, or theme snapshots is inert. `applyPatches` never starts a structural transition. A newer id supersedes an in-flight transition from its current visual geometry; host disposal cancels it. Reduced-motion settings disable animation while preserving final rows and scroll position.
|
|
1729
|
+
|
|
1730
|
+
The host captures the first visible row whose key survives the update and keeps its viewport offset where feasible. If none survives, an off-screen `anchorKey` supplies a fallback target. The host never teleports the fallback into view before animation. A collapse that makes the prior absolute offset unreachable must ease the final clamp over the same transition instead of jumping. The React footer slot's old/new height difference is compensated inside that same transition so its button and following content remain continuous while the slot changes. The header/footer slots remain in the one list scroll owner. Web external-scroll-host mode must suppress conflicting browser scroll anchoring during the transition. A failed or unsupported animation still applies the final snapshot and anchor atomically. Row geometry, footer position, and scroll offset share one platform timeline; Pager observes native offset changes as list-owned scroll commands. iOS and Android hosts drive interpolation on the native UI thread without JavaScript bridge frames; Web uses one animation frame timeline for height and offset.
|
|
1731
|
+
|
|
1732
|
+
Native prefix-tail refinement status: Android source and its production-class native regressions are implemented. The prior iOS presentation-layer implementation exercised Home row, footer, sidecar, and scroll geometry, but manual testing found long display-link gaps. The iOS single-tree replacement below is source implemented with limited iOS simulator consuming-app verification; actual presented FPS remains unverified. Only a vertical, single-column `linear` or `table` update whose old and new keys share a nonempty unchanged ordered prefix with unchanged retained-row geometry may use this path. The accepted snapshot becomes current immediately. On iOS the one collection view presents full-natural-height row cells: expansion installs the final row snapshot once before the first animation frame, while collapse retains the old suffix only as presentation until completion. Its flow layout clips the suffix at one animated boundary and reports the animated left extent, combined with the right sidecar maximum, as the scrollable content height. A partially visible boundary cell is masked without changing its measured height. Every suffix row is inert and hidden from accessibility throughout the transition, including newly accepted expansion rows and retiring collapse rows; none dispatch actions, receive selection, or enter visible-range and end-reached events until final commit. No second row-host tree is created, and expansion completion does not reinstall the same row snapshot. If UIKit cannot safely maintain the clipped row window, the host commits the final layout atomically rather than exposing blank or interactive retired content. Android continues to use its bounded inert native presentation layer, with at most 128 row hosts covering the viewport and one viewport of overscan on either side.
|
|
1733
|
+
|
|
1734
|
+
The same native display-link/animator tick changes suffix extent, footer position, clipping, and owner offset. A footer height change during the transition remains on that clock: both native hosts may temporarily use a signed tail extent to offset the footer's new naturally measured height, while the native root keeps its normal measurement; the current extent and owner offset become the start of the remaining segment, which still ends at the original deadline. On iOS a negative extent clips only the footer's overlapped top and excludes that region from hit testing, while the visible footer button remains interactive; cancellation or completion restores the unadjusted extent and full hit region. Expansion preserves the starting offset. Collapse chooses the nearest final offset that fully exposes the collapsed footer below sticky chrome when it fits; a footer taller than the viewport starts below sticky chrome. The final scroll extent is the maximum of the left content and the right sidecar. A newer transition id starts from the current presentation extent and offset and invalidates the old completion when the retained prefix boundary is unchanged. If that boundary changes, the host commits the latest snapshot atomically instead of carrying the old spacer into a different row position. User drag, an explicit scroll command, owner change, resize, reduced motion, or disposal releases temporary presentation state without a stale completion taking scroll ownership. Price patches update the accepted snapshot and any still presented matching rows without restarting the clock. Unsupported shapes retain the existing structural behavior. This refinement introduces no public field or command.
|
|
1735
|
+
|
|
1736
|
+
Web/Desktop/extension prefix-preserving tail refinement status: source implemented; Web and Desktop consuming-app preview routes exercised with the local candidate Web overlay. Browser extension was outside that preview scope, and no FPS or paint-duration claim has been verified. The complete accepted snapshot becomes current immediately, while removed suffix rows may remain only in an inert, accessibility-hidden presentation window until the existing 220 ms transition completes. That window follows the same scroll owner and mounts only viewport-near old rows as the owner moves; each row keeps its natural height while the tail is clipped by the interpolated list extent. Inserted suffix rows use their final layout immediately and are revealed by the growing extent. The shared animation frame updates list height, React footer compensation, and programmatic scroll together. Its final scroll range uses the actual parent page extent, including a taller sidecar. For an off-screen fallback, Web uses the footer's complete visible interval where it fits, including the sticky inset, and otherwise aligns the footer start below the sticky region; the retained row is preferred only when the footer is absent. Price patches update the accepted snapshot and visible retained rows without restarting the transition. A newer structure command, unsupported shape, user scroll takeover, reduced motion, resize, or disposal releases presentation nodes and animation ownership without a stale completion writing an older offset. Other structural changes retain the existing behavior. This refinement adds no public field or native behavior; focused Web DOM tests passed, while browser extension and broader runtime performance remain unverified.
|
|
1737
|
+
|
|
1738
|
+
`DataRow.trailing?: readonly TrailingAccessory[]` reuses the existing accessory action contract, including `rowKey`, `actionKey`, disabled state, and accessibility/test labels. At most two accessories are accepted. Desktop Home can render its independent price/balance columns with `DataRow.columns` and a Swap button in `trailing`. Existing rows without `trailing` retain their current layout. On iOS, trailing controls reserve their intrinsic width and the weighted columns fill the remaining row width. Changing between linear and table layout rebinds retained cells even when their row payload is unchanged.
|
|
1739
|
+
|
|
1740
|
+
`DataColumn.textSegments` and `secondaryTextSegments` reuse `ValueTextSegment` (`text`, optional `style: 'subscript'`) for formatted prices, balances, and values in each independent table column. Plain `text` and `secondaryText` remain the spoken/fallback strings. Missing segments preserve existing rendering.
|
|
1741
|
+
|
|
1742
|
+
`DataColumn.titleIcon` and `secondaryTitleIcon` reuse `IdentityRow.titleIcon` after the primary column text. They preserve native-asset and receipt-token symbols in the desktop asset column. Web `hoverActionKey` uses the same anchored help action as Identity rows; absent icons leave the existing column layout unchanged.
|
|
1743
|
+
|
|
1744
|
+
`IdentityRow.titleSuffixes?: readonly { text: string; tone: 'secondary' | 'disabled'; maxWidth?: number }[]` supports up to three plain 12/16 labels on the title line. Each suffix truncates independently at its optional maximum width. This preserves Recent recipient EVM, network, and relative-time metadata without using background badges; rows without suffixes keep their present title layout.
|
|
1745
|
+
|
|
1301
1746
|
### Web slot-list scroll ownership
|
|
1302
1747
|
|
|
1303
1748
|
When container slots create an owned Web scroll host without `webScrollHost`,
|
package/docs/STYLE_SPEC.md
CHANGED
|
@@ -153,6 +153,7 @@ resolved colors; `style.color` does not resolve application color-token names.
|
|
|
153
153
|
| `bgApp` | `background` | List ground |
|
|
154
154
|
| `bg` | `rowBackground` | Row ground |
|
|
155
155
|
| `bgActive` | `rowSelectedBackground`, `rowPressedBackground` | Selected / pressed row |
|
|
156
|
+
| `bgHover` | `rowHoverBackground` (optional Web) | Hovered row; omission uses pressed color |
|
|
156
157
|
| `bgSubdued` | `subduedBackground` | Card and group ground |
|
|
157
158
|
| `bgStrong` | `strongBackground` | Chip / badge ground |
|
|
158
159
|
| `bgInverse` | `inverseBackground` | Inverted ground |
|
|
@@ -269,6 +270,11 @@ Each cell describes the same public contract on **Web + iOS + Android**.
|
|
|
269
270
|
| `action` | title, value | padding, leadingGap, trailingGap, image |
|
|
270
271
|
| `system` | title, message, actionText | padding, lineGap |
|
|
271
272
|
|
|
273
|
+
`identity.style.leadingContentMaxWidthRatio` optionally caps the leading
|
|
274
|
+
visual, gap and title content to a finite fraction in `(0, 1]` of the row's
|
|
275
|
+
inner width. Omission retains the existing width behavior. This lets a
|
|
276
|
+
same-row trailing text button keep its own space while long titles truncate.
|
|
277
|
+
|
|
272
278
|
Market additionally exposes `titleBadgeLayout: inline`, `contentTrailingGap`,
|
|
273
279
|
`subtitleTrailingPadding`, `changeWidth`, `changeHeight`, `changeCornerRadius`, preserving
|
|
274
280
|
its existing template-specific style surface on every platform.
|
|
@@ -27,17 +27,22 @@ final class HybridNativeList: HybridNativeListSpec {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
var containerSlotHeightsJson: String = "[]" {
|
|
30
|
+
didSet { if containerSlotHeightsJson != oldValue { slotHeightsChanged = true } }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
var sidecarLayoutJson: String = "" {
|
|
30
34
|
didSet {
|
|
31
|
-
guard
|
|
35
|
+
guard sidecarLayoutJson != oldValue else { return }
|
|
32
36
|
runOnMain { [weak self] in
|
|
33
37
|
guard let self else { return }
|
|
34
|
-
self.hostView?.
|
|
38
|
+
self.hostView?.setSidecarLayoutJson(self.sidecarLayoutJson)
|
|
35
39
|
}
|
|
36
40
|
}
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
private var fontsChanged = false
|
|
40
44
|
private var snapshotChanged = false
|
|
45
|
+
private var slotHeightsChanged = false
|
|
41
46
|
|
|
42
47
|
var fontFamiliesJson: String = "{}" {
|
|
43
48
|
didSet { if fontFamiliesJson != oldValue { fontsChanged = true } }
|
|
@@ -75,11 +80,16 @@ final class HybridNativeList: HybridNativeListSpec {
|
|
|
75
80
|
|
|
76
81
|
func afterUpdate() {
|
|
77
82
|
let fonts = fontsChanged ? fontFamiliesJson : nil
|
|
83
|
+
let slotHeights = slotHeightsChanged ? containerSlotHeightsJson : nil
|
|
78
84
|
let snapshot = snapshotChanged ? snapshotJson : nil
|
|
79
85
|
fontsChanged = false
|
|
86
|
+
slotHeightsChanged = false
|
|
80
87
|
snapshotChanged = false
|
|
81
88
|
runOnMain { [weak self] in
|
|
82
89
|
if let fonts { self?.hostView?.setFontFamiliesJson(fonts) }
|
|
90
|
+
if let slotHeights {
|
|
91
|
+
self?.hostView?.setContainerSlotHeightsJson(slotHeights, followedBySnapshot: snapshot != nil)
|
|
92
|
+
}
|
|
83
93
|
if let snapshot { self?.hostView?.applySnapshotJson(snapshot) }
|
|
84
94
|
}
|
|
85
95
|
}
|
|
@@ -109,6 +119,14 @@ final class HybridNativeList: HybridNativeListSpec {
|
|
|
109
119
|
var onSelectionDelta: ((_ payloadJson: String) -> Void)?
|
|
110
120
|
var onReorder: ((_ payloadJson: String) -> Void)?
|
|
111
121
|
var onEndReached: ((_ payloadJson: String) -> Void)?
|
|
122
|
+
var onStructuralTransitionSettled: ((_ payloadJson: String) -> Void)? {
|
|
123
|
+
didSet {
|
|
124
|
+
runOnMain { [weak self] in
|
|
125
|
+
guard let self else { return }
|
|
126
|
+
self.hostView?.onStructuralTransitionSettled = self.onStructuralTransitionSettled
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
112
130
|
var onVisibleRangeChanged: ((_ payloadJson: String) -> Void)? {
|
|
113
131
|
didSet {
|
|
114
132
|
runOnMain { [weak self] in
|
|
@@ -222,6 +240,7 @@ final class HybridNativeList: HybridNativeListSpec {
|
|
|
222
240
|
onSelectionDelta = nil
|
|
223
241
|
onReorder = nil
|
|
224
242
|
onEndReached = nil
|
|
243
|
+
onStructuralTransitionSettled = nil
|
|
225
244
|
onVisibleRangeChanged = nil
|
|
226
245
|
onScrollPositionThresholdChange = nil
|
|
227
246
|
hostView.dispose()
|