@onekeyfe/react-native-native-list 6.7.0-alpha.398 → 6.7.0-alpha.400
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/NativeListIdentityRenderer.kt +33 -2
- package/android/src/main/java/com/onekey/nativelist/NativeListModels.kt +3 -1
- package/android/src/main/java/com/onekey/nativelist/NativeListView.kt +31 -8
- package/docs/ROW_TEMPLATES.md +4 -0
- package/docs/SPEC.md +161 -35
- package/ios/NativeListIdentityRenderer.swift +50 -3
- package/ios/RNCNativeListView.swift +6 -1
- package/ios/tests/NativeListLifecycleTests.swift +73 -4
- package/lib/module/NativeList.web.js +9 -12
- package/lib/module/models.js +2 -0
- package/lib/module/validation.js +627 -114
- package/lib/module/web/NativeListWebEngine.js +3 -9
- package/lib/module/web/templates/IdentityRowRenderer.js +58 -4
- package/lib/typescript/src/models.d.ts +25 -14
- package/lib/typescript/src/validation.d.ts +7 -2
- package/package.json +3 -3
- package/src/NativeList.web.tsx +10 -14
- package/src/models.ts +144 -153
- package/src/validation.ts +1617 -167
- package/src/web/NativeListWebEngine.ts +6 -10
- package/src/web/templates/IdentityRowRenderer.ts +70 -4
|
@@ -11,7 +11,7 @@ import { measureRailWidth } from "./templates/RailRowRenderer.js";
|
|
|
11
11
|
import { rowRenderer, rowRendererKey, recycleRowBody } from "./templates/RowRendererRegistry.js";
|
|
12
12
|
import { checkboxStateForKeys, checkboxStateForSection, isRowPressEnabled, isSelectableRow, reduceSelection, selectionStateFromSnapshot } from "../selection.js";
|
|
13
13
|
import { calculateAlignedScrollOffset, resolveLocationIndex, scrollFailure, validateOffset } from "../scrolling.js";
|
|
14
|
-
import {
|
|
14
|
+
import { applyValidatedRowPatches, containNativeListInput, emptyNativeListSnapshot, validateSnapshot } from "../validation.js";
|
|
15
15
|
import { avatarPrefetchWindow } from "../avatarPrefetch.js";
|
|
16
16
|
import { acquireNativeListAvatar, canonicalNativeListAvatarUri } from "./NativeListWebAvatarCache.js";
|
|
17
17
|
const SECTION_INDEX_CONTENT_INSET = 16;
|
|
@@ -2007,20 +2007,14 @@ export class NativeListWebEngine {
|
|
|
2007
2007
|
return this.snapshot;
|
|
2008
2008
|
}
|
|
2009
2009
|
applyPatches(patches) {
|
|
2010
|
-
const accepted = containNativeListInput(() =>
|
|
2011
|
-
const normalized = validatePatches(patches);
|
|
2012
|
-
return {
|
|
2013
|
-
patches: normalized,
|
|
2014
|
-
snapshot: applyRowPatches(this.snapshot, normalized)
|
|
2015
|
-
};
|
|
2016
|
-
});
|
|
2010
|
+
const accepted = containNativeListInput(() => applyValidatedRowPatches(this.snapshot, patches));
|
|
2017
2011
|
if (!accepted || accepted.patches.length === 0) return;
|
|
2018
2012
|
patches = accepted.patches;
|
|
2019
2013
|
const next = accepted.snapshot;
|
|
2020
2014
|
const selection = new Set(this.selectedKeys);
|
|
2021
2015
|
patches.forEach(patch => {
|
|
2022
2016
|
const selected = 'selected' in patch.changes ? patch.changes.selected : undefined;
|
|
2023
|
-
if (selected === true) selection.add(patch.key);else if (selected === false) selection.delete(patch.key);
|
|
2017
|
+
if (selected === true) selection.add(patch.key);else if (selected === false || selected === null) selection.delete(patch.key);
|
|
2024
2018
|
});
|
|
2025
2019
|
const tableValuesOnly = patches.every(patch => {
|
|
2026
2020
|
const index = this.rows.findIndex(row => row.key === patch.key);
|
|
@@ -100,7 +100,7 @@ function createIdentityRow(document, primitives, row) {
|
|
|
100
100
|
});
|
|
101
101
|
column.insertBefore(segments, titleElement.nextSibling);
|
|
102
102
|
}
|
|
103
|
-
if (row.titleIcon || row.secondaryTitleIcon) {
|
|
103
|
+
if (row.titleIcon || row.secondaryTitleIcon || row.titleImage) {
|
|
104
104
|
const badgeLine = titleElement.querySelector(':scope > .ok-native-list-badges');
|
|
105
105
|
const titleText = createElement(document, 'span', 'ok-native-list-identity-title-text');
|
|
106
106
|
while (titleElement.firstChild && titleElement.firstChild !== badgeLine) titleText.appendChild(titleElement.firstChild);
|
|
@@ -116,6 +116,38 @@ function createIdentityRow(document, primitives, row) {
|
|
|
116
116
|
if (descriptor.accessibilityLabel) icon.setAttribute('aria-label', descriptor.accessibilityLabel);else icon.setAttribute('aria-hidden', 'true');
|
|
117
117
|
return icon;
|
|
118
118
|
});
|
|
119
|
+
if (row.titleImage) {
|
|
120
|
+
const descriptor = row.titleImage;
|
|
121
|
+
const holder = createElement(document, 'span', 'ok-native-list-identity-title-image');
|
|
122
|
+
Object.assign(holder.style, {
|
|
123
|
+
flex: `0 0 ${descriptor.image.width}px`,
|
|
124
|
+
width: `${descriptor.image.width}px`,
|
|
125
|
+
height: `${descriptor.image.height}px`,
|
|
126
|
+
borderRadius: '50%',
|
|
127
|
+
overflow: 'hidden'
|
|
128
|
+
});
|
|
129
|
+
const image = primitives.thumbnail(descriptor.image);
|
|
130
|
+
if (image) {
|
|
131
|
+
Object.assign(image.style, {
|
|
132
|
+
width: '100%',
|
|
133
|
+
height: '100%',
|
|
134
|
+
borderRadius: 'inherit',
|
|
135
|
+
objectFit: descriptor.image.contentFit === 'center' ? 'none' : descriptor.image.contentFit ?? 'cover'
|
|
136
|
+
});
|
|
137
|
+
image.setAttribute('alt', '');
|
|
138
|
+
holder.appendChild(image);
|
|
139
|
+
}
|
|
140
|
+
if (descriptor.hoverActionKey) {
|
|
141
|
+
setData(holder, 'nativeListHoverAction', descriptor.hoverActionKey);
|
|
142
|
+
setData(holder, 'nativeListIconHelp', true);
|
|
143
|
+
markActionAnchorSource(holder, 'leadingAction');
|
|
144
|
+
}
|
|
145
|
+
if (descriptor.accessibilityLabel) {
|
|
146
|
+
holder.setAttribute('role', 'img');
|
|
147
|
+
holder.setAttribute('aria-label', descriptor.accessibilityLabel);
|
|
148
|
+
} else holder.setAttribute('aria-hidden', 'true');
|
|
149
|
+
icons.push(holder);
|
|
150
|
+
}
|
|
119
151
|
titleText.after(...icons);
|
|
120
152
|
titleElement.style.display = 'flex';
|
|
121
153
|
titleElement.style.alignItems = 'center';
|
|
@@ -144,7 +176,7 @@ function createIdentityRow(document, primitives, row) {
|
|
|
144
176
|
const label = createElement(document, 'span', 'ok-native-list-identity-title-suffix', suffix.text);
|
|
145
177
|
label.style.flex = '0 1 auto';
|
|
146
178
|
label.style.minWidth = '0';
|
|
147
|
-
label.style.marginInlineStart = row.titleIcon || row.secondaryTitleIcon ? '4px' : '8px';
|
|
179
|
+
label.style.marginInlineStart = row.titleIcon || row.secondaryTitleIcon || row.titleImage ? '4px' : '8px';
|
|
148
180
|
label.style.maxWidth = suffix.maxWidth === undefined ? '' : `${suffix.maxWidth}px`;
|
|
149
181
|
label.style.overflow = 'hidden';
|
|
150
182
|
label.style.textOverflow = 'ellipsis';
|
|
@@ -157,7 +189,7 @@ function createIdentityRow(document, primitives, row) {
|
|
|
157
189
|
titleElement.insertBefore(label, badges);
|
|
158
190
|
});
|
|
159
191
|
}
|
|
160
|
-
if (row.badges?.length && !row.titleIcon && !row.secondaryTitleIcon) {
|
|
192
|
+
if (row.badges?.length && !row.titleIcon && !row.secondaryTitleIcon && !row.titleImage) {
|
|
161
193
|
const titleText = titleElement.querySelector(':scope > [data-nl-slot="title"]');
|
|
162
194
|
const badges = titleElement.querySelector(':scope > .ok-native-list-badges');
|
|
163
195
|
titleElement.style.display = 'flex';
|
|
@@ -267,8 +299,22 @@ function bind(body, row, primitives) {
|
|
|
267
299
|
const v = views.get(body);
|
|
268
300
|
v.primitives = primitives;
|
|
269
301
|
let used = false;
|
|
302
|
+
let usedTitleImage = false;
|
|
270
303
|
const next = createIdentityRow(body.ownerDocument, {
|
|
271
304
|
...primitives,
|
|
305
|
+
thumbnail: source => {
|
|
306
|
+
usedTitleImage = true;
|
|
307
|
+
const key = JSON.stringify([row.key, source]);
|
|
308
|
+
if (v.titleImage?.key !== key) {
|
|
309
|
+
if (v.titleImage) primitives.dispose(v.titleImage.node);
|
|
310
|
+
const node = primitives.thumbnail(source);
|
|
311
|
+
v.titleImage = node ? {
|
|
312
|
+
key,
|
|
313
|
+
node
|
|
314
|
+
} : undefined;
|
|
315
|
+
}
|
|
316
|
+
return v.titleImage?.node;
|
|
317
|
+
},
|
|
272
318
|
visual: (source, presentation) => {
|
|
273
319
|
used = true;
|
|
274
320
|
const key = JSON.stringify([row.key, source, presentation]);
|
|
@@ -293,6 +339,10 @@ function bind(body, row, primitives) {
|
|
|
293
339
|
primitives.dispose(v.visual.node);
|
|
294
340
|
v.visual = undefined;
|
|
295
341
|
}
|
|
342
|
+
if (!usedTitleImage && v.titleImage) {
|
|
343
|
+
primitives.dispose(v.titleImage.node);
|
|
344
|
+
v.titleImage = undefined;
|
|
345
|
+
}
|
|
296
346
|
body.replaceChildren(...Array.from(next.childNodes));
|
|
297
347
|
body.className = next.className;
|
|
298
348
|
body.style.cssText = next.style.cssText;
|
|
@@ -357,7 +407,11 @@ function create(document, row, primitives) {
|
|
|
357
407
|
function recycle(body) {
|
|
358
408
|
const v = views.get(body);
|
|
359
409
|
if (v?.visual) v.primitives.dispose(v.visual.node);
|
|
360
|
-
if (v) v.
|
|
410
|
+
if (v?.titleImage) v.primitives.dispose(v.titleImage.node);
|
|
411
|
+
if (v) {
|
|
412
|
+
v.visual = undefined;
|
|
413
|
+
v.titleImage = undefined;
|
|
414
|
+
}
|
|
361
415
|
body.replaceChildren();
|
|
362
416
|
}
|
|
363
417
|
export const identityRowRenderer = {
|
|
@@ -428,6 +428,13 @@ export type IdentityRow = RowBase & Readonly<{
|
|
|
428
428
|
}>;
|
|
429
429
|
/** Web-only additional help icon after titleIcon. */
|
|
430
430
|
secondaryTitleIcon?: IdentityRow['titleIcon'];
|
|
431
|
+
/** Circular image after title icons; image dimensions must be square, 1...40. */
|
|
432
|
+
titleImage?: Readonly<{
|
|
433
|
+
image: ImageSource;
|
|
434
|
+
accessibilityLabel?: string;
|
|
435
|
+
/** Web-only help anchor; the consumer owns tooltip content. */
|
|
436
|
+
hoverActionKey?: string;
|
|
437
|
+
}>;
|
|
431
438
|
subtitle?: string;
|
|
432
439
|
tertiary?: string;
|
|
433
440
|
tertiaryTone?: 'secondary' | 'info';
|
|
@@ -864,57 +871,61 @@ export type NativeListSnapshot = Readonly<{
|
|
|
864
871
|
listStyle?: NativeListListStyle;
|
|
865
872
|
}>;
|
|
866
873
|
type CommonPatchFields = 'revision' | 'disabled' | 'selected' | 'separator' | 'height' | 'heightRounding' | 'opacity' | 'pressDisabled' | 'accessibilityLabel' | 'style';
|
|
874
|
+
/** Null removes optional fields; undefined leaves the existing value unchanged. */
|
|
875
|
+
export type PatchClearableChanges<Row, Keys extends keyof Row> = Readonly<{
|
|
876
|
+
[K in Keys]?: undefined extends Row[K] ? Row[K] | null : Row[K];
|
|
877
|
+
}>;
|
|
867
878
|
export type RowPatch = Readonly<{
|
|
868
879
|
type: 'identity';
|
|
869
880
|
key: string;
|
|
870
|
-
changes:
|
|
881
|
+
changes: PatchClearableChanges<IdentityRow, CommonPatchFields | 'title' | 'titleSuffixes' | 'titleIcon' | 'secondaryTitleIcon' | 'titleImage' | 'subtitle' | 'subtitleLines' | 'contentHeight' | 'longPressActionKey' | 'tertiary' | 'tertiaryTone' | 'badges' | 'trailing' | 'leading' | 'leadingAction' | 'titleMatch' | 'titleActionKey' | 'titleActionOnHover' | 'subtitleSegments'>;
|
|
871
882
|
}> | Readonly<{
|
|
872
883
|
type: 'rail';
|
|
873
884
|
key: string;
|
|
874
|
-
changes:
|
|
885
|
+
changes: PatchClearableChanges<RailRow, CommonPatchFields | 'title' | 'status' | 'badge' | 'visual'>;
|
|
875
886
|
}> | Readonly<{
|
|
876
887
|
type: 'activity';
|
|
877
888
|
key: string;
|
|
878
|
-
changes:
|
|
889
|
+
changes: PatchClearableChanges<ActivityRow, CommonPatchFields | 'title' | 'description' | 'status' | 'amounts' | 'badges' | 'presentation' | 'fee' | 'descriptionActionKey' | 'primaryAmount' | 'secondaryAmount' | 'leading' | 'hideLeading' | 'secondaryLeading' | 'footerActions'>;
|
|
879
890
|
}> | Readonly<{
|
|
880
891
|
type: 'message';
|
|
881
892
|
key: string;
|
|
882
|
-
changes:
|
|
893
|
+
changes: PatchClearableChanges<MessageRow, CommonPatchFields | 'unread' | 'title' | 'body' | 'bodyLines' | 'presentation' | 'time' | 'thumbnail'>;
|
|
883
894
|
}> | Readonly<{
|
|
884
895
|
type: 'dataRow';
|
|
885
896
|
key: string;
|
|
886
|
-
changes:
|
|
897
|
+
changes: PatchClearableChanges<DataRow, CommonPatchFields | 'leading' | 'columns' | 'trailing' | 'checkbox' | 'index' | 'badge' | 'badges' | 'favorite' | 'favoriteActive'>;
|
|
887
898
|
}> | Readonly<{
|
|
888
899
|
type: 'market';
|
|
889
900
|
key: string;
|
|
890
|
-
changes:
|
|
901
|
+
changes: PatchClearableChanges<MarketRow, CommonPatchFields | 'leadingAction' | 'leading' | 'title' | 'subtitle' | 'subtitleSegments' | 'price' | 'priceSegments' | 'change' | 'badges' | 'pressActionKey' | 'pressInActionKey' | 'longPressActionKey' | 'diagnostics' | 'webTable' | 'style'>;
|
|
891
902
|
}> | Readonly<{
|
|
892
903
|
type: 'mediaTile';
|
|
893
904
|
key: string;
|
|
894
|
-
changes:
|
|
905
|
+
changes: PatchClearableChanges<MediaTileRow, CommonPatchFields | 'image' | 'imageState' | 'media' | 'networkImage' | 'title' | 'subtitle' | 'badge' | 'closeActionKey'>;
|
|
895
906
|
}> | Readonly<{
|
|
896
907
|
type: 'metricCard';
|
|
897
908
|
key: string;
|
|
898
|
-
changes:
|
|
909
|
+
changes: PatchClearableChanges<MetricCardRow, CommonPatchFields | 'variant' | 'title' | 'value' | 'subtitle' | 'trend' | 'trendTone' | 'visual' | 'badge' | 'metrics' | 'progress'>;
|
|
899
910
|
}> | Readonly<{
|
|
900
911
|
type: 'sectionHeader';
|
|
901
912
|
key: string;
|
|
902
|
-
changes:
|
|
913
|
+
changes: PatchClearableChanges<SectionHeaderRow, CommonPatchFields | 'variant' | 'titleLoading' | 'title' | 'subtitle' | 'value' | 'valueHelp' | 'leadingBadge' | 'valueActionKey' | 'titleActionKey' | 'titleActionOnHover' | 'titleIcon' | 'valueIcon' | 'checkbox'>;
|
|
903
914
|
}> | Readonly<{
|
|
904
915
|
type: 'action';
|
|
905
916
|
key: string;
|
|
906
|
-
changes:
|
|
917
|
+
changes: PatchClearableChanges<ActionRow, CommonPatchFields | 'title' | 'tone' | 'icon' | 'checkbox' | 'trailing'>;
|
|
907
918
|
}> | Readonly<{
|
|
908
919
|
type: 'buttonGroup';
|
|
909
920
|
key: string;
|
|
910
|
-
changes:
|
|
921
|
+
changes: PatchClearableChanges<ButtonGroupRow, CommonPatchFields | 'buttons'>;
|
|
911
922
|
}> | Readonly<{
|
|
912
923
|
type: 'system';
|
|
913
924
|
key: string;
|
|
914
925
|
changes: Readonly<{
|
|
915
|
-
revision?: number;
|
|
916
|
-
disabled?: boolean;
|
|
917
|
-
message?: string;
|
|
926
|
+
revision?: number | null;
|
|
927
|
+
disabled?: boolean | null;
|
|
928
|
+
message?: string | null;
|
|
918
929
|
}>;
|
|
919
930
|
}>;
|
|
920
931
|
export type NativeListActionSource = 'row' | 'marketBadge' | 'leadingAction' | 'trailingAccessory' | 'footerAction' | 'buttonGroup' | 'description' | 'mediaClose';
|
|
@@ -10,13 +10,18 @@ export declare function ownValidatedNativeSnapshot(snapshot: NativeListSnapshot)
|
|
|
10
10
|
export declare function validatePatches(patches: readonly RowPatch[]): readonly RowPatch[];
|
|
11
11
|
/** Plan value-only patches; callers own valid immutable snapshots and commit state. */
|
|
12
12
|
export declare function planNativeListPatches(previous: NativeListSnapshot, next: NativeListSnapshot): readonly RowPatch[] | undefined;
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
/** One canonical batch owns both accepted rows and the native wire payload. */
|
|
14
|
+
export declare function applyValidatedRowPatches(snapshot: NativeListSnapshot, patches: readonly RowPatch[]): {
|
|
15
|
+
patches: readonly RowPatch[];
|
|
16
|
+
snapshot: NativeListSnapshot;
|
|
17
|
+
json: string;
|
|
18
|
+
};
|
|
15
19
|
export declare function applyOwnedNativePatches(snapshot: NativeListSnapshot, patches: readonly RowPatch[]): {
|
|
16
20
|
patches: readonly RowPatch[];
|
|
17
21
|
snapshot: NativeListSnapshot;
|
|
18
22
|
json: string;
|
|
19
23
|
};
|
|
24
|
+
export declare function applyRowPatches(snapshot: NativeListSnapshot, patches: readonly RowPatch[]): NativeListSnapshot;
|
|
20
25
|
export declare function serializeSnapshot(snapshot: NativeListSnapshot): string;
|
|
21
26
|
export declare function serializePatches(patches: readonly RowPatch[]): string;
|
|
22
27
|
//# sourceMappingURL=validation.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/react-native-native-list",
|
|
3
|
-
"version": "6.7.0-alpha.
|
|
3
|
+
"version": "6.7.0-alpha.400",
|
|
4
4
|
"description": "Template-driven native RecyclerView and UICollectionView for React Native",
|
|
5
5
|
"source": "./src/index.ts",
|
|
6
6
|
"main": "./lib/module/index.js",
|
|
@@ -86,8 +86,8 @@
|
|
|
86
86
|
"typescript": "^5.9.2"
|
|
87
87
|
},
|
|
88
88
|
"peerDependencies": {
|
|
89
|
-
"@onekeyfe/react-native-image": "6.7.0-alpha.
|
|
90
|
-
"@onekeyfe/react-native-native-logger": "6.7.0-alpha.
|
|
89
|
+
"@onekeyfe/react-native-image": "6.7.0-alpha.400",
|
|
90
|
+
"@onekeyfe/react-native-native-logger": "6.7.0-alpha.400",
|
|
91
91
|
"react": "*",
|
|
92
92
|
"react-native": "*",
|
|
93
93
|
"react-native-nitro-modules": "0.37.0"
|
package/src/NativeList.web.tsx
CHANGED
|
@@ -25,7 +25,6 @@ import {
|
|
|
25
25
|
applyRowPatches,
|
|
26
26
|
containNativeListInput,
|
|
27
27
|
emptyNativeListSnapshot,
|
|
28
|
-
validatePatches,
|
|
29
28
|
validateSnapshot,
|
|
30
29
|
} from './validation';
|
|
31
30
|
import {
|
|
@@ -454,20 +453,17 @@ export const NativeList = forwardRef<NativeListRef, NativeListProps>(
|
|
|
454
453
|
synchronizeSlots(next);
|
|
455
454
|
},
|
|
456
455
|
applyPatches(patches) {
|
|
457
|
-
const
|
|
458
|
-
if (
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
);
|
|
467
|
-
if (next) snapshotRef.current = next;
|
|
468
|
-
}
|
|
469
|
-
acceptedSnapshotRef.current = snapshotRef.current;
|
|
456
|
+
const engine = engineRef.current;
|
|
457
|
+
if (engine) {
|
|
458
|
+
engine.applyPatches(patches);
|
|
459
|
+
snapshotRef.current = engine.currentSnapshot();
|
|
460
|
+
} else {
|
|
461
|
+
const next = containNativeListInput(() =>
|
|
462
|
+
applyRowPatches(snapshotRef.current, patches)
|
|
463
|
+
);
|
|
464
|
+
if (next) snapshotRef.current = next;
|
|
470
465
|
}
|
|
466
|
+
acceptedSnapshotRef.current = snapshotRef.current;
|
|
471
467
|
},
|
|
472
468
|
reconcileSelection(selectedKeys) {
|
|
473
469
|
engineRef.current?.reconcileSelection(selectedKeys);
|