@onekeyfe/react-native-native-list 6.7.0-alpha.378 → 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/src/NativeList.web.tsx
CHANGED
|
@@ -37,6 +37,8 @@ import {
|
|
|
37
37
|
canonicalNativeListAvatarUri,
|
|
38
38
|
} from './web/NativeListWebAvatarCache';
|
|
39
39
|
|
|
40
|
+
const collapsedExpandedContentStyle = { display: 'none' } as const;
|
|
41
|
+
|
|
40
42
|
export type { NativeListProps, NativeListRef } from './NativeList.types';
|
|
41
43
|
export type {
|
|
42
44
|
ActionAnchorState,
|
|
@@ -93,6 +95,10 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
93
95
|
listHeader,
|
|
94
96
|
listEmpty,
|
|
95
97
|
listFooter,
|
|
98
|
+
listExpandedContent,
|
|
99
|
+
listStructureToggle,
|
|
100
|
+
listSidecar,
|
|
101
|
+
sidecarLayout,
|
|
96
102
|
topOverlay: _topOverlay,
|
|
97
103
|
topScrollEdgeEffect: _topScrollEdgeEffect,
|
|
98
104
|
webScrollHost,
|
|
@@ -109,6 +115,7 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
109
115
|
onReorder,
|
|
110
116
|
onEndReached,
|
|
111
117
|
onVisibleRangeChanged,
|
|
118
|
+
onStructuralTransitionSettled: _onStructuralTransitionSettled,
|
|
112
119
|
onRefresh,
|
|
113
120
|
onScrollToIndexFailed,
|
|
114
121
|
initialScrollIndex,
|
|
@@ -132,10 +139,16 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
132
139
|
}) ?? 0,
|
|
133
140
|
[inputStickyInset]
|
|
134
141
|
);
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
142
|
+
// Freshness is limited to this component invocation; later renders revalidate.
|
|
143
|
+
let freshlyValidatedSnapshot: NativeListSnapshot | undefined;
|
|
144
|
+
const preparedSnapshot = useMemo(() => {
|
|
145
|
+
const prepared = containNativeListInput(() =>
|
|
146
|
+
validateSnapshot(inputSnapshot)
|
|
147
|
+
);
|
|
148
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- The value is intentionally render-local.
|
|
149
|
+
freshlyValidatedSnapshot = prepared;
|
|
150
|
+
return prepared;
|
|
151
|
+
}, [inputSnapshot]);
|
|
139
152
|
const snapshot = preparedSnapshot ?? acceptedSnapshotRef.current;
|
|
140
153
|
const fontFamiliesJson = useNativeListFontFamiliesJson();
|
|
141
154
|
const fontFamiliesJsonRef = useRef(fontFamiliesJson);
|
|
@@ -143,49 +156,28 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
143
156
|
const hasEmptySlot = listEmpty !== undefined;
|
|
144
157
|
const hasEmptySlotRef = useRef(hasEmptySlot);
|
|
145
158
|
hasEmptySlotRef.current = hasEmptySlot;
|
|
146
|
-
const [
|
|
147
|
-
|
|
148
|
-
emptyBottomPadding:
|
|
149
|
-
!snapshot.rows.length && hasEmptySlot
|
|
150
|
-
? snapshot.layout.contentPaddingBottom ??
|
|
151
|
-
snapshot.layout.contentPadding ??
|
|
152
|
-
0
|
|
153
|
-
: 0,
|
|
154
|
-
padding:
|
|
155
|
-
snapshot.layout.contentPaddingHorizontal ??
|
|
156
|
-
snapshot.layout.contentPadding ??
|
|
157
|
-
0,
|
|
158
|
-
}));
|
|
159
|
-
const synchronizeSlots = (next: NativeListSnapshot) => {
|
|
160
|
-
const hasRows = next.rows.length > 0;
|
|
161
|
-
const padding =
|
|
162
|
-
next.layout.contentPaddingHorizontal ?? next.layout.contentPadding ?? 0;
|
|
163
|
-
const emptyBottomPadding =
|
|
164
|
-
!hasRows && hasEmptySlotRef.current
|
|
165
|
-
? next.layout.contentPaddingBottom ?? next.layout.contentPadding ?? 0
|
|
166
|
-
: 0;
|
|
167
|
-
setSlotState((previous) =>
|
|
168
|
-
previous.hasRows === hasRows &&
|
|
169
|
-
previous.padding === padding &&
|
|
170
|
-
previous.emptyBottomPadding === emptyBottomPadding
|
|
171
|
-
? previous
|
|
172
|
-
: { hasRows, padding, emptyBottomPadding }
|
|
173
|
-
);
|
|
174
|
-
};
|
|
159
|
+
const [viewportWidth, setViewportWidth] = useState(0);
|
|
160
|
+
const [sidecarHeight, setSidecarHeight] = useState(0);
|
|
175
161
|
const normalizeWebSnapshot = (
|
|
176
162
|
next: NativeListSnapshot,
|
|
177
163
|
hasReactEmpty = hasEmptySlotRef.current,
|
|
178
|
-
hasSlotLayout = useSlotLayoutRef.current
|
|
164
|
+
hasSlotLayout = useSlotLayoutRef.current,
|
|
165
|
+
validatedInThisRender = false
|
|
179
166
|
) => {
|
|
180
167
|
if (hasSlotLayout && next.layout.orientation === 'horizontal')
|
|
181
168
|
throw new Error(
|
|
182
169
|
'Web parent scrolling and container slots support vertical lists only'
|
|
183
170
|
);
|
|
184
171
|
const emptySlot = hasReactEmpty && next.rows.length === 0;
|
|
185
|
-
const
|
|
186
|
-
emptySlot
|
|
187
|
-
|
|
188
|
-
|
|
172
|
+
const normalized =
|
|
173
|
+
emptySlot && next.emptyState !== undefined
|
|
174
|
+
? { ...next, emptyState: undefined }
|
|
175
|
+
: next;
|
|
176
|
+
const validated = validatedInThisRender
|
|
177
|
+
? normalized
|
|
178
|
+
: validateSnapshot(normalized);
|
|
179
|
+
// Preserve an already normalized fallback's identity across rerenders.
|
|
180
|
+
return emptySlot && validated.layout.contentPaddingBottom !== 0
|
|
189
181
|
? {
|
|
190
182
|
...validated,
|
|
191
183
|
layout: { ...validated.layout, contentPaddingBottom: 0 },
|
|
@@ -205,6 +197,9 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
205
197
|
listHeader !== undefined ||
|
|
206
198
|
listEmpty !== undefined ||
|
|
207
199
|
listFooter !== undefined;
|
|
200
|
+
useSlotLayoutRef.current ||= listSidecar !== undefined;
|
|
201
|
+
useSlotLayoutRef.current ||=
|
|
202
|
+
listExpandedContent !== undefined || listStructureToggle !== undefined;
|
|
208
203
|
const useSlotLayout = useSlotLayoutRef.current;
|
|
209
204
|
const resolvedScrollHost =
|
|
210
205
|
webScrollHost === undefined
|
|
@@ -217,11 +212,86 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
217
212
|
webVirtualizationEnabledRef.current = webVirtualizationEnabled;
|
|
218
213
|
const validatedSnapshot = useMemo(
|
|
219
214
|
() =>
|
|
220
|
-
containNativeListInput(() =>
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
215
|
+
containNativeListInput(() => {
|
|
216
|
+
if ((listSidecar !== undefined) !== (sidecarLayout !== undefined))
|
|
217
|
+
throw new Error(
|
|
218
|
+
'listSidecar and sidecarLayout must be supplied together'
|
|
219
|
+
);
|
|
220
|
+
if (sidecarLayout) {
|
|
221
|
+
const { width, gap, maxContentWidth } = sidecarLayout;
|
|
222
|
+
if (
|
|
223
|
+
snapshot.layout.kind !== 'linear' ||
|
|
224
|
+
!Number.isFinite(width) ||
|
|
225
|
+
width <= 0 ||
|
|
226
|
+
!Number.isFinite(gap) ||
|
|
227
|
+
gap < 0 ||
|
|
228
|
+
!Number.isFinite(maxContentWidth) ||
|
|
229
|
+
maxContentWidth <= width + gap
|
|
230
|
+
)
|
|
231
|
+
throw new Error('Invalid NativeList sidecar layout');
|
|
232
|
+
}
|
|
233
|
+
return normalizeWebSnapshot(
|
|
234
|
+
snapshot,
|
|
235
|
+
hasEmptySlot,
|
|
236
|
+
useSlotLayout,
|
|
237
|
+
preparedSnapshot !== undefined &&
|
|
238
|
+
preparedSnapshot === freshlyValidatedSnapshot
|
|
239
|
+
);
|
|
240
|
+
}) ?? acceptedSnapshotRef.current,
|
|
241
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- Preserve the existing memo dependencies; freshness is render-local.
|
|
242
|
+
[snapshot, hasEmptySlot, useSlotLayout, listSidecar, sidecarLayout]
|
|
224
243
|
);
|
|
244
|
+
const [slotState, setSlotState] = useState(() => ({
|
|
245
|
+
hasRows: validatedSnapshot.rows.length > 0,
|
|
246
|
+
emptyBottomPadding:
|
|
247
|
+
!validatedSnapshot.rows.length && hasEmptySlot
|
|
248
|
+
? validatedSnapshot.layout.contentPaddingBottom ??
|
|
249
|
+
validatedSnapshot.layout.contentPadding ??
|
|
250
|
+
0
|
|
251
|
+
: 0,
|
|
252
|
+
padding:
|
|
253
|
+
validatedSnapshot.layout.contentPaddingHorizontal ??
|
|
254
|
+
validatedSnapshot.layout.contentPadding ??
|
|
255
|
+
0,
|
|
256
|
+
disclosurePresent: validatedSnapshot.disclosure !== undefined,
|
|
257
|
+
disclosureExpanded: validatedSnapshot.disclosure?.expanded === true,
|
|
258
|
+
hasExpandedContent: validatedSnapshot.disclosure?.expanded === true,
|
|
259
|
+
expandedTrailingInset:
|
|
260
|
+
validatedSnapshot.disclosure?.expandedTrailingInset ?? 0,
|
|
261
|
+
}));
|
|
262
|
+
const synchronizeSlots = (next: NativeListSnapshot) => {
|
|
263
|
+
const hasRows = next.rows.length > 0;
|
|
264
|
+
const padding =
|
|
265
|
+
next.layout.contentPaddingHorizontal ?? next.layout.contentPadding ?? 0;
|
|
266
|
+
const emptyBottomPadding =
|
|
267
|
+
!hasRows && hasEmptySlotRef.current
|
|
268
|
+
? next.layout.contentPaddingBottom ?? next.layout.contentPadding ?? 0
|
|
269
|
+
: 0;
|
|
270
|
+
const disclosurePresent = next.disclosure !== undefined;
|
|
271
|
+
const disclosureExpanded = next.disclosure?.expanded === true;
|
|
272
|
+
const expandedTrailingInset = next.disclosure?.expandedTrailingInset ?? 0;
|
|
273
|
+
setSlotState((previous) => {
|
|
274
|
+
const hasExpandedContent =
|
|
275
|
+
previous.hasExpandedContent || disclosureExpanded;
|
|
276
|
+
return previous.hasRows === hasRows &&
|
|
277
|
+
previous.padding === padding &&
|
|
278
|
+
previous.emptyBottomPadding === emptyBottomPadding &&
|
|
279
|
+
previous.disclosurePresent === disclosurePresent &&
|
|
280
|
+
previous.disclosureExpanded === disclosureExpanded &&
|
|
281
|
+
previous.hasExpandedContent === hasExpandedContent &&
|
|
282
|
+
previous.expandedTrailingInset === expandedTrailingInset
|
|
283
|
+
? previous
|
|
284
|
+
: {
|
|
285
|
+
hasRows,
|
|
286
|
+
padding,
|
|
287
|
+
emptyBottomPadding,
|
|
288
|
+
disclosurePresent,
|
|
289
|
+
disclosureExpanded,
|
|
290
|
+
hasExpandedContent,
|
|
291
|
+
expandedTrailingInset,
|
|
292
|
+
};
|
|
293
|
+
});
|
|
294
|
+
};
|
|
225
295
|
const snapshotRef = useRef(validatedSnapshot);
|
|
226
296
|
const snapshotPropRef = useRef(validatedSnapshot);
|
|
227
297
|
// An imperative snapshot survives host mounting until the snapshot prop changes.
|
|
@@ -229,7 +299,7 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
229
299
|
snapshotPropRef.current = validatedSnapshot;
|
|
230
300
|
snapshotRef.current = validatedSnapshot;
|
|
231
301
|
acceptedSnapshotRef.current = validatedSnapshot;
|
|
232
|
-
synchronizeSlots(
|
|
302
|
+
synchronizeSlots(validatedSnapshot);
|
|
233
303
|
}
|
|
234
304
|
if (!initializedSnapshot.current) {
|
|
235
305
|
acceptedSnapshotRef.current = validatedSnapshot;
|
|
@@ -498,9 +568,19 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
498
568
|
);
|
|
499
569
|
if (!useSlotLayout) return <View {...viewProps} ref={setHostRef} />;
|
|
500
570
|
const padding = slotState.padding;
|
|
571
|
+
const footerBandStyle = { paddingHorizontal: padding };
|
|
572
|
+
const trailingInsetStyle = {
|
|
573
|
+
height: slotState.disclosureExpanded
|
|
574
|
+
? slotState.expandedTrailingInset
|
|
575
|
+
: 0,
|
|
576
|
+
};
|
|
577
|
+
const hasFooterBand =
|
|
578
|
+
listFooter !== undefined ||
|
|
579
|
+
listExpandedContent !== undefined ||
|
|
580
|
+
listStructureToggle !== undefined;
|
|
501
581
|
const content = (
|
|
502
582
|
<View
|
|
503
|
-
ref={setBoundsRef}
|
|
583
|
+
ref={sidecarLayout ? undefined : setBoundsRef}
|
|
504
584
|
style={{ position: 'relative', flexShrink: 0, minWidth: 0 }}
|
|
505
585
|
>
|
|
506
586
|
{listHeader !== undefined ? (
|
|
@@ -517,14 +597,90 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
517
597
|
{listEmpty}
|
|
518
598
|
</View>
|
|
519
599
|
) : null}
|
|
520
|
-
{
|
|
521
|
-
<View style={
|
|
600
|
+
{hasFooterBand ? (
|
|
601
|
+
<View testID="native-list-footer-slot" style={footerBandStyle}>
|
|
602
|
+
{listExpandedContent !== undefined &&
|
|
603
|
+
slotState.hasExpandedContent ? (
|
|
604
|
+
<View
|
|
605
|
+
testID="native-list-expanded-content-slot"
|
|
606
|
+
aria-hidden={!slotState.disclosureExpanded}
|
|
607
|
+
pointerEvents={slotState.disclosureExpanded ? 'auto' : 'none'}
|
|
608
|
+
style={
|
|
609
|
+
slotState.disclosureExpanded
|
|
610
|
+
? undefined
|
|
611
|
+
: collapsedExpandedContentStyle
|
|
612
|
+
}
|
|
613
|
+
>
|
|
614
|
+
{listExpandedContent}
|
|
615
|
+
</View>
|
|
616
|
+
) : null}
|
|
617
|
+
{listStructureToggle !== undefined ? (
|
|
618
|
+
<View
|
|
619
|
+
testID="native-list-structure-toggle-slot"
|
|
620
|
+
aria-hidden={!slotState.disclosurePresent}
|
|
621
|
+
pointerEvents={slotState.disclosurePresent ? 'auto' : 'none'}
|
|
622
|
+
style={
|
|
623
|
+
slotState.disclosurePresent
|
|
624
|
+
? undefined
|
|
625
|
+
: collapsedExpandedContentStyle
|
|
626
|
+
}
|
|
627
|
+
>
|
|
628
|
+
{listStructureToggle}
|
|
629
|
+
</View>
|
|
630
|
+
) : null}
|
|
631
|
+
{slotState.expandedTrailingInset > 0 &&
|
|
632
|
+
(listExpandedContent !== undefined ||
|
|
633
|
+
listStructureToggle !== undefined) ? (
|
|
634
|
+
<View
|
|
635
|
+
testID="native-list-trailing-inset-slot"
|
|
636
|
+
style={trailingInsetStyle}
|
|
637
|
+
/>
|
|
638
|
+
) : null}
|
|
639
|
+
{listFooter}
|
|
640
|
+
</View>
|
|
522
641
|
) : null}
|
|
523
642
|
</View>
|
|
524
643
|
);
|
|
644
|
+
const bandWidth = sidecarLayout
|
|
645
|
+
? Math.min(viewportWidth, sidecarLayout.maxContentWidth)
|
|
646
|
+
: 0;
|
|
647
|
+
const sidecarMargin = Math.max(0, (viewportWidth - bandWidth) / 2);
|
|
648
|
+
const leftWidth = Math.max(
|
|
649
|
+
0,
|
|
650
|
+
bandWidth - (sidecarLayout?.width ?? 0) - (sidecarLayout?.gap ?? 0)
|
|
651
|
+
);
|
|
652
|
+
const scrollContent = sidecarLayout ? (
|
|
653
|
+
<View
|
|
654
|
+
ref={setBoundsRef}
|
|
655
|
+
style={{ position: 'relative', minHeight: sidecarHeight }}
|
|
656
|
+
>
|
|
657
|
+
<View style={{ marginStart: sidecarMargin, width: leftWidth }}>
|
|
658
|
+
{content}
|
|
659
|
+
</View>
|
|
660
|
+
<View
|
|
661
|
+
style={{
|
|
662
|
+
position: 'absolute',
|
|
663
|
+
end: sidecarMargin,
|
|
664
|
+
top: 0,
|
|
665
|
+
width: sidecarLayout.width,
|
|
666
|
+
}}
|
|
667
|
+
onLayout={(event) =>
|
|
668
|
+
setSidecarHeight(event.nativeEvent.layout.height)
|
|
669
|
+
}
|
|
670
|
+
>
|
|
671
|
+
{listSidecar}
|
|
672
|
+
</View>
|
|
673
|
+
</View>
|
|
674
|
+
) : (
|
|
675
|
+
content
|
|
676
|
+
);
|
|
525
677
|
return (
|
|
526
678
|
<View
|
|
527
679
|
{...viewProps}
|
|
680
|
+
onLayout={(event) => {
|
|
681
|
+
setViewportWidth(event.nativeEvent.layout.width);
|
|
682
|
+
viewProps.onLayout?.(event);
|
|
683
|
+
}}
|
|
528
684
|
style={[viewProps.style, { position: 'relative', minHeight: 0 }]}
|
|
529
685
|
>
|
|
530
686
|
{webScrollHost === undefined ? (
|
|
@@ -532,10 +688,10 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
532
688
|
ref={setScrollHostRef}
|
|
533
689
|
style={{ position: 'absolute', inset: 0, overflow: 'scroll' }}
|
|
534
690
|
>
|
|
535
|
-
{
|
|
691
|
+
{scrollContent}
|
|
536
692
|
</View>
|
|
537
693
|
) : (
|
|
538
|
-
|
|
694
|
+
scrollContent
|
|
539
695
|
)}
|
|
540
696
|
</View>
|
|
541
697
|
);
|
package/src/index.ts
CHANGED
|
@@ -14,10 +14,12 @@ export type {
|
|
|
14
14
|
ScrollToKeyParams,
|
|
15
15
|
ScrollToLocationParams,
|
|
16
16
|
ScrollToOffsetParams,
|
|
17
|
+
StructuralTransitionSettledEvent,
|
|
17
18
|
} from './NativeList';
|
|
18
19
|
export type * from './models';
|
|
19
20
|
export {
|
|
20
21
|
applyRowPatches,
|
|
22
|
+
planNativeListPatches,
|
|
21
23
|
serializePatches,
|
|
22
24
|
serializeSnapshot,
|
|
23
25
|
validatePatches,
|
package/src/models.ts
CHANGED
|
@@ -185,6 +185,7 @@ export const ROW_BOX_STYLE_KEYS_BY_TYPE = {
|
|
|
185
185
|
'trailingGap',
|
|
186
186
|
'image',
|
|
187
187
|
],
|
|
188
|
+
buttonGroup: [],
|
|
188
189
|
system: ['horizontalPadding', 'verticalPadding', 'lineGap'],
|
|
189
190
|
} as const;
|
|
190
191
|
|
|
@@ -201,6 +202,7 @@ type TemplateBoxStyle<T extends keyof typeof ROW_BOX_STYLE_KEYS_BY_TYPE> = Pick<
|
|
|
201
202
|
*/
|
|
202
203
|
export type IdentityRowStyle = TemplateBoxStyle<'identity'> &
|
|
203
204
|
Readonly<{
|
|
205
|
+
leadingContentMaxWidthRatio?: number;
|
|
204
206
|
title?: NativeListTextStyle;
|
|
205
207
|
subtitle?: NativeListTextStyle;
|
|
206
208
|
tertiary?: NativeListTextStyle;
|
|
@@ -279,8 +281,16 @@ export type SectionHeaderRowStyle = TemplateBoxStyle<'sectionHeader'> &
|
|
|
279
281
|
title?: NativeListTextStyle;
|
|
280
282
|
subtitle?: NativeListTextStyle;
|
|
281
283
|
value?: NativeListTextStyle;
|
|
284
|
+
valuePlacement?: 'afterTitle';
|
|
285
|
+
titleDecoration?: 'none' | 'dashed';
|
|
282
286
|
}>;
|
|
283
287
|
|
|
288
|
+
export type RowTextHelp = Readonly<{
|
|
289
|
+
actionKey: string;
|
|
290
|
+
presentation: 'tooltip' | 'popover';
|
|
291
|
+
underline?: 'dashed';
|
|
292
|
+
}>;
|
|
293
|
+
|
|
284
294
|
export type ActionRowStyle = TemplateBoxStyle<'action'> &
|
|
285
295
|
Readonly<{
|
|
286
296
|
title?: NativeListTextStyle;
|
|
@@ -344,19 +354,27 @@ export type VisualOverlay = Readonly<{
|
|
|
344
354
|
offsetX?: number;
|
|
345
355
|
offsetY?: number;
|
|
346
356
|
image?: ImageSource;
|
|
357
|
+
fallbackIcon?: Readonly<{ name: string; tintColor?: string }>;
|
|
347
358
|
name?: string;
|
|
348
359
|
text?: string;
|
|
360
|
+
fontWeight?: NativeListTextStyle['fontWeight'];
|
|
349
361
|
tintColor?: string;
|
|
350
362
|
backgroundColor?: string;
|
|
351
363
|
}>;
|
|
352
364
|
|
|
353
365
|
type VisualWithImage = Readonly<{
|
|
354
|
-
fallbackIcon?: Readonly<{
|
|
366
|
+
fallbackIcon?: Readonly<{
|
|
367
|
+
name: string;
|
|
368
|
+
tintColor?: string;
|
|
369
|
+
size?: number;
|
|
370
|
+
backgroundColor?: string;
|
|
371
|
+
}>;
|
|
355
372
|
overlays?: readonly VisualOverlay[];
|
|
356
373
|
borderStyle?: 'dashed';
|
|
357
374
|
borderColor?: string;
|
|
358
375
|
image?: ImageSource;
|
|
359
376
|
fallbackText?: string;
|
|
377
|
+
fallbackTextColor?: string;
|
|
360
378
|
backgroundColor?: string;
|
|
361
379
|
shape?: 'circle' | 'rounded' | 'square';
|
|
362
380
|
cornerIcon?: Readonly<{
|
|
@@ -408,9 +426,26 @@ export type TrailingAccessory =
|
|
|
408
426
|
kind: 'valuePair';
|
|
409
427
|
primary: string;
|
|
410
428
|
secondary: string;
|
|
429
|
+
primaryFontWeight?: 'medium';
|
|
430
|
+
primaryHelp?: RowTextHelp;
|
|
431
|
+
primaryTextSegments?: readonly ValueTextSegment[];
|
|
432
|
+
secondaryTextSegments?: readonly ValueTextSegment[];
|
|
411
433
|
primaryTone?: TextTone;
|
|
412
434
|
secondaryTone?: TextTone;
|
|
413
435
|
}>
|
|
436
|
+
| Readonly<{
|
|
437
|
+
kind: 'button';
|
|
438
|
+
label: string;
|
|
439
|
+
actionKey: string;
|
|
440
|
+
disabled?: boolean;
|
|
441
|
+
testID?: string;
|
|
442
|
+
accessibilityLabel?: string;
|
|
443
|
+
textColor?: string;
|
|
444
|
+
backgroundColor?: string;
|
|
445
|
+
pressedBackgroundColor?: string;
|
|
446
|
+
hoverBackgroundColor?: string;
|
|
447
|
+
focusRingColor?: string;
|
|
448
|
+
}>
|
|
414
449
|
| Readonly<{
|
|
415
450
|
kind: 'checkbox';
|
|
416
451
|
state: CheckboxState;
|
|
@@ -438,6 +473,7 @@ export type TrailingAccessory =
|
|
|
438
473
|
kind: 'icon';
|
|
439
474
|
name: string;
|
|
440
475
|
tintColor?: string;
|
|
476
|
+
buttonStyle?: 'tertiary';
|
|
441
477
|
disabled?: boolean;
|
|
442
478
|
actionKey?: string;
|
|
443
479
|
testID?: string;
|
|
@@ -480,6 +516,10 @@ export type RowBase = Readonly<{
|
|
|
480
516
|
export type IdentityRow = RowBase &
|
|
481
517
|
Readonly<{
|
|
482
518
|
type: 'identity';
|
|
519
|
+
/** Opt in to content-based height; explicit row/container height still wins. */
|
|
520
|
+
contentHeight?: 'auto';
|
|
521
|
+
/** Emits a row action instead of a press after a successful long press. */
|
|
522
|
+
longPressActionKey?: string;
|
|
483
523
|
// OneKey patch: UTF-16 ranges match Fuse indices and native attributed strings.
|
|
484
524
|
titleMatch?: readonly Readonly<{ start: number; end: number }>[];
|
|
485
525
|
titleActionKey?: string;
|
|
@@ -489,11 +529,27 @@ export type IdentityRow = RowBase &
|
|
|
489
529
|
leading: LeadingVisual;
|
|
490
530
|
leadingAction?: Extract<TrailingAccessory, { kind: 'icon' }>;
|
|
491
531
|
title: string;
|
|
532
|
+
/** Plain 12/16 labels beside the title, preserving independent tones. */
|
|
533
|
+
titleSuffixes?: readonly Readonly<{
|
|
534
|
+
text: string;
|
|
535
|
+
tone: 'secondary' | 'disabled';
|
|
536
|
+
maxWidth?: number;
|
|
537
|
+
}>[];
|
|
538
|
+
titleIcon?: Readonly<{
|
|
539
|
+
name: string;
|
|
540
|
+
tintColor?: string;
|
|
541
|
+
accessibilityLabel?: string;
|
|
542
|
+
/** Web-only anchored help from this icon; does not add a click action. */
|
|
543
|
+
hoverActionKey?: string;
|
|
544
|
+
}>;
|
|
545
|
+
/** Web-only additional help icon after titleIcon. */
|
|
546
|
+
secondaryTitleIcon?: IdentityRow['titleIcon'];
|
|
492
547
|
subtitle?: string;
|
|
493
548
|
tertiary?: string;
|
|
494
549
|
tertiaryTone?: 'secondary' | 'info';
|
|
495
550
|
titleLines?: 1 | 2;
|
|
496
|
-
|
|
551
|
+
/** Zero displays the complete subtitle. */
|
|
552
|
+
subtitleLines?: 0 | 1 | 2;
|
|
497
553
|
badges?: readonly BadgeModel[];
|
|
498
554
|
trailing?: readonly TrailingAccessory[];
|
|
499
555
|
draggable?: boolean;
|
|
@@ -534,6 +590,7 @@ export type ActivityRow = RowBase &
|
|
|
534
590
|
Readonly<{
|
|
535
591
|
type: 'activity';
|
|
536
592
|
leading: LeadingVisual;
|
|
593
|
+
hideLeading?: boolean;
|
|
537
594
|
secondaryLeading?: LeadingVisual;
|
|
538
595
|
title: string;
|
|
539
596
|
description?: string;
|
|
@@ -559,11 +616,14 @@ export type ActivityRow = RowBase &
|
|
|
559
616
|
export type MessageRow = RowBase &
|
|
560
617
|
Readonly<{
|
|
561
618
|
type: 'message';
|
|
619
|
+
/** Draws only top and bottom container borders when a border is supplied. */
|
|
620
|
+
presentation?: 'inlineWarning';
|
|
562
621
|
unread?: boolean;
|
|
563
622
|
leading?: LeadingVisual;
|
|
564
623
|
title: string;
|
|
565
624
|
body: string;
|
|
566
|
-
|
|
625
|
+
/** Zero displays the complete body. */
|
|
626
|
+
bodyLines?: 0 | 1 | 2 | 3;
|
|
567
627
|
time: string;
|
|
568
628
|
thumbnail?: ImageSource;
|
|
569
629
|
style?: MessageRowStyle;
|
|
@@ -572,8 +632,12 @@ export type MessageRow = RowBase &
|
|
|
572
632
|
export type DataColumn = Readonly<{
|
|
573
633
|
key: string;
|
|
574
634
|
text: string;
|
|
635
|
+
textSegments?: readonly ValueTextSegment[];
|
|
636
|
+
titleIcon?: IdentityRow['titleIcon'];
|
|
637
|
+
secondaryTitleIcon?: IdentityRow['titleIcon'];
|
|
575
638
|
secondaryLeadingText?: string;
|
|
576
639
|
secondaryText?: string;
|
|
640
|
+
secondaryTextSegments?: readonly ValueTextSegment[];
|
|
577
641
|
weight?: 1 | 2 | 3;
|
|
578
642
|
alignment?: 'start' | 'center' | 'end';
|
|
579
643
|
tone?: 'primary' | 'secondary' | 'positive' | 'negative';
|
|
@@ -585,6 +649,7 @@ export type DataRow = RowBase &
|
|
|
585
649
|
type: 'dataRow';
|
|
586
650
|
leading?: LeadingVisual;
|
|
587
651
|
columns: readonly DataColumn[];
|
|
652
|
+
trailing?: readonly TrailingAccessory[];
|
|
588
653
|
checkbox?: Extract<TrailingAccessory, { kind: 'checkbox' }>;
|
|
589
654
|
index?: number;
|
|
590
655
|
badge?: BadgeModel;
|
|
@@ -766,9 +831,11 @@ export type SectionHeaderRow = RowBase &
|
|
|
766
831
|
indexTitle?: string;
|
|
767
832
|
variant?: 'summary' | 'gallery' | 'history';
|
|
768
833
|
title: string;
|
|
834
|
+
leadingBadge?: BadgeModel;
|
|
769
835
|
titleLoading?: boolean;
|
|
770
836
|
subtitle?: string;
|
|
771
837
|
value?: string;
|
|
838
|
+
valueHelp?: RowTextHelp;
|
|
772
839
|
valueActionKey?: string;
|
|
773
840
|
titleIcon?: Extract<TrailingAccessory, { kind: 'icon' }>;
|
|
774
841
|
valueIcon?: Extract<TrailingAccessory, { kind: 'icon' }>;
|
|
@@ -789,6 +856,21 @@ export type ActionRow = RowBase &
|
|
|
789
856
|
style?: ActionRowStyle;
|
|
790
857
|
}>;
|
|
791
858
|
|
|
859
|
+
export type ButtonGroupRow = RowBase &
|
|
860
|
+
Readonly<{
|
|
861
|
+
type: 'buttonGroup';
|
|
862
|
+
buttons: readonly Readonly<{
|
|
863
|
+
key: string;
|
|
864
|
+
label: string;
|
|
865
|
+
actionKey: string;
|
|
866
|
+
disabled?: boolean;
|
|
867
|
+
loading?: boolean;
|
|
868
|
+
testID?: string;
|
|
869
|
+
accessibilityLabel?: string;
|
|
870
|
+
}>[];
|
|
871
|
+
style?: TemplateBoxStyle<'buttonGroup'>;
|
|
872
|
+
}>;
|
|
873
|
+
|
|
792
874
|
export type SystemRow = RowBase &
|
|
793
875
|
Readonly<{ style?: SystemRowStyle }> &
|
|
794
876
|
(
|
|
@@ -841,6 +923,7 @@ export type RowModel =
|
|
|
841
923
|
| MetricCardRow
|
|
842
924
|
| SectionHeaderRow
|
|
843
925
|
| ActionRow
|
|
926
|
+
| ButtonGroupRow
|
|
844
927
|
| SystemRow;
|
|
845
928
|
|
|
846
929
|
export type NativeListTheme = Readonly<{
|
|
@@ -853,6 +936,8 @@ export type NativeListTheme = Readonly<{
|
|
|
853
936
|
rowBackground: string;
|
|
854
937
|
rowSelectedBackground: string;
|
|
855
938
|
rowPressedBackground?: string;
|
|
939
|
+
/** Web-only enabled-row hover color; defaults to rowPressedBackground. */
|
|
940
|
+
rowHoverBackground?: string;
|
|
856
941
|
subduedBackground?: string;
|
|
857
942
|
strongBackground?: string;
|
|
858
943
|
/** Web-only background for a displayed declared image fallback. */
|
|
@@ -865,6 +950,8 @@ export type NativeListTheme = Readonly<{
|
|
|
865
950
|
separator: string;
|
|
866
951
|
accent: string;
|
|
867
952
|
positive: string;
|
|
953
|
+
/** Optional exact background for the success tone of section leading badges. */
|
|
954
|
+
successBackground?: string;
|
|
868
955
|
negative: string;
|
|
869
956
|
criticalBackground?: string;
|
|
870
957
|
inverseBackground?: string;
|
|
@@ -911,6 +998,16 @@ export type NativeListListStyle = Readonly<{
|
|
|
911
998
|
export type NativeListSnapshot = Readonly<{
|
|
912
999
|
schemaVersion: 1;
|
|
913
1000
|
generation: number;
|
|
1001
|
+
/** One-shot animation for a user-initiated row-key insertion or removal. */
|
|
1002
|
+
structuralTransition?: Readonly<{
|
|
1003
|
+
id: string;
|
|
1004
|
+
durationMs?: number;
|
|
1005
|
+
anchorKey?: string;
|
|
1006
|
+
}>;
|
|
1007
|
+
disclosure?: Readonly<{
|
|
1008
|
+
expanded: boolean;
|
|
1009
|
+
expandedTrailingInset?: number;
|
|
1010
|
+
}>;
|
|
914
1011
|
layout: Readonly<{
|
|
915
1012
|
kind: NativeListLayout;
|
|
916
1013
|
orientation?: NativeListOrientation;
|
|
@@ -968,7 +1065,13 @@ export type RowPatch =
|
|
|
968
1065
|
IdentityRow,
|
|
969
1066
|
| CommonPatchFields
|
|
970
1067
|
| 'title'
|
|
1068
|
+
| 'titleSuffixes'
|
|
1069
|
+
| 'titleIcon'
|
|
1070
|
+
| 'secondaryTitleIcon'
|
|
971
1071
|
| 'subtitle'
|
|
1072
|
+
| 'subtitleLines'
|
|
1073
|
+
| 'contentHeight'
|
|
1074
|
+
| 'longPressActionKey'
|
|
972
1075
|
| 'tertiary'
|
|
973
1076
|
| 'tertiaryTone'
|
|
974
1077
|
| 'badges'
|
|
@@ -1010,6 +1113,7 @@ export type RowPatch =
|
|
|
1010
1113
|
| 'primaryAmount'
|
|
1011
1114
|
| 'secondaryAmount'
|
|
1012
1115
|
| 'leading'
|
|
1116
|
+
| 'hideLeading'
|
|
1013
1117
|
| 'secondaryLeading'
|
|
1014
1118
|
| 'footerActions'
|
|
1015
1119
|
>
|
|
@@ -1026,6 +1130,7 @@ export type RowPatch =
|
|
|
1026
1130
|
| 'title'
|
|
1027
1131
|
| 'body'
|
|
1028
1132
|
| 'bodyLines'
|
|
1133
|
+
| 'presentation'
|
|
1029
1134
|
| 'time'
|
|
1030
1135
|
| 'thumbnail'
|
|
1031
1136
|
>
|
|
@@ -1040,6 +1145,7 @@ export type RowPatch =
|
|
|
1040
1145
|
| CommonPatchFields
|
|
1041
1146
|
| 'leading'
|
|
1042
1147
|
| 'columns'
|
|
1148
|
+
| 'trailing'
|
|
1043
1149
|
| 'checkbox'
|
|
1044
1150
|
| 'index'
|
|
1045
1151
|
| 'badge'
|
|
@@ -1124,6 +1230,8 @@ export type RowPatch =
|
|
|
1124
1230
|
| 'title'
|
|
1125
1231
|
| 'subtitle'
|
|
1126
1232
|
| 'value'
|
|
1233
|
+
| 'valueHelp'
|
|
1234
|
+
| 'leadingBadge'
|
|
1127
1235
|
| 'valueActionKey'
|
|
1128
1236
|
| 'titleActionKey'
|
|
1129
1237
|
| 'titleActionOnHover'
|
|
@@ -1148,6 +1256,11 @@ export type RowPatch =
|
|
|
1148
1256
|
>
|
|
1149
1257
|
>;
|
|
1150
1258
|
}>
|
|
1259
|
+
| Readonly<{
|
|
1260
|
+
type: 'buttonGroup';
|
|
1261
|
+
key: string;
|
|
1262
|
+
changes: Partial<Pick<ButtonGroupRow, CommonPatchFields | 'buttons'>>;
|
|
1263
|
+
}>
|
|
1151
1264
|
| Readonly<{
|
|
1152
1265
|
type: 'system';
|
|
1153
1266
|
key: string;
|
|
@@ -1164,6 +1277,7 @@ export type NativeListActionSource =
|
|
|
1164
1277
|
| 'leadingAction'
|
|
1165
1278
|
| 'trailingAccessory'
|
|
1166
1279
|
| 'footerAction'
|
|
1280
|
+
| 'buttonGroup'
|
|
1167
1281
|
| 'description'
|
|
1168
1282
|
| 'mediaClose';
|
|
1169
1283
|
|