@onekeyfe/react-native-native-list 3.0.157-alpha.249 → 3.0.157-alpha.251

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.
Files changed (130) hide show
  1. package/README.md +21 -5
  2. package/android/src/main/java/com/onekey/nativelist/NativeListAccessoryStack.kt +711 -0
  3. package/android/src/main/java/com/onekey/nativelist/NativeListActionRenderer.kt +169 -0
  4. package/android/src/main/java/com/onekey/nativelist/NativeListActivityRenderer.kt +243 -0
  5. package/android/src/main/java/com/onekey/nativelist/NativeListAdapter.kt +20 -6
  6. package/android/src/main/java/com/onekey/nativelist/NativeListDataRowRenderer.kt +487 -0
  7. package/android/src/main/java/com/onekey/nativelist/NativeListIdentityRenderer.kt +820 -0
  8. package/android/src/main/java/com/onekey/nativelist/NativeListImageSlot.kt +147 -0
  9. package/android/src/main/java/com/onekey/nativelist/NativeListLeadingVisual.kt +453 -0
  10. package/android/src/main/java/com/onekey/nativelist/NativeListMarketRenderer.kt +997 -0
  11. package/android/src/main/java/com/onekey/nativelist/NativeListMediaTileRenderer.kt +234 -0
  12. package/android/src/main/java/com/onekey/nativelist/NativeListMessageRenderer.kt +232 -0
  13. package/android/src/main/java/com/onekey/nativelist/NativeListMetricCardRenderer.kt +551 -0
  14. package/android/src/main/java/com/onekey/nativelist/NativeListModels.kt +24 -0
  15. package/android/src/main/java/com/onekey/nativelist/NativeListPrimitives.kt +475 -0
  16. package/android/src/main/java/com/onekey/nativelist/NativeListRailRenderer.kt +143 -0
  17. package/android/src/main/java/com/onekey/nativelist/NativeListRendererRegistry.kt +42 -0
  18. package/android/src/main/java/com/onekey/nativelist/NativeListRendererRowView.kt +415 -0
  19. package/android/src/main/java/com/onekey/nativelist/NativeListResolvedText.kt +163 -0
  20. package/android/src/main/java/com/onekey/nativelist/NativeListRowHost.kt +70 -0
  21. package/android/src/main/java/com/onekey/nativelist/NativeListSectionHeaderRenderer.kt +748 -0
  22. package/android/src/main/java/com/onekey/nativelist/NativeListSystemRenderer.kt +464 -0
  23. package/android/src/main/java/com/onekey/nativelist/NativeListTextView.kt +25 -0
  24. package/android/src/main/java/com/onekey/nativelist/NativeListView.kt +174 -104
  25. package/android/src/main/java/com/onekey/nativelist/NativeListWalletGroupRenderer.kt +407 -0
  26. package/android/src/test/java/com/margelo/nitro/nativelist/NativeListSelectorBackgroundColorTest.kt +34 -0
  27. package/docs/DESIGN.md +518 -9
  28. package/docs/MIGRATION_BASELINE.md +117 -0
  29. package/docs/ROW_TEMPLATES.md +359 -0
  30. package/docs/SPEC.md +358 -0
  31. package/docs/STYLE_SPEC.md +978 -0
  32. package/docs/images/list-capabilities.svg +39 -0
  33. package/docs/images/row-action.svg +17 -0
  34. package/docs/images/row-activity.svg +24 -0
  35. package/docs/images/row-data.svg +24 -0
  36. package/docs/images/row-identity.svg +25 -0
  37. package/docs/images/row-market.svg +23 -0
  38. package/docs/images/row-media-tile.svg +26 -0
  39. package/docs/images/row-message.svg +20 -0
  40. package/docs/images/row-metric-card.svg +33 -0
  41. package/docs/images/row-rail.svg +18 -0
  42. package/docs/images/row-section-header.svg +19 -0
  43. package/docs/images/row-style-regions.svg +30 -0
  44. package/docs/images/row-system.svg +30 -0
  45. package/docs/images/row-wallet-group.svg +26 -0
  46. package/ios/NativeListAccessoryStack.swift +680 -0
  47. package/ios/NativeListActionRenderer.swift +108 -0
  48. package/ios/NativeListActivityRenderer.swift +179 -0
  49. package/ios/NativeListDataRowRenderer.swift +444 -0
  50. package/ios/NativeListIdentityRenderer.swift +685 -0
  51. package/ios/NativeListImageSlot.swift +140 -0
  52. package/ios/NativeListLeadingVisual.swift +372 -0
  53. package/ios/NativeListMarketRenderer.swift +698 -0
  54. package/ios/NativeListMediaTileRenderer.swift +184 -0
  55. package/ios/NativeListMessageRenderer.swift +168 -0
  56. package/ios/NativeListMetricCardRenderer.swift +420 -0
  57. package/ios/NativeListModels.swift +32 -0
  58. package/ios/NativeListRailRenderer.swift +152 -0
  59. package/ios/NativeListRendererCell.swift +311 -0
  60. package/ios/NativeListRendererRegistry.swift +50 -0
  61. package/ios/NativeListResolvedText.swift +122 -0
  62. package/ios/NativeListRowHost.swift +63 -0
  63. package/ios/NativeListSectionHeaderRenderer.swift +708 -0
  64. package/ios/NativeListSystemRenderer.swift +568 -0
  65. package/ios/NativeListTextLabel.swift +42 -0
  66. package/ios/NativeListTextStyles.swift +236 -0
  67. package/ios/NativeListWalletGroupRenderer.swift +255 -0
  68. package/ios/RNCNativeListView.swift +111 -180
  69. package/lib/module/models.js +54 -0
  70. package/lib/module/validation.js +324 -26
  71. package/lib/module/web/NativeListWebEngine.js +148 -936
  72. package/lib/module/web/templates/ActionRowRenderer.js +97 -0
  73. package/lib/module/web/templates/ActivityRowRenderer.js +95 -0
  74. package/lib/module/web/templates/DataRowRenderer.js +145 -0
  75. package/lib/module/web/templates/IdentityRowRenderer.js +289 -0
  76. package/lib/module/web/templates/MarketRowRenderer.js +310 -0
  77. package/lib/module/web/templates/MediaTileRowRenderer.js +132 -0
  78. package/lib/module/web/templates/MessageRowRenderer.js +165 -0
  79. package/lib/module/web/templates/MetricCardRowRenderer.js +172 -0
  80. package/lib/module/web/templates/RailRowRenderer.js +98 -0
  81. package/lib/module/web/templates/RowContainerStyle.js +100 -0
  82. package/lib/module/web/templates/RowElements.js +93 -0
  83. package/lib/module/web/templates/RowRendererRegistry.js +70 -0
  84. package/lib/module/web/templates/RowText.js +121 -0
  85. package/lib/module/web/templates/RowVisual.js +43 -0
  86. package/lib/module/web/templates/SectionHeaderRowRenderer.js +146 -0
  87. package/lib/module/web/templates/SystemRowRenderer.js +161 -0
  88. package/lib/module/web/templates/WalletGroupRowRenderer.js +96 -0
  89. package/lib/typescript/src/models.d.ts +166 -13
  90. package/lib/typescript/src/web/NativeListWebEngine.d.ts +18 -16
  91. package/lib/typescript/src/web/templates/ActionRowRenderer.d.ts +15 -0
  92. package/lib/typescript/src/web/templates/ActivityRowRenderer.d.ts +15 -0
  93. package/lib/typescript/src/web/templates/DataRowRenderer.d.ts +15 -0
  94. package/lib/typescript/src/web/templates/IdentityRowRenderer.d.ts +16 -0
  95. package/lib/typescript/src/web/templates/MarketRowRenderer.d.ts +30 -0
  96. package/lib/typescript/src/web/templates/MediaTileRowRenderer.d.ts +15 -0
  97. package/lib/typescript/src/web/templates/MessageRowRenderer.d.ts +44 -0
  98. package/lib/typescript/src/web/templates/MetricCardRowRenderer.d.ts +15 -0
  99. package/lib/typescript/src/web/templates/RailRowRenderer.d.ts +28 -0
  100. package/lib/typescript/src/web/templates/RowContainerStyle.d.ts +9 -0
  101. package/lib/typescript/src/web/templates/RowElements.d.ts +38 -0
  102. package/lib/typescript/src/web/templates/RowRendererRegistry.d.ts +83 -0
  103. package/lib/typescript/src/web/templates/RowText.d.ts +9 -0
  104. package/lib/typescript/src/web/templates/RowVisual.d.ts +20 -0
  105. package/lib/typescript/src/web/templates/SectionHeaderRowRenderer.d.ts +15 -0
  106. package/lib/typescript/src/web/templates/SystemRowRenderer.d.ts +16 -0
  107. package/lib/typescript/src/web/templates/WalletGroupRowRenderer.d.ts +20 -0
  108. package/package.json +3 -3
  109. package/src/models.ts +281 -19
  110. package/src/validation.ts +501 -46
  111. package/src/web/NativeListWebEngine.ts +361 -1932
  112. package/src/web/templates/ActionRowRenderer.ts +123 -0
  113. package/src/web/templates/ActivityRowRenderer.ts +159 -0
  114. package/src/web/templates/DataRowRenderer.ts +212 -0
  115. package/src/web/templates/IdentityRowRenderer.ts +527 -0
  116. package/src/web/templates/MarketRowRenderer.ts +467 -0
  117. package/src/web/templates/MediaTileRowRenderer.ts +182 -0
  118. package/src/web/templates/MessageRowRenderer.ts +306 -0
  119. package/src/web/templates/MetricCardRowRenderer.ts +337 -0
  120. package/src/web/templates/RailRowRenderer.ts +136 -0
  121. package/src/web/templates/RowContainerStyle.ts +131 -0
  122. package/src/web/templates/RowElements.ts +160 -0
  123. package/src/web/templates/RowRendererRegistry.ts +111 -0
  124. package/src/web/templates/RowText.ts +181 -0
  125. package/src/web/templates/RowVisual.ts +115 -0
  126. package/src/web/templates/SectionHeaderRowRenderer.ts +243 -0
  127. package/src/web/templates/SystemRowRenderer.ts +282 -0
  128. package/src/web/templates/WalletGroupRowRenderer.ts +131 -0
  129. package/android/src/main/java/com/onekey/nativelist/NativeListRowView.kt +0 -4538
  130. package/ios/NativeListCell.swift +0 -4300
