@onekeyfe/react-native-native-list 6.7.0-alpha.403 → 6.7.0-alpha.405

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.
@@ -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' | 'titleSegments' | 'titleLeadingIcon' | 'subtitle' | 'subtitleLines' | 'contentHeight' | 'longPressActionKey' | 'tertiary' | 'tertiaryTone' | 'badges' | 'trailing' | 'leading' | 'leadingAction' | 'titleMatch' | 'titleActionKey' | 'titleActionOnHover' | 'subtitleSegments'>;
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.403",
3
+ "version": "6.7.0-alpha.405",
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.403",
90
- "@onekeyfe/react-native-native-logger": "6.7.0-alpha.403",
89
+ "@onekeyfe/react-native-image": "6.7.0-alpha.405",
90
+ "@onekeyfe/react-native-native-logger": "6.7.0-alpha.405",
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) => {
@@ -3194,7 +3083,9 @@ function isAllowedImageUri(value: unknown): value is string {
3194
3083
  typeof value === 'string' &&
3195
3084
  value.trim().length > 0 &&
3196
3085
  (!/^[a-z][a-z\d+.-]*:/i.test(value) ||
3197
- /^(https?|file|content|ph|assets-library|asset|res|blob):/i.test(value) ||
3086
+ /^(https?|file|content|ph|assets-library|asset|res|blob|onekey-avatar):/i.test(
3087
+ value
3088
+ ) ||
3198
3089
  /^data:image\//i.test(value))
3199
3090
  );
3200
3091
  }
@@ -3574,14 +3465,6 @@ const NESTED_DECORATION_FIELDS: Readonly<Record<string, readonly string[]>> = {
3574
3465
 
3575
3466
  function sanitizeDecoration(value: unknown, field: string): unknown {
3576
3467
  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
3468
  if (
3586
3469
  !Array.isArray(value) &&
3587
3470
  [
@@ -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-leading-icon{flex:0 0 16px;width:16px;height:16px;display:inline-flex;align-items:center;justify-content:center;color:var(--nl-icon-subdued);font-size:16px}.ok-native-list-identity-title-text{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
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
- }