@onekeyfe/react-native-native-list 6.7.0-alpha.403 → 6.7.0-alpha.404
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 +3 -82
- package/android/src/main/java/com/onekey/nativelist/NativeListPrimitives.kt +3 -6
- package/docs/ROW_TEMPLATES.md +1 -8
- package/docs/SPEC.md +0 -84
- package/ios/NativeListIdentityRenderer.swift +6 -86
- package/lib/module/models.js +0 -2
- package/lib/module/validation.js +4 -70
- package/lib/module/web/NativeListWebEngine.js +1 -1
- package/lib/module/web/templates/IdentityRowRenderer.js +0 -91
- package/lib/typescript/src/models.d.ts +1 -21
- package/package.json +3 -3
- package/src/models.ts +0 -23
- package/src/validation.ts +1 -120
- package/src/web/NativeListWebEngine.ts +1 -1
- package/src/web/templates/IdentityRowRenderer.ts +0 -138
- package/android/src/main/java/com/onekey/nativelist/NativeListTitleSegmentLayout.kt +0 -38
- package/android/src/main/java/com/onekey/nativelist/NativeListTitleSegmentsView.kt +0 -105
- package/android/src/test/java/com/margelo/nitro/nativelist/NativeListTitleSegmentLayoutTest.kt +0 -65
- package/ios/NativeListTitleSegmentLayout.swift +0 -32
- package/ios/NativeListTitleSegmentsLabel.swift +0 -80
|
@@ -11,54 +11,6 @@ function toneColor(tone, fallback) {
|
|
|
11
11
|
if (tone === 'secondary') return 'var(--nl-secondary)';
|
|
12
12
|
return fallback === 'secondary' ? 'var(--nl-secondary)' : 'var(--nl-primary)';
|
|
13
13
|
}
|
|
14
|
-
// Hidden from sight, still read: the runs themselves are hidden from
|
|
15
|
-
// assistive technology in favor of `title`.
|
|
16
|
-
const VISUALLY_HIDDEN = {
|
|
17
|
-
position: 'absolute',
|
|
18
|
-
width: '1px',
|
|
19
|
-
height: '1px',
|
|
20
|
-
overflow: 'hidden',
|
|
21
|
-
clip: 'rect(0 0 0 0)',
|
|
22
|
-
whiteSpace: 'nowrap'
|
|
23
|
-
};
|
|
24
|
-
function appendTitleRuns(document, target, row) {
|
|
25
|
-
const runs = createElement(document, 'span', 'ok-native-list-identity-title-runs');
|
|
26
|
-
runs.setAttribute('aria-hidden', 'true');
|
|
27
|
-
Object.assign(runs.style, {
|
|
28
|
-
display: 'flex',
|
|
29
|
-
flex: '1 1 auto',
|
|
30
|
-
minWidth: '0',
|
|
31
|
-
maxWidth: '100%',
|
|
32
|
-
overflow: 'hidden'
|
|
33
|
-
});
|
|
34
|
-
row.titleSegments?.forEach(segment => {
|
|
35
|
-
const run = createElement(document, 'span', 'ok-native-list-identity-title-run', segment.text);
|
|
36
|
-
// Only the shrinking run gives up width; a cap is a share of the line.
|
|
37
|
-
Object.assign(run.style, {
|
|
38
|
-
flex: segment.shrink ? '0 1 auto' : '0 0 auto',
|
|
39
|
-
minWidth: segment.shrink ? '0' : '',
|
|
40
|
-
maxWidth: segment.maxWidthRatio === undefined ? '' : `${segment.maxWidthRatio * 100}%`,
|
|
41
|
-
overflow: 'hidden',
|
|
42
|
-
textOverflow: 'ellipsis',
|
|
43
|
-
whiteSpace: 'pre'
|
|
44
|
-
});
|
|
45
|
-
if (segment.tone) run.style.color = segment.tone === 'secondary' ? 'var(--nl-secondary)' : 'var(--nl-primary)';
|
|
46
|
-
if (segment.fontWeight === 'medium') {
|
|
47
|
-
run.style.fontWeight = '500';
|
|
48
|
-
run.style.fontFamily = 'var(--nl-font-medium)';
|
|
49
|
-
} else if (segment.fontWeight === 'regular') {
|
|
50
|
-
run.style.fontWeight = '400';
|
|
51
|
-
run.style.fontFamily = 'var(--nl-font-regular)';
|
|
52
|
-
}
|
|
53
|
-
setData(run, 'tone', segment.tone);
|
|
54
|
-
runs.appendChild(run);
|
|
55
|
-
});
|
|
56
|
-
const spoken = createElement(document, 'span', 'ok-native-list-identity-title-spoken', row.title);
|
|
57
|
-
Object.assign(spoken.style, VISUALLY_HIDDEN);
|
|
58
|
-
target.replaceChildren(runs, spoken);
|
|
59
|
-
target.style.display = 'flex';
|
|
60
|
-
target.style.minWidth = '0';
|
|
61
|
-
}
|
|
62
14
|
function appendAccessories(parent, document, primitives, row, accessories) {
|
|
63
15
|
if (!accessories?.length) return;
|
|
64
16
|
const container = createElement(document, 'span', 'ok-native-list-accessories');
|
|
@@ -134,13 +86,6 @@ function createIdentityRow(document, primitives, row) {
|
|
|
134
86
|
fragment.appendChild(document.createTextNode(row.title.slice(offset)));
|
|
135
87
|
textTarget.prepend(fragment);
|
|
136
88
|
}
|
|
137
|
-
// The runs replace the plain title, except where `title` itself is
|
|
138
|
-
// highlighted or shown in the wallet sidebar.
|
|
139
|
-
const hasTitleRuns = !!row.titleSegments?.length && presentation !== 'walletSidebar' && !row.titleMatch?.length;
|
|
140
|
-
if (hasTitleRuns) {
|
|
141
|
-
const textTarget = titleElement.querySelector('[data-nl-slot="title"]') ?? titleElement;
|
|
142
|
-
appendTitleRuns(document, textTarget, row);
|
|
143
|
-
}
|
|
144
89
|
if (row.subtitleSegments?.length) {
|
|
145
90
|
column.querySelector('.ok-native-list-secondary')?.remove();
|
|
146
91
|
const segments = createElement(document, 'span', 'ok-native-list-subtitle-segments');
|
|
@@ -258,48 +203,12 @@ function createIdentityRow(document, primitives, row) {
|
|
|
258
203
|
badges.style.minWidth = '0';
|
|
259
204
|
}
|
|
260
205
|
}
|
|
261
|
-
if (hasTitleRuns) {
|
|
262
|
-
// The runs fill what the title line leaves, so a ratio is a share of
|
|
263
|
-
// that width and title accessories sit at the line's end.
|
|
264
|
-
const runs = titleElement.querySelector('.ok-native-list-identity-title-runs');
|
|
265
|
-
for (let node = runs?.parentElement; node && node !== titleElement; node = node.parentElement) {
|
|
266
|
-
node.style.display = 'flex';
|
|
267
|
-
node.style.flex = '1 1 auto';
|
|
268
|
-
node.style.minWidth = '0';
|
|
269
|
-
}
|
|
270
|
-
titleElement.style.display = 'flex';
|
|
271
|
-
titleElement.style.alignItems = 'center';
|
|
272
|
-
}
|
|
273
|
-
if (row.titleLeadingIcon && presentation !== 'walletSidebar') {
|
|
274
|
-
const descriptor = row.titleLeadingIcon;
|
|
275
|
-
if (titleElement.hasAttribute('data-nl-slot')) {
|
|
276
|
-
// Keep the glyph out of the title slot that text styles apply to.
|
|
277
|
-
const titleText = tagSlot(createElement(document, 'span', 'ok-native-list-identity-title-text'), 'title');
|
|
278
|
-
while (titleElement.firstChild) titleText.appendChild(titleElement.firstChild);
|
|
279
|
-
if (hasTitleRuns) {
|
|
280
|
-
titleText.style.display = 'flex';
|
|
281
|
-
titleText.style.minWidth = '0';
|
|
282
|
-
}
|
|
283
|
-
titleElement.removeAttribute('data-nl-slot');
|
|
284
|
-
titleElement.appendChild(titleText);
|
|
285
|
-
}
|
|
286
|
-
const icon = primitives.iconAction(descriptor.name, undefined, undefined, descriptor.tintColor);
|
|
287
|
-
icon.className = 'ok-native-list-identity-title-leading-icon';
|
|
288
|
-
if (descriptor.accessibilityLabel) icon.setAttribute('aria-label', descriptor.accessibilityLabel);else icon.setAttribute('aria-hidden', 'true');
|
|
289
|
-
titleElement.style.display = 'flex';
|
|
290
|
-
titleElement.style.alignItems = 'center';
|
|
291
|
-
// 6px to the title, whatever gap the title line already has.
|
|
292
|
-
const lineGap = Number.parseFloat(titleElement.style.gap || '0') || 0;
|
|
293
|
-
icon.style.marginInlineEnd = `${Math.max(0, 6 - lineGap)}px`;
|
|
294
|
-
titleElement.prepend(icon);
|
|
295
|
-
}
|
|
296
206
|
if (presentation === 'walletSidebar' && row.badges?.length) {
|
|
297
207
|
const badges = createElement(document, 'span', 'ok-native-list-wallet-badges');
|
|
298
208
|
row.badges.forEach(badge => badges.appendChild(createBadge(document, badge)));
|
|
299
209
|
column.appendChild(badges);
|
|
300
210
|
}
|
|
301
211
|
for (const key of ['title', 'subtitle']) {
|
|
302
|
-
if (key === 'title' && hasTitleRuns) continue;
|
|
303
212
|
const lines = row.style?.[key]?.lines ?? row[`${key}Lines`];
|
|
304
213
|
if (lines !== undefined) column.querySelectorAll(`[data-nl-slot="${key}"]`).forEach(text => applyTextLayout(text, lines === 0 ? {} : {
|
|
305
214
|
lines
|
|
@@ -394,18 +394,6 @@ export type RowBase = Readonly<{
|
|
|
394
394
|
accessibilityLabel?: string;
|
|
395
395
|
separator?: boolean;
|
|
396
396
|
}>;
|
|
397
|
-
/** One run of an identity title line; see docs/SPEC.md. */
|
|
398
|
-
export type IdentityTitleSegment = Readonly<{
|
|
399
|
-
text: string;
|
|
400
|
-
/** Omitted: the title color. */
|
|
401
|
-
tone?: 'primary' | 'secondary';
|
|
402
|
-
/** Omitted: the title weight. */
|
|
403
|
-
fontWeight?: 'regular' | 'medium';
|
|
404
|
-
/** The one run that gives up width when the line is too narrow. */
|
|
405
|
-
shrink?: boolean;
|
|
406
|
-
/** Caps the run at this fraction of the title line, within (0, 1]. */
|
|
407
|
-
maxWidthRatio?: number;
|
|
408
|
-
}>;
|
|
409
397
|
export type IdentityRow = RowBase & Readonly<{
|
|
410
398
|
type: 'identity';
|
|
411
399
|
/** Opt in to content-based height; explicit row/container height still wins. */
|
|
@@ -440,14 +428,6 @@ export type IdentityRow = RowBase & Readonly<{
|
|
|
440
428
|
}>;
|
|
441
429
|
/** Web-only additional help icon after titleIcon. */
|
|
442
430
|
secondaryTitleIcon?: IdentityRow['titleIcon'];
|
|
443
|
-
/** Runs drawn in place of `title`, which stays the spoken label. */
|
|
444
|
-
titleSegments?: readonly IdentityTitleSegment[];
|
|
445
|
-
/** 16-point glyph before the title; decorative unless labelled. */
|
|
446
|
-
titleLeadingIcon?: Readonly<{
|
|
447
|
-
name: string;
|
|
448
|
-
tintColor?: string;
|
|
449
|
-
accessibilityLabel?: string;
|
|
450
|
-
}>;
|
|
451
431
|
/** Circular image after title icons; image dimensions must be square, 1...40. */
|
|
452
432
|
titleImage?: Readonly<{
|
|
453
433
|
image: ImageSource;
|
|
@@ -898,7 +878,7 @@ export type PatchClearableChanges<Row, Keys extends keyof Row> = Readonly<{
|
|
|
898
878
|
export type RowPatch = Readonly<{
|
|
899
879
|
type: 'identity';
|
|
900
880
|
key: string;
|
|
901
|
-
changes: PatchClearableChanges<IdentityRow, CommonPatchFields | 'title' | 'titleSuffixes' | 'titleIcon' | 'secondaryTitleIcon' | 'titleImage' | '
|
|
881
|
+
changes: PatchClearableChanges<IdentityRow, CommonPatchFields | 'title' | 'titleSuffixes' | 'titleIcon' | 'secondaryTitleIcon' | 'titleImage' | 'subtitle' | 'subtitleLines' | 'contentHeight' | 'longPressActionKey' | 'tertiary' | 'tertiaryTone' | 'badges' | 'trailing' | 'leading' | 'leadingAction' | 'titleMatch' | 'titleActionKey' | 'titleActionOnHover' | 'subtitleSegments'>;
|
|
902
882
|
}> | Readonly<{
|
|
903
883
|
type: 'rail';
|
|
904
884
|
key: string;
|
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.404",
|
|
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.404",
|
|
90
|
+
"@onekeyfe/react-native-native-logger": "6.7.0-alpha.404",
|
|
91
91
|
"react": "*",
|
|
92
92
|
"react-native": "*",
|
|
93
93
|
"react-native-nitro-modules": "0.37.0"
|
package/src/models.ts
CHANGED
|
@@ -513,19 +513,6 @@ export type RowBase = Readonly<{
|
|
|
513
513
|
separator?: boolean;
|
|
514
514
|
}>;
|
|
515
515
|
|
|
516
|
-
/** One run of an identity title line; see docs/SPEC.md. */
|
|
517
|
-
export type IdentityTitleSegment = Readonly<{
|
|
518
|
-
text: string;
|
|
519
|
-
/** Omitted: the title color. */
|
|
520
|
-
tone?: 'primary' | 'secondary';
|
|
521
|
-
/** Omitted: the title weight. */
|
|
522
|
-
fontWeight?: 'regular' | 'medium';
|
|
523
|
-
/** The one run that gives up width when the line is too narrow. */
|
|
524
|
-
shrink?: boolean;
|
|
525
|
-
/** Caps the run at this fraction of the title line, within (0, 1]. */
|
|
526
|
-
maxWidthRatio?: number;
|
|
527
|
-
}>;
|
|
528
|
-
|
|
529
516
|
export type IdentityRow = RowBase &
|
|
530
517
|
Readonly<{
|
|
531
518
|
type: 'identity';
|
|
@@ -557,14 +544,6 @@ export type IdentityRow = RowBase &
|
|
|
557
544
|
}>;
|
|
558
545
|
/** Web-only additional help icon after titleIcon. */
|
|
559
546
|
secondaryTitleIcon?: IdentityRow['titleIcon'];
|
|
560
|
-
/** Runs drawn in place of `title`, which stays the spoken label. */
|
|
561
|
-
titleSegments?: readonly IdentityTitleSegment[];
|
|
562
|
-
/** 16-point glyph before the title; decorative unless labelled. */
|
|
563
|
-
titleLeadingIcon?: Readonly<{
|
|
564
|
-
name: string;
|
|
565
|
-
tintColor?: string;
|
|
566
|
-
accessibilityLabel?: string;
|
|
567
|
-
}>;
|
|
568
547
|
/** Circular image after title icons; image dimensions must be square, 1...40. */
|
|
569
548
|
titleImage?: Readonly<{
|
|
570
549
|
image: ImageSource;
|
|
@@ -1101,8 +1080,6 @@ export type RowPatch =
|
|
|
1101
1080
|
| 'titleIcon'
|
|
1102
1081
|
| 'secondaryTitleIcon'
|
|
1103
1082
|
| 'titleImage'
|
|
1104
|
-
| 'titleSegments'
|
|
1105
|
-
| 'titleLeadingIcon'
|
|
1106
1083
|
| 'subtitle'
|
|
1107
1084
|
| 'subtitleLines'
|
|
1108
1085
|
| 'contentHeight'
|
package/src/validation.ts
CHANGED
|
@@ -1252,108 +1252,6 @@ function assertButtonGroup(
|
|
|
1252
1252
|
});
|
|
1253
1253
|
}
|
|
1254
1254
|
|
|
1255
|
-
const MAX_TITLE_SEGMENTS = 4;
|
|
1256
|
-
const HEX_COLOR_PATTERN = /^#[0-9a-f]{6}([0-9a-f]{2})?$/i;
|
|
1257
|
-
|
|
1258
|
-
function assertTitleSegments(value: unknown, path: string): void {
|
|
1259
|
-
if (
|
|
1260
|
-
!Array.isArray(value) ||
|
|
1261
|
-
value.length === 0 ||
|
|
1262
|
-
value.length > MAX_TITLE_SEGMENTS
|
|
1263
|
-
)
|
|
1264
|
-
fail(path, `must hold 1...${MAX_TITLE_SEGMENTS} segments`);
|
|
1265
|
-
let shrinking = 0;
|
|
1266
|
-
value.forEach((item: unknown, index) => {
|
|
1267
|
-
const segmentPath = `${path}[${index}]`;
|
|
1268
|
-
const segment = protocolObject(item, segmentPath);
|
|
1269
|
-
assertUnknownKeys(
|
|
1270
|
-
segment,
|
|
1271
|
-
['text', 'tone', 'fontWeight', 'shrink', 'maxWidthRatio'],
|
|
1272
|
-
segmentPath,
|
|
1273
|
-
'title segment key'
|
|
1274
|
-
);
|
|
1275
|
-
const { text, tone, fontWeight, shrink, maxWidthRatio } = segment;
|
|
1276
|
-
if (typeof text !== 'string' || !text.trim())
|
|
1277
|
-
fail(`${segmentPath}.text`, 'must be non-blank');
|
|
1278
|
-
assertText(text, `${segmentPath}.text`);
|
|
1279
|
-
if (tone !== undefined && tone !== 'primary' && tone !== 'secondary')
|
|
1280
|
-
fail(`${segmentPath}.tone`, 'must be primary or secondary');
|
|
1281
|
-
if (
|
|
1282
|
-
fontWeight !== undefined &&
|
|
1283
|
-
fontWeight !== 'regular' &&
|
|
1284
|
-
fontWeight !== 'medium'
|
|
1285
|
-
)
|
|
1286
|
-
fail(`${segmentPath}.fontWeight`, 'must be regular or medium');
|
|
1287
|
-
if (shrink !== undefined && typeof shrink !== 'boolean')
|
|
1288
|
-
fail(`${segmentPath}.shrink`, 'must be a boolean');
|
|
1289
|
-
if (shrink) shrinking += 1;
|
|
1290
|
-
if (
|
|
1291
|
-
maxWidthRatio !== undefined &&
|
|
1292
|
-
(typeof maxWidthRatio !== 'number' ||
|
|
1293
|
-
!Number.isFinite(maxWidthRatio) ||
|
|
1294
|
-
maxWidthRatio <= 0 ||
|
|
1295
|
-
maxWidthRatio > 1)
|
|
1296
|
-
)
|
|
1297
|
-
fail(`${segmentPath}.maxWidthRatio`, 'must be finite within (0, 1]');
|
|
1298
|
-
});
|
|
1299
|
-
if (shrinking > 1) fail(path, 'allows at most one shrinking segment');
|
|
1300
|
-
}
|
|
1301
|
-
|
|
1302
|
-
function assertTitleLeadingIcon(value: unknown, path: string): void {
|
|
1303
|
-
const icon = protocolObject(value, path);
|
|
1304
|
-
assertUnknownKeys(
|
|
1305
|
-
icon,
|
|
1306
|
-
['name', 'tintColor', 'accessibilityLabel'],
|
|
1307
|
-
path,
|
|
1308
|
-
'title leading icon key'
|
|
1309
|
-
);
|
|
1310
|
-
const { name, tintColor, accessibilityLabel } = icon;
|
|
1311
|
-
if (typeof name !== 'string' || !name.trim())
|
|
1312
|
-
fail(`${path}.name`, 'must be non-blank');
|
|
1313
|
-
if (
|
|
1314
|
-
tintColor !== undefined &&
|
|
1315
|
-
(typeof tintColor !== 'string' || !HEX_COLOR_PATTERN.test(tintColor))
|
|
1316
|
-
)
|
|
1317
|
-
fail(`${path}.tintColor`, 'must be #RRGGBB or #RRGGBBAA');
|
|
1318
|
-
if (
|
|
1319
|
-
accessibilityLabel !== undefined &&
|
|
1320
|
-
typeof accessibilityLabel !== 'string'
|
|
1321
|
-
)
|
|
1322
|
-
fail(`${path}.accessibilityLabel`, 'must be a string');
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
function assertIdentityTitleRuns(
|
|
1326
|
-
row: Pick<
|
|
1327
|
-
IdentityRow,
|
|
1328
|
-
'titleSegments' | 'titleLeadingIcon' | 'titleMatch' | 'presentation'
|
|
1329
|
-
>,
|
|
1330
|
-
path: string
|
|
1331
|
-
): void {
|
|
1332
|
-
if (row.titleSegments !== undefined) {
|
|
1333
|
-
assertTitleSegments(row.titleSegments, `${path}.titleSegments`);
|
|
1334
|
-
// titleMatch ranges index `title`, which the runs replace on screen.
|
|
1335
|
-
if (row.titleMatch?.length)
|
|
1336
|
-
fail(`${path}.titleMatch`, 'cannot be combined with titleSegments');
|
|
1337
|
-
if (row.presentation === 'walletSidebar')
|
|
1338
|
-
fail(`${path}.titleSegments`, 'is not supported with walletSidebar');
|
|
1339
|
-
}
|
|
1340
|
-
if (row.titleLeadingIcon !== undefined) {
|
|
1341
|
-
assertTitleLeadingIcon(row.titleLeadingIcon, `${path}.titleLeadingIcon`);
|
|
1342
|
-
if (row.presentation === 'walletSidebar')
|
|
1343
|
-
fail(`${path}.titleLeadingIcon`, 'is not supported with walletSidebar');
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
/** Production keeps decorative title runs only when they pass the rules. */
|
|
1348
|
-
function passesTitleRunRules(check: () => void): boolean {
|
|
1349
|
-
try {
|
|
1350
|
-
check();
|
|
1351
|
-
return true;
|
|
1352
|
-
} catch {
|
|
1353
|
-
return false;
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
|
|
1357
1255
|
function assertIdentityExtras(
|
|
1358
1256
|
row: Pick<
|
|
1359
1257
|
IdentityRow,
|
|
@@ -1844,7 +1742,6 @@ function assertRow(
|
|
|
1844
1742
|
);
|
|
1845
1743
|
});
|
|
1846
1744
|
assertIdentityExtras(row, path);
|
|
1847
|
-
assertIdentityTitleRuns(row, path);
|
|
1848
1745
|
assertText(row.subtitle, `${path}.subtitle`);
|
|
1849
1746
|
// OneKey patch: selector text segments truncate independently.
|
|
1850
1747
|
row.subtitleSegments?.forEach((segment, segmentIndex) => {
|
|
@@ -2427,8 +2324,6 @@ const ROW_FIELDS: Readonly<Record<string, readonly string[]>> = {
|
|
|
2427
2324
|
'titleIcon',
|
|
2428
2325
|
'secondaryTitleIcon',
|
|
2429
2326
|
'titleImage',
|
|
2430
|
-
'titleSegments',
|
|
2431
|
-
'titleLeadingIcon',
|
|
2432
2327
|
'subtitle',
|
|
2433
2328
|
'tertiary',
|
|
2434
2329
|
'tertiaryTone',
|
|
@@ -2605,8 +2500,6 @@ const PATCH_FIELDS: Readonly<Record<string, readonly string[]>> = {
|
|
|
2605
2500
|
'titleIcon',
|
|
2606
2501
|
'secondaryTitleIcon',
|
|
2607
2502
|
'titleImage',
|
|
2608
|
-
'titleSegments',
|
|
2609
|
-
'titleLeadingIcon',
|
|
2610
2503
|
'subtitle',
|
|
2611
2504
|
'tertiary',
|
|
2612
2505
|
'tertiaryTone',
|
|
@@ -2737,7 +2630,6 @@ function protocolKey(value: unknown, path: string): void {
|
|
|
2737
2630
|
const PROTOCOL_ARRAY_FIELDS = new Set([
|
|
2738
2631
|
'columns',
|
|
2739
2632
|
'titleSuffixes',
|
|
2740
|
-
'titleSegments',
|
|
2741
2633
|
'cells',
|
|
2742
2634
|
'points',
|
|
2743
2635
|
'images',
|
|
@@ -2979,10 +2871,7 @@ function assertRowProtocol(row: RowModel, path: string): void {
|
|
|
2979
2871
|
if (row.height !== undefined && row.height < 0)
|
|
2980
2872
|
fail(`${path}.height`, 'must be non-negative');
|
|
2981
2873
|
if (row.type === 'identity') {
|
|
2982
|
-
if (isDevelopment())
|
|
2983
|
-
assertIdentityExtras(row, path);
|
|
2984
|
-
assertIdentityTitleRuns(row, path);
|
|
2985
|
-
}
|
|
2874
|
+
if (isDevelopment()) assertIdentityExtras(row, path);
|
|
2986
2875
|
if (row.titleSuffixes !== undefined && !Array.isArray(row.titleSuffixes))
|
|
2987
2876
|
fail(`${path}.titleSuffixes`, 'must be an array');
|
|
2988
2877
|
row.titleSuffixes?.forEach((suffix, index) => {
|
|
@@ -3574,14 +3463,6 @@ const NESTED_DECORATION_FIELDS: Readonly<Record<string, readonly string[]>> = {
|
|
|
3574
3463
|
|
|
3575
3464
|
function sanitizeDecoration(value: unknown, field: string): unknown {
|
|
3576
3465
|
if (value === undefined || value === null) return undefined;
|
|
3577
|
-
if (field === 'titleSegments')
|
|
3578
|
-
return passesTitleRunRules(() => assertTitleSegments(value, field))
|
|
3579
|
-
? value
|
|
3580
|
-
: undefined;
|
|
3581
|
-
if (field === 'titleLeadingIcon')
|
|
3582
|
-
return passesTitleRunRules(() => assertTitleLeadingIcon(value, field))
|
|
3583
|
-
? value
|
|
3584
|
-
: undefined;
|
|
3585
3466
|
if (
|
|
3586
3467
|
!Array.isArray(value) &&
|
|
3587
3468
|
[
|
|
@@ -845,7 +845,7 @@ export const WEB_LIST_CSS = `
|
|
|
845
845
|
.ok-native-list-trailing-button{appearance:none;box-sizing:border-box;flex:0 0 auto;padding:4px 10px;border:1px solid transparent;border-radius:9999px;background:var(--nl-button-bg,var(--nl-strong));color:var(--nl-button-text,var(--nl-primary));font:500 14px/20px var(--nl-font-medium);white-space:nowrap;cursor:pointer}.ok-native-list-trailing-button:hover:not(:disabled){background:var(--nl-button-hover,var(--nl-strong))}.ok-native-list-trailing-button:active:not(:disabled){background:var(--nl-button-pressed,var(--nl-pressed))}.ok-native-list-trailing-button:focus-visible{outline:2px solid var(--nl-button-focus,var(--nl-accent))}.ok-native-list-trailing-button:disabled{opacity:.4;cursor:default}
|
|
846
846
|
.ok-native-list-icon-button.ok-native-list-tertiary-icon{box-sizing:border-box;flex:0 0 38px;width:38px;height:38px;margin:-7px;padding:6px;border:1px solid transparent;border-radius:9999px;color:var(--nl-icon-subdued);background:transparent}.ok-native-list-icon-button.ok-native-list-tertiary-icon:hover:not(:disabled){background:var(--nl-hover)}.ok-native-list-icon-button.ok-native-list-tertiary-icon:active:not(:disabled){background:var(--nl-pressed)}.ok-native-list-icon-button.ok-native-list-tertiary-icon:disabled{opacity:.4;cursor:default}
|
|
847
847
|
.ok-native-list-icon-button.ok-native-list-tertiary-icon:focus-visible:not(:disabled){outline:2px solid var(--nl-accent)}
|
|
848
|
-
.ok-native-list-identity-title-icon{flex:0 0 20px;width:20px;height:20px;display:inline-flex;align-items:center;justify-content:center;color:var(--nl-icon);font-size:20px}.ok-native-list-identity-title-
|
|
848
|
+
.ok-native-list-identity-title-icon{flex:0 0 20px;width:20px;height:20px;display:inline-flex;align-items:center;justify-content:center;color:var(--nl-icon);font-size:20px}.ok-native-list-identity-title-text{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
849
849
|
.ok-native-list-account-row .ok-native-list-visual,.ok-native-list-account-action-row .ok-native-list-visual{width:32px;height:32px;flex-basis:32px}.ok-native-list-account-row .ok-native-list-visual>img,.ok-native-list-account-row .ok-native-list-visual-main,.ok-native-list-account-action-row .ok-native-list-visual>img,.ok-native-list-account-action-row .ok-native-list-visual-main{width:32px;height:32px}.ok-native-list-account-row .ok-native-list-visual>.ok-native-list-visual-corner{width:20px;height:20px;padding:2px}.ok-native-list-account-action-row .ok-native-list-visual{border-radius:8px!important}.ok-native-list-wallet-row .ok-native-list-title{color:var(--nl-secondary);font-size:12px;line-height:16px;font-weight:400;font-family:var(--nl-font-regular)}.ok-native-list-item[data-native-list-selected="true"]>.ok-native-list-wallet-row .ok-native-list-title{color:var(--nl-primary)}.ok-native-list-account-row .ok-native-list-title{font-size:16px;line-height:20px;font-weight:400;font-family:var(--nl-font-regular)}.ok-native-list-account-row .ok-native-list-secondary{font-size:14px;line-height:20px;font-weight:400;font-family:var(--nl-font-regular)}
|
|
850
850
|
.ok-native-list-flex{display:flex;flex:1;min-width:0;flex-direction:column;justify-content:center}.ok-native-list-title{font-size:15px;line-height:20px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-family:var(--nl-font-semibold)}.ok-native-list-secondary{font-size:13px;line-height:18px;color:var(--nl-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ok-native-list-tertiary{color:var(--nl-secondary)}.ok-native-list-info{color:var(--nl-info)}
|
|
851
851
|
.ok-native-list-identity-row .ok-native-list-title,.ok-native-list-identity-row .ok-native-list-amounts>span:first-child{font-size:16px;line-height:20px}.ok-native-list-identity-row .ok-native-list-secondary{font-size:14px;line-height:20px}
|
|
@@ -30,79 +30,6 @@ function toneColor(
|
|
|
30
30
|
if (tone === 'secondary') return 'var(--nl-secondary)';
|
|
31
31
|
return fallback === 'secondary' ? 'var(--nl-secondary)' : 'var(--nl-primary)';
|
|
32
32
|
}
|
|
33
|
-
// Hidden from sight, still read: the runs themselves are hidden from
|
|
34
|
-
// assistive technology in favor of `title`.
|
|
35
|
-
const VISUALLY_HIDDEN: Partial<CSSStyleDeclaration> = {
|
|
36
|
-
position: 'absolute',
|
|
37
|
-
width: '1px',
|
|
38
|
-
height: '1px',
|
|
39
|
-
overflow: 'hidden',
|
|
40
|
-
clip: 'rect(0 0 0 0)',
|
|
41
|
-
whiteSpace: 'nowrap',
|
|
42
|
-
};
|
|
43
|
-
function appendTitleRuns(
|
|
44
|
-
document: Document,
|
|
45
|
-
target: HTMLElement,
|
|
46
|
-
row: IdentityRow
|
|
47
|
-
): void {
|
|
48
|
-
const runs = createElement(
|
|
49
|
-
document,
|
|
50
|
-
'span',
|
|
51
|
-
'ok-native-list-identity-title-runs'
|
|
52
|
-
);
|
|
53
|
-
runs.setAttribute('aria-hidden', 'true');
|
|
54
|
-
Object.assign(runs.style, {
|
|
55
|
-
display: 'flex',
|
|
56
|
-
flex: '1 1 auto',
|
|
57
|
-
minWidth: '0',
|
|
58
|
-
maxWidth: '100%',
|
|
59
|
-
overflow: 'hidden',
|
|
60
|
-
});
|
|
61
|
-
row.titleSegments?.forEach((segment) => {
|
|
62
|
-
const run = createElement(
|
|
63
|
-
document,
|
|
64
|
-
'span',
|
|
65
|
-
'ok-native-list-identity-title-run',
|
|
66
|
-
segment.text
|
|
67
|
-
);
|
|
68
|
-
// Only the shrinking run gives up width; a cap is a share of the line.
|
|
69
|
-
Object.assign(run.style, {
|
|
70
|
-
flex: segment.shrink ? '0 1 auto' : '0 0 auto',
|
|
71
|
-
minWidth: segment.shrink ? '0' : '',
|
|
72
|
-
maxWidth:
|
|
73
|
-
segment.maxWidthRatio === undefined
|
|
74
|
-
? ''
|
|
75
|
-
: `${segment.maxWidthRatio * 100}%`,
|
|
76
|
-
overflow: 'hidden',
|
|
77
|
-
textOverflow: 'ellipsis',
|
|
78
|
-
whiteSpace: 'pre',
|
|
79
|
-
});
|
|
80
|
-
if (segment.tone)
|
|
81
|
-
run.style.color =
|
|
82
|
-
segment.tone === 'secondary'
|
|
83
|
-
? 'var(--nl-secondary)'
|
|
84
|
-
: 'var(--nl-primary)';
|
|
85
|
-
if (segment.fontWeight === 'medium') {
|
|
86
|
-
run.style.fontWeight = '500';
|
|
87
|
-
run.style.fontFamily = 'var(--nl-font-medium)';
|
|
88
|
-
} else if (segment.fontWeight === 'regular') {
|
|
89
|
-
run.style.fontWeight = '400';
|
|
90
|
-
run.style.fontFamily = 'var(--nl-font-regular)';
|
|
91
|
-
}
|
|
92
|
-
setData(run, 'tone', segment.tone);
|
|
93
|
-
runs.appendChild(run);
|
|
94
|
-
});
|
|
95
|
-
const spoken = createElement(
|
|
96
|
-
document,
|
|
97
|
-
'span',
|
|
98
|
-
'ok-native-list-identity-title-spoken',
|
|
99
|
-
row.title
|
|
100
|
-
);
|
|
101
|
-
Object.assign(spoken.style, VISUALLY_HIDDEN);
|
|
102
|
-
target.replaceChildren(runs, spoken);
|
|
103
|
-
target.style.display = 'flex';
|
|
104
|
-
target.style.minWidth = '0';
|
|
105
|
-
}
|
|
106
33
|
function appendAccessories(
|
|
107
34
|
parent: HTMLElement,
|
|
108
35
|
document: Document,
|
|
@@ -268,18 +195,6 @@ function createIdentityRow(
|
|
|
268
195
|
fragment.appendChild(document.createTextNode(row.title.slice(offset)));
|
|
269
196
|
textTarget.prepend(fragment);
|
|
270
197
|
}
|
|
271
|
-
// The runs replace the plain title, except where `title` itself is
|
|
272
|
-
// highlighted or shown in the wallet sidebar.
|
|
273
|
-
const hasTitleRuns =
|
|
274
|
-
!!row.titleSegments?.length &&
|
|
275
|
-
presentation !== 'walletSidebar' &&
|
|
276
|
-
!row.titleMatch?.length;
|
|
277
|
-
if (hasTitleRuns) {
|
|
278
|
-
const textTarget =
|
|
279
|
-
titleElement.querySelector<HTMLElement>('[data-nl-slot="title"]') ??
|
|
280
|
-
titleElement;
|
|
281
|
-
appendTitleRuns(document, textTarget, row);
|
|
282
|
-
}
|
|
283
198
|
if (row.subtitleSegments?.length) {
|
|
284
199
|
column.querySelector('.ok-native-list-secondary')?.remove();
|
|
285
200
|
const segments = createElement(
|
|
@@ -468,58 +383,6 @@ function createIdentityRow(
|
|
|
468
383
|
badges.style.minWidth = '0';
|
|
469
384
|
}
|
|
470
385
|
}
|
|
471
|
-
if (hasTitleRuns) {
|
|
472
|
-
// The runs fill what the title line leaves, so a ratio is a share of
|
|
473
|
-
// that width and title accessories sit at the line's end.
|
|
474
|
-
const runs = titleElement.querySelector<HTMLElement>(
|
|
475
|
-
'.ok-native-list-identity-title-runs'
|
|
476
|
-
);
|
|
477
|
-
for (
|
|
478
|
-
let node = runs?.parentElement;
|
|
479
|
-
node && node !== titleElement;
|
|
480
|
-
node = node.parentElement
|
|
481
|
-
) {
|
|
482
|
-
node.style.display = 'flex';
|
|
483
|
-
node.style.flex = '1 1 auto';
|
|
484
|
-
node.style.minWidth = '0';
|
|
485
|
-
}
|
|
486
|
-
titleElement.style.display = 'flex';
|
|
487
|
-
titleElement.style.alignItems = 'center';
|
|
488
|
-
}
|
|
489
|
-
if (row.titleLeadingIcon && presentation !== 'walletSidebar') {
|
|
490
|
-
const descriptor = row.titleLeadingIcon;
|
|
491
|
-
if (titleElement.hasAttribute('data-nl-slot')) {
|
|
492
|
-
// Keep the glyph out of the title slot that text styles apply to.
|
|
493
|
-
const titleText = tagSlot(
|
|
494
|
-
createElement(document, 'span', 'ok-native-list-identity-title-text'),
|
|
495
|
-
'title'
|
|
496
|
-
);
|
|
497
|
-
while (titleElement.firstChild)
|
|
498
|
-
titleText.appendChild(titleElement.firstChild);
|
|
499
|
-
if (hasTitleRuns) {
|
|
500
|
-
titleText.style.display = 'flex';
|
|
501
|
-
titleText.style.minWidth = '0';
|
|
502
|
-
}
|
|
503
|
-
titleElement.removeAttribute('data-nl-slot');
|
|
504
|
-
titleElement.appendChild(titleText);
|
|
505
|
-
}
|
|
506
|
-
const icon = primitives.iconAction(
|
|
507
|
-
descriptor.name,
|
|
508
|
-
undefined,
|
|
509
|
-
undefined,
|
|
510
|
-
descriptor.tintColor
|
|
511
|
-
);
|
|
512
|
-
icon.className = 'ok-native-list-identity-title-leading-icon';
|
|
513
|
-
if (descriptor.accessibilityLabel)
|
|
514
|
-
icon.setAttribute('aria-label', descriptor.accessibilityLabel);
|
|
515
|
-
else icon.setAttribute('aria-hidden', 'true');
|
|
516
|
-
titleElement.style.display = 'flex';
|
|
517
|
-
titleElement.style.alignItems = 'center';
|
|
518
|
-
// 6px to the title, whatever gap the title line already has.
|
|
519
|
-
const lineGap = Number.parseFloat(titleElement.style.gap || '0') || 0;
|
|
520
|
-
icon.style.marginInlineEnd = `${Math.max(0, 6 - lineGap)}px`;
|
|
521
|
-
titleElement.prepend(icon);
|
|
522
|
-
}
|
|
523
386
|
if (presentation === 'walletSidebar' && row.badges?.length) {
|
|
524
387
|
const badges = createElement(
|
|
525
388
|
document,
|
|
@@ -532,7 +395,6 @@ function createIdentityRow(
|
|
|
532
395
|
column.appendChild(badges);
|
|
533
396
|
}
|
|
534
397
|
for (const key of ['title', 'subtitle'] as const) {
|
|
535
|
-
if (key === 'title' && hasTitleRuns) continue;
|
|
536
398
|
const lines = row.style?.[key]?.lines ?? row[`${key}Lines`];
|
|
537
399
|
if (lines !== undefined)
|
|
538
400
|
column
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
package com.margelo.nitro.nativelist
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Widths for identity title runs (docs/SPEC.md, "Identity title segments and
|
|
5
|
-
* leading title icon"). A run's base width is its natural width under its cap;
|
|
6
|
-
* the one shrinking run takes what the others leave; runs that do not shrink
|
|
7
|
-
* and still overflow are cut from the end of the line.
|
|
8
|
-
*/
|
|
9
|
-
internal fun nativeListTitleSegmentWidths(
|
|
10
|
-
natural: List<Float>,
|
|
11
|
-
shrink: List<Boolean>,
|
|
12
|
-
maxWidthRatio: List<Float?>,
|
|
13
|
-
available: Float,
|
|
14
|
-
): List<Float> {
|
|
15
|
-
val line = available.coerceAtLeast(0f)
|
|
16
|
-
val widths =
|
|
17
|
-
natural.indices
|
|
18
|
-
.map { index ->
|
|
19
|
-
val width = natural[index].coerceAtLeast(0f)
|
|
20
|
-
val ratio = maxWidthRatio.getOrNull(index)
|
|
21
|
-
if (ratio == null) width else minOf(width, ratio * line)
|
|
22
|
-
}
|
|
23
|
-
.toMutableList()
|
|
24
|
-
val shrinking = shrink.indexOf(true)
|
|
25
|
-
if (shrinking in widths.indices) {
|
|
26
|
-
val others = widths.indices.filter { it != shrinking }.sumOf { widths[it].toDouble() }.toFloat()
|
|
27
|
-
widths[shrinking] = minOf(widths[shrinking], line - others).coerceAtLeast(0f)
|
|
28
|
-
}
|
|
29
|
-
var overflow = widths.sum() - line
|
|
30
|
-
var index = widths.lastIndex
|
|
31
|
-
while (overflow > 0f && index >= 0) {
|
|
32
|
-
val cut = minOf(widths[index], overflow)
|
|
33
|
-
widths[index] -= cut
|
|
34
|
-
overflow -= cut
|
|
35
|
-
index -= 1
|
|
36
|
-
}
|
|
37
|
-
return widths
|
|
38
|
-
}
|