@@ -0,0 +1,123 @@
1
+ import { applyTabularNumbers, hasExplicitRowHeight } from './RowElements';
2
+ import type { ActionRow } from '../../models';
3
+ import { RowVisualStyle, type RowPrimitives } from './RowVisual';
4
+
5
+ type Views = {
6
+ title: HTMLElement;
7
+ controls: HTMLElement;
8
+ visual?: HTMLElement;
9
+ visualStyle?: RowVisualStyle;
10
+ visualKey: string;
11
+ primitives: RowPrimitives;
12
+ };
13
+ const views = new WeakMap<HTMLElement, Views>();
14
+ function bind(body: HTMLElement, row: ActionRow, primitives: RowPrimitives) {
15
+ const v = views.get(body)!;
16
+ v.primitives = primitives;
17
+ body.className =
18
+ 'ok-native-list-row ok-native-list-action-row' +
19
+ (row.presentation === 'accountSelector'
20
+ ? ' ok-native-list-account-action-row'
21
+ : '');
22
+ body.style.cssText = '';
23
+ body.removeAttribute('data-nl-container-background');
24
+ body.removeAttribute('data-nl-container-border');
25
+ const style = row.style;
26
+ if (style?.horizontalPadding !== undefined)
27
+ body.style.paddingInline = `${style.horizontalPadding}px`;
28
+ if (style?.verticalPadding !== undefined)
29
+ body.style.paddingBlock = `${style.verticalPadding}px`;
30
+ v.title.style.cssText = '';
31
+ v.title.textContent = row.title;
32
+ v.title.dataset.tone = row.tone ?? '';
33
+ if (row.presentation === 'accountSelector' && row.icon)
34
+ v.title.style.fontWeight = '500';
35
+ if (style?.title) primitives.textStyle(v.title, style.title);
36
+ const visualKey = JSON.stringify([row.key, row.icon]);
37
+ if (v.visualKey !== visualKey) {
38
+ if (v.visual) {
39
+ primitives.dispose(v.visual);
40
+ v.visual.remove();
41
+ }
42
+ v.visual = primitives.visual(row.icon);
43
+ v.visualStyle = v.visual ? new RowVisualStyle(v.visual) : undefined;
44
+ if (v.visual) body.insertBefore(v.visual, v.title);
45
+ v.visualKey = visualKey;
46
+ }
47
+ if (v.visualStyle && v.visual) {
48
+ const size = row.presentation === 'accountSelector' ? 32 : 40;
49
+ v.visualStyle.apply(
50
+ style?.image,
51
+ style?.image?.width ?? size,
52
+ style?.image?.height ?? size,
53
+ (style?.leadingGap ?? 12) - 12
54
+ );
55
+ }
56
+ v.controls.replaceChildren();
57
+ v.controls.style.cssText = '';
58
+ v.controls.removeAttribute('data-native-list-account-control');
59
+ const descriptors = row.trailing ?? [];
60
+ if (row.checkbox)
61
+ v.controls.appendChild(primitives.accessory(row.key, row.checkbox, 0));
62
+ if (descriptors.length) {
63
+ const trailing = body.ownerDocument.createElement('span');
64
+ trailing.className = 'ok-native-list-accessories';
65
+ if (style?.trailingGap !== undefined)
66
+ trailing.style.gap = `${style.trailingGap}px`;
67
+ if (
68
+ hasExplicitRowHeight(row) &&
69
+ row.presentation === 'accountSelector' &&
70
+ descriptors.length === 1 &&
71
+ descriptors[0]?.kind === 'icon' &&
72
+ descriptors[0].name === 'PlusSmallOutline'
73
+ )
74
+ trailing.dataset.nativeListAccountControl = 'createAddress';
75
+ let valueIndex = 0;
76
+ descriptors.forEach((descriptor, index) => {
77
+ const node = primitives.accessory(row.key, descriptor, index);
78
+ if (descriptor.kind === 'value' || descriptor.kind === 'valuePair') {
79
+ node.dataset.nlSlot = valueIndex++ === 0 ? 'value' : 'valueSecondary';
80
+ if (node.dataset.nlSlot === 'value' && style?.value)
81
+ primitives.textStyle(node, style.value);
82
+ }
83
+ trailing.appendChild(node);
84
+ });
85
+ v.controls.appendChild(trailing);
86
+ }
87
+ v.controls.style.display = 'contents';
88
+ if (row.presentation === 'accountSelector') applyTabularNumbers(body);
89
+ }
90
+ function create(document: Document, row: ActionRow, primitives: RowPrimitives) {
91
+ const body = document.createElement('div');
92
+ body.dataset.nlRenderer = 'action';
93
+ const title = document.createElement('span');
94
+ title.className = 'ok-native-list-action-title';
95
+ title.dataset.nlSlot = 'title';
96
+ const controls = document.createElement('span');
97
+ body.append(title, controls);
98
+ views.set(body, { title, controls, visualKey: '', primitives });
99
+ bind(body, row, primitives);
100
+ return body;
101
+ }
102
+ function recycle(body: HTMLElement) {
103
+ const v = views.get(body);
104
+ if (!v) return;
105
+ if (v.visual) {
106
+ v.primitives.dispose(v.visual);
107
+ v.visual.remove();
108
+ }
109
+ v.visual = undefined;
110
+ v.visualStyle = undefined;
111
+ v.visualKey = '';
112
+ v.controls.replaceChildren();
113
+ }
114
+ export const actionRowRenderer = {
115
+ key: 'action' as const,
116
+ create,
117
+ bind,
118
+ recycle,
119
+ measure: (row: ActionRow, _width: number) =>
120
+ row.presentation === 'accountSelector' ? 48 : row.icon ? 60 : 44,
121
+ measureRendered: (_body: HTMLElement, _row: ActionRow): number | undefined =>
122
+ undefined,
123
+ };
@@ -0,0 +1,159 @@
1
+ import type { ActivityRow } from '../../models';
2
+ import { createElement, tagSlot, markActionAnchorSource } from './RowElements';
3
+ import { RowVisualStyle, type RowPrimitives } from './RowVisual';
4
+ type Views = {
5
+ column: HTMLElement;
6
+ fields: Record<string, HTMLElement>;
7
+ amounts: HTMLElement;
8
+ actions: HTMLElement;
9
+ visuals: HTMLElement[];
10
+ visualStyles: RowVisualStyle[];
11
+ visualKey: string;
12
+ primitives: RowPrimitives;
13
+ };
14
+ const views = new WeakMap<HTMLElement, Views>();
15
+ function bind(body: HTMLElement, row: ActivityRow, primitives: RowPrimitives) {
16
+ const v = views.get(body)!;
17
+ v.primitives = primitives;
18
+ body.style.cssText = '';
19
+ body.removeAttribute('data-nl-container-background');
20
+ body.removeAttribute('data-nl-container-border');
21
+ const style = row.style;
22
+ if (style?.horizontalPadding !== undefined)
23
+ body.style.paddingInline = `${style.horizontalPadding}px`;
24
+ if (style?.verticalPadding !== undefined)
25
+ body.style.paddingBlock = `${style.verticalPadding}px`;
26
+ v.column.style.rowGap =
27
+ style?.lineGap !== undefined ? `${style.lineGap}px` : '';
28
+ v.amounts.style.gap =
29
+ style?.trailingGap !== undefined ? `${style.trailingGap}px` : '';
30
+ for (const key of [
31
+ 'title',
32
+ 'description',
33
+ 'status',
34
+ 'primaryAmount',
35
+ 'secondaryAmount',
36
+ ] as const) {
37
+ const node = v.fields[key]!;
38
+ node.style.cssText = '';
39
+ node.textContent = row[key] ?? '';
40
+ node.hidden = !row[key];
41
+ if (style?.[key]) primitives.textStyle(node, style[key]!);
42
+ }
43
+ const visualKey = JSON.stringify([
44
+ row.key,
45
+ row.leading,
46
+ row.secondaryLeading,
47
+ ]);
48
+ if (v.visualKey !== visualKey) {
49
+ v.visuals.forEach((node) => {
50
+ primitives.dispose(node);
51
+ node.remove();
52
+ });
53
+ v.visuals = [row.leading, row.secondaryLeading]
54
+ .map((source) => primitives.visual(source))
55
+ .filter((node): node is HTMLElement => !!node);
56
+ v.visuals.forEach((node) => body.insertBefore(node, v.column));
57
+ v.visualStyles = v.visuals.map((node) => new RowVisualStyle(node));
58
+ v.visualKey = visualKey;
59
+ }
60
+ if (v.visualStyles[0])
61
+ v.visualStyles[0].apply(
62
+ style?.image,
63
+ style?.image?.width ?? 40,
64
+ style?.image?.height ?? 40,
65
+ (style?.leadingGap ?? 12) - 12
66
+ );
67
+ v.actions.replaceChildren();
68
+ v.actions.hidden = !row.footerActions?.length;
69
+ row.footerActions?.forEach((action, index) => {
70
+ const button = createElement(
71
+ body.ownerDocument,
72
+ 'button',
73
+ 'ok-native-list-action-button',
74
+ action.label
75
+ );
76
+ button.setAttribute('type', 'button');
77
+ button.toggleAttribute('disabled', Boolean(action.disabled));
78
+ button.dataset.tone = action.tone ?? '';
79
+ button.dataset.nativeListAction = action.key;
80
+ markActionAnchorSource(button, 'footerAction', index);
81
+ v.actions.appendChild(button);
82
+ });
83
+ }
84
+ function create(
85
+ document: Document,
86
+ row: ActivityRow,
87
+ primitives: RowPrimitives
88
+ ) {
89
+ const body = createElement(
90
+ document,
91
+ 'div',
92
+ 'ok-native-list-row ok-native-list-standard'
93
+ );
94
+ body.dataset.nlRenderer = 'activity';
95
+ const column = createElement(document, 'span', 'ok-native-list-flex');
96
+ const amounts = createElement(document, 'span', 'ok-native-list-amounts');
97
+ const actions = createElement(document, 'span', 'ok-native-list-actions');
98
+ const fields: Record<string, HTMLElement> = {};
99
+ for (const key of [
100
+ 'title',
101
+ 'description',
102
+ 'status',
103
+ 'primaryAmount',
104
+ 'secondaryAmount',
105
+ ]) {
106
+ const node = tagSlot(
107
+ createElement(
108
+ document,
109
+ 'span',
110
+ key === 'title'
111
+ ? 'ok-native-list-title'
112
+ : key === 'primaryAmount'
113
+ ? 'ok-native-list-value'
114
+ : 'ok-native-list-secondary'
115
+ ),
116
+ key
117
+ );
118
+ fields[key] = node;
119
+ (key.endsWith('Amount') ? amounts : column).appendChild(node);
120
+ }
121
+ column.appendChild(actions);
122
+ body.append(column, amounts);
123
+ views.set(body, {
124
+ column,
125
+ amounts,
126
+ actions,
127
+ fields,
128
+ visuals: [],
129
+ visualStyles: [],
130
+ visualKey: '',
131
+ primitives,
132
+ });
133
+ bind(body, row, primitives);
134
+ return body;
135
+ }
136
+ function recycle(body: HTMLElement) {
137
+ const v = views.get(body);
138
+ if (!v) return;
139
+ v.visuals.forEach((node) => {
140
+ v.primitives.dispose(node);
141
+ node.remove();
142
+ });
143
+ v.visuals = [];
144
+ v.visualStyles = [];
145
+ v.visualKey = '';
146
+ v.actions.replaceChildren();
147
+ }
148
+ export const activityRowRenderer = {
149
+ key: 'activity' as const,
150
+ create,
151
+ bind,
152
+ recycle,
153
+ measure: (row: ActivityRow, _width: number) =>
154
+ row.footerActions?.length ? 100 : 60,
155
+ measureRendered: (
156
+ _body: HTMLElement,
157
+ _row: ActivityRow
158
+ ): number | undefined => undefined,
159
+ };
@@ -0,0 +1,212 @@
1
+ import type { DataRow, TextTone } from '../../models';
2
+ import { createElement, tagSlot } from './RowElements';
3
+ import { RowVisualStyle, type RowPrimitives } from './RowVisual';
4
+ type ColumnViews = {
5
+ body: HTMLElement;
6
+ primaryLine: HTMLElement;
7
+ primary: HTMLElement;
8
+ badges: HTMLElement;
9
+ secondaryLine: HTMLElement;
10
+ secondary: HTMLElement[];
11
+ };
12
+ type Views = {
13
+ favorite: HTMLElement;
14
+ index: HTMLElement;
15
+ controls: HTMLElement;
16
+ columns: ColumnViews[];
17
+ visual?: HTMLElement;
18
+ visualStyle?: RowVisualStyle;
19
+ visualKey: string;
20
+ primitives: RowPrimitives;
21
+ };
22
+ const views = new WeakMap<HTMLElement, Views>();
23
+ const tone = (value: TextTone | undefined, fallback: string) =>
24
+ value === 'positive'
25
+ ? 'var(--nl-positive)'
26
+ : value === 'negative'
27
+ ? 'var(--nl-negative)'
28
+ : value === 'secondary'
29
+ ? 'var(--nl-secondary)'
30
+ : `var(--nl-${fallback})`;
31
+ function bind(body: HTMLElement, row: DataRow, primitives: RowPrimitives) {
32
+ const v = views.get(body)!;
33
+ v.primitives = primitives;
34
+ body.style.cssText = '';
35
+ body.removeAttribute('data-nl-container-background');
36
+ body.removeAttribute('data-nl-container-border');
37
+ const style = row.style;
38
+ if (style?.horizontalPadding !== undefined)
39
+ body.style.paddingInline = `${style.horizontalPadding}px`;
40
+ if (style?.verticalPadding !== undefined)
41
+ body.style.paddingBlock = `${style.verticalPadding}px`;
42
+ v.controls.replaceChildren();
43
+ if (row.checkbox)
44
+ v.controls.appendChild(primitives.accessory(row.key, row.checkbox, 0));
45
+ v.controls.style.display = 'contents';
46
+ v.index.style.cssText = '';
47
+ v.index.hidden = row.index === undefined;
48
+ v.index.textContent = String(row.index ?? '');
49
+ if (style?.index) primitives.textStyle(v.index, style.index);
50
+ v.favorite.hidden = !row.favorite;
51
+ v.favorite.textContent = row.favoriteActive ? '★' : '☆';
52
+ v.favorite.dataset.active = String(!!row.favoriteActive);
53
+ const visualKey = JSON.stringify([row.key, row.leading]);
54
+ if (visualKey !== v.visualKey) {
55
+ if (v.visual) {
56
+ primitives.dispose(v.visual);
57
+ v.visual.remove();
58
+ }
59
+ v.visual = primitives.visual(row.leading);
60
+ v.visualStyle = v.visual ? new RowVisualStyle(v.visual) : undefined;
61
+ if (v.visual) body.insertBefore(v.visual, v.columns[0]!.body);
62
+ v.visualKey = visualKey;
63
+ }
64
+ v.visualStyle?.apply(
65
+ style?.image,
66
+ style?.image?.width ?? 40,
67
+ style?.image?.height ?? 40,
68
+ (style?.leadingGap ?? 12) - 12
69
+ );
70
+ v.columns.forEach((cell, index) => {
71
+ const column = row.columns[index];
72
+ cell.body.hidden = !column;
73
+ if (!column) {
74
+ cell.body.remove();
75
+ return;
76
+ }
77
+ body.appendChild(cell.body);
78
+ cell.body.style.cssText = '';
79
+ cell.body.hidden = false;
80
+ cell.body.style.flex = String(column.weight ?? 1);
81
+ cell.body.style.display = 'flex';
82
+ cell.body.dataset.align = column.alignment ?? 'start';
83
+ if (style?.lineGap !== undefined)
84
+ cell.body.style.rowGap = `${style.lineGap}px`;
85
+ cell.primaryLine.style.cssText = '';
86
+ cell.primaryLine.style.color = tone(column.tone, 'primary');
87
+ if (style?.titleBadgeGap !== undefined)
88
+ cell.primaryLine.style.gap = `${style.titleBadgeGap}px`;
89
+ cell.primary.style.cssText = '';
90
+ cell.primary.textContent = column.text;
91
+ if (style?.columns) primitives.textStyle(cell.primary, style.columns);
92
+ cell.badges.replaceChildren();
93
+ cell.badges.style.display = 'contents';
94
+ if (column.key === 'asset')
95
+ row.badges?.forEach((badge) => {
96
+ const label = tagSlot(
97
+ createElement(
98
+ body.ownerDocument,
99
+ 'span',
100
+ 'ok-native-list-badge',
101
+ badge.text
102
+ ),
103
+ 'badge'
104
+ );
105
+ label.dataset.tone = badge.tone ?? '';
106
+ cell.badges.appendChild(label);
107
+ });
108
+ // Legacy Web layout: secondary leading text sits inline before the primary
109
+ // text; secondary text is a direct line below it, toned by secondaryTone.
110
+ cell.secondaryLine.style.display = column.secondaryText
111
+ ? 'contents'
112
+ : 'none';
113
+ [column.secondaryLeadingText, column.secondaryText].forEach(
114
+ (text, slot) => {
115
+ const label = cell.secondary[slot]!;
116
+ label.hidden = !text;
117
+ if (!text) label.remove();
118
+ else if (slot === 0) cell.primaryLine.insertBefore(label, cell.primary);
119
+ else cell.secondaryLine.appendChild(label);
120
+ label.style.cssText = '';
121
+ label.textContent = text ?? '';
122
+ if (slot === 1)
123
+ label.style.color = tone(column.secondaryTone, 'secondary');
124
+ if (style?.columnSecondary)
125
+ primitives.textStyle(label, style.columnSecondary);
126
+ }
127
+ );
128
+ });
129
+ }
130
+ function create(document: Document, row: DataRow, primitives: RowPrimitives) {
131
+ const body = createElement(
132
+ document,
133
+ 'div',
134
+ 'ok-native-list-row ok-native-list-data'
135
+ );
136
+ body.dataset.nlRenderer = 'dataRow';
137
+ const index = tagSlot(
138
+ createElement(document, 'span', 'ok-native-list-index'),
139
+ 'index'
140
+ );
141
+ const favorite = createElement(document, 'span', 'ok-native-list-favorite');
142
+ const controls = createElement(document, 'span');
143
+ body.append(controls, index, favorite);
144
+ const columns = Array.from({ length: 4 }, () => {
145
+ const cell = createElement(document, 'span', 'ok-native-list-data-cell');
146
+ const primaryLine = createElement(
147
+ document,
148
+ 'span',
149
+ 'ok-native-list-data-primary'
150
+ );
151
+ const primary = tagSlot(createElement(document, 'span'), 'columns');
152
+ const badges = createElement(document, 'span');
153
+ primaryLine.append(primary, badges);
154
+ const secondaryLine = createElement(
155
+ document,
156
+ 'span',
157
+ 'ok-native-list-data-secondary-line'
158
+ );
159
+ const secondary = Array.from({ length: 2 }, () =>
160
+ tagSlot(
161
+ createElement(document, 'span', 'ok-native-list-secondary'),
162
+ 'columnSecondary'
163
+ )
164
+ );
165
+ secondaryLine.append(...secondary);
166
+ cell.append(primaryLine, secondaryLine);
167
+ body.appendChild(cell);
168
+ return {
169
+ body: cell,
170
+ primaryLine,
171
+ primary,
172
+ badges,
173
+ secondaryLine,
174
+ secondary,
175
+ };
176
+ });
177
+ views.set(body, {
178
+ favorite,
179
+ index,
180
+ controls,
181
+ columns,
182
+ visualKey: '',
183
+ primitives,
184
+ });
185
+ bind(body, row, primitives);
186
+ return body;
187
+ }
188
+ function recycle(body: HTMLElement) {
189
+ const v = views.get(body);
190
+ if (!v) return;
191
+ if (v.visual) {
192
+ v.primitives.dispose(v.visual);
193
+ v.visual.remove();
194
+ }
195
+ v.visual = undefined;
196
+ v.visualStyle = undefined;
197
+ v.visualKey = '';
198
+ v.controls.replaceChildren();
199
+ }
200
+ export const dataRowRenderer = {
201
+ key: 'dataRow' as const,
202
+ create,
203
+ bind,
204
+ recycle,
205
+ // Legacy presets: table rows without secondary text are 8 units shorter.
206
+ measure: (row: DataRow, _width: number, layout?: string) => {
207
+ const secondary = row.columns.some((column) => column.secondaryText);
208
+ return (secondary ? 60 : 56) + (layout === 'table' && !secondary ? -8 : 0);
209
+ },
210
+ measureRendered: (_body: HTMLElement, _row: DataRow): number | undefined =>
211
+ undefined,
212
+ };