@onekeyfe/react-native-native-list 6.7.0-alpha.430 → 6.7.0-alpha.435
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 +20 -87
- package/ios/NativeListIdentityRenderer.swift +13 -89
- package/ios/tests/NativeListAccountSelectorLayoutTests.swift +96 -0
- package/ios/tests/NativeListIdentityTitleIconLayoutTests.swift +0 -32
- 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/NativeList.types.d.ts +2 -2
- package/lib/typescript/src/models.d.ts +1 -21
- package/package.json +9 -9
- package/src/NativeList.types.ts +2 -2
- 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
|
@@ -31,8 +31,6 @@ internal class NativeListIdentityRowView(context: ThemedReactContext, fonts: Nat
|
|
|
31
31
|
private val titleLine = PackedTitleLineLayout(context)
|
|
32
32
|
private val title = NativeListTextView(context)
|
|
33
33
|
private val titleIcon = OneKeyIconView(context)
|
|
34
|
-
private val titleLeadingIcon = OneKeyIconView(context)
|
|
35
|
-
private val titleSegments = NativeListTitleSegmentsView(context)
|
|
36
34
|
private var titleImage: NativeListImageSlot? = null
|
|
37
35
|
private val subtitle = NativeListTextView(context)
|
|
38
36
|
private val tertiary = NativeListTextView(context)
|
|
@@ -111,13 +109,8 @@ internal class NativeListIdentityRowView(context: ThemedReactContext, fonts: Nat
|
|
|
111
109
|
mainColumn.gravity = Gravity.CENTER_VERTICAL
|
|
112
110
|
titleLine.orientation = HORIZONTAL
|
|
113
111
|
titleLine.gravity = Gravity.CENTER_VERTICAL
|
|
114
|
-
titleLine.addView(titleLeadingIcon, LayoutParams(dp(16), dp(16)).apply { marginEnd = dp(6) })
|
|
115
112
|
titleLine.addView(title, LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f))
|
|
116
|
-
titleLine.addView(titleSegments, LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f))
|
|
117
113
|
titleLine.addView(titleIcon, LayoutParams(dp(20), dp(20)).apply { marginStart = dp(4) })
|
|
118
|
-
titleLine.titleChild = title
|
|
119
|
-
titleLeadingIcon.visibility = GONE
|
|
120
|
-
titleSegments.visibility = GONE
|
|
121
114
|
titleLine.addView(badgeLine)
|
|
122
115
|
trailingColumn.onAction = { key, view, slot, target ->
|
|
123
116
|
emitAction(key, view, "trailingAccessory", slot, target, trailingColumn.anchorInset(view))
|
|
@@ -230,12 +223,6 @@ internal class NativeListIdentityRowView(context: ThemedReactContext, fonts: Nat
|
|
|
230
223
|
titleIcon.visibility = GONE
|
|
231
224
|
titleIcon.contentDescription = null
|
|
232
225
|
titleIcon.setOnClickListener(null)
|
|
233
|
-
titleLeadingIcon.visibility = GONE
|
|
234
|
-
titleLeadingIcon.contentDescription = null
|
|
235
|
-
titleSegments.visibility = GONE
|
|
236
|
-
titleSegments.clearRuns()
|
|
237
|
-
titleSegments.layoutParams = LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f)
|
|
238
|
-
titleLine.titleChild = title
|
|
239
226
|
for (view in listOf(title, subtitle, tertiary, badgeLine)) {
|
|
240
227
|
view.text = ""
|
|
241
228
|
view.visibility = GONE
|
|
@@ -356,46 +343,6 @@ internal class NativeListIdentityRowView(context: ThemedReactContext, fonts: Nat
|
|
|
356
343
|
}
|
|
357
344
|
// Legacy: overhanging selector icons/checkboxes must not be clipped by row padding.
|
|
358
345
|
if (trailingColumn.parent === this && trailingColumn.requestsUnclippedHost) clipToPadding = false
|
|
359
|
-
if (titleSegments.visibility == VISIBLE) bindTitleRuns(item, theme)
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// Runs take the title's final text appearance and color unless they set
|
|
363
|
-
// their own tone or weight.
|
|
364
|
-
private fun bindTitleRuns(item: NativeListItem, theme: JSONObject?) {
|
|
365
|
-
titleSegments.typeface = title.typeface
|
|
366
|
-
titleSegments.setTextSize(android.util.TypedValue.COMPLEX_UNIT_PX, title.textSize)
|
|
367
|
-
titleSegments.includeFontPadding = title.includeFontPadding
|
|
368
|
-
titleSegments.letterSpacing = title.letterSpacing
|
|
369
|
-
titleSegments.opticalOffsetY = title.opticalOffsetY
|
|
370
|
-
titleSegments.maxLines = 1
|
|
371
|
-
titleSegments.ellipsize = TextUtils.TruncateAt.END
|
|
372
|
-
TextViewCompat.setLineHeight(titleSegments, title.lineHeight)
|
|
373
|
-
val baseColor = title.currentTextColor
|
|
374
|
-
titleSegments.setTextColor(baseColor)
|
|
375
|
-
val segments = item.json.optJSONArray("titleSegments") ?: return
|
|
376
|
-
val runs =
|
|
377
|
-
(0 until segments.length()).mapNotNull { index ->
|
|
378
|
-
val segment = segments.optJSONObject(index) ?: return@mapNotNull null
|
|
379
|
-
NativeListTitleSegmentsView.Run(
|
|
380
|
-
text = segment.optString("text"),
|
|
381
|
-
typeface =
|
|
382
|
-
when (segment.optString("fontWeight")) {
|
|
383
|
-
"medium" -> fonts.medium(context)
|
|
384
|
-
"regular" -> fonts.regular(context)
|
|
385
|
-
else -> null
|
|
386
|
-
},
|
|
387
|
-
color =
|
|
388
|
-
when (segment.optString("tone")) {
|
|
389
|
-
"secondary" -> color(theme, "secondaryText", "#0000009B")
|
|
390
|
-
"primary" -> color(theme, "primaryText", "#000000DF")
|
|
391
|
-
else -> baseColor
|
|
392
|
-
},
|
|
393
|
-
shrink = segment.optBoolean("shrink", false),
|
|
394
|
-
maxWidthRatio =
|
|
395
|
-
if (segment.has("maxWidthRatio")) segment.optDouble("maxWidthRatio").toFloat() else null,
|
|
396
|
-
)
|
|
397
|
-
}
|
|
398
|
-
titleSegments.setRuns(runs, item.json.optString("title"))
|
|
399
346
|
}
|
|
400
347
|
|
|
401
348
|
private fun styleGap(column: LinearLayout, gap: Int) {
|
|
@@ -529,8 +476,7 @@ internal class NativeListIdentityRowView(context: ThemedReactContext, fonts: Nat
|
|
|
529
476
|
val imageWidth = titleImage?.view?.takeIf { it.visibility == VISIBLE }?.let {
|
|
530
477
|
it.layoutParams.width + dp(4)
|
|
531
478
|
} ?: 0
|
|
532
|
-
val icon = (if (titleIcon.visibility == VISIBLE) dp(24) else 0) + imageWidth
|
|
533
|
-
(if (titleLeadingIcon.visibility == VISIBLE) dp(22) else 0)
|
|
479
|
+
val icon = (if (titleIcon.visibility == VISIBLE) dp(24) else 0) + imageWidth
|
|
534
480
|
(inner * ratio).roundToInt()
|
|
535
481
|
.minus(leading.width + leading.marginEnd + icon)
|
|
536
482
|
.coerceAtLeast(0)
|
|
@@ -541,10 +487,9 @@ internal class NativeListIdentityRowView(context: ThemedReactContext, fonts: Nat
|
|
|
541
487
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
|
542
488
|
if (titleSuffixLabels.isNotEmpty()) {
|
|
543
489
|
val fixedWidth =
|
|
544
|
-
(
|
|
490
|
+
(1 until titleLine.childCount).sumOf { index ->
|
|
545
491
|
val view = titleLine.getChildAt(index)
|
|
546
|
-
if (view.visibility == GONE ||
|
|
547
|
-
titleSuffixLabels.any { it === view }) 0
|
|
492
|
+
if (view.visibility == GONE || titleSuffixLabels.any { it === view }) 0
|
|
548
493
|
else {
|
|
549
494
|
val margins = view.layoutParams as MarginLayoutParams
|
|
550
495
|
view.measuredWidth + margins.leftMargin + margins.rightMargin
|
|
@@ -808,30 +753,6 @@ internal class NativeListIdentityRowView(context: ThemedReactContext, fonts: Nat
|
|
|
808
753
|
titleLine.packsChildrenAtStart = true
|
|
809
754
|
title.ellipsize = TextUtils.TruncateAt.END
|
|
810
755
|
showText(title, item.json.optString("title"), item.json.optInt("titleLines", 1))
|
|
811
|
-
// Runs replace the drawn title except where `title` itself is highlighted;
|
|
812
|
-
// their content is bound once the title style is final.
|
|
813
|
-
if ((item.json.optJSONArray("titleSegments")?.length() ?: 0) > 0 &&
|
|
814
|
-
(item.json.optJSONArray("titleMatch")?.length() ?: 0) == 0
|
|
815
|
-
) {
|
|
816
|
-
title.visibility = GONE
|
|
817
|
-
titleSegments.visibility = VISIBLE
|
|
818
|
-
titleLine.titleChild = titleSegments
|
|
819
|
-
}
|
|
820
|
-
item.json.optJSONObject("titleLeadingIcon")?.let { icon ->
|
|
821
|
-
titleLeadingIcon.iconName = icon.optString("name")
|
|
822
|
-
titleLeadingIcon.glyphSizeDp = 16
|
|
823
|
-
titleLeadingIcon.useSourceScale = sourceScale
|
|
824
|
-
titleLeadingIcon.tintColor = safeColor(
|
|
825
|
-
icon.optString("tintColor").takeIf(String::isNotEmpty),
|
|
826
|
-
color(theme, "iconSubdued", "#00000072"),
|
|
827
|
-
)
|
|
828
|
-
titleLeadingIcon.contentDescription =
|
|
829
|
-
icon.optString("accessibilityLabel").takeIf(String::isNotEmpty)
|
|
830
|
-
titleLeadingIcon.importantForAccessibility =
|
|
831
|
-
if (titleLeadingIcon.contentDescription == null) View.IMPORTANT_FOR_ACCESSIBILITY_NO
|
|
832
|
-
else View.IMPORTANT_FOR_ACCESSIBILITY_YES
|
|
833
|
-
titleLeadingIcon.visibility = VISIBLE
|
|
834
|
-
}
|
|
835
756
|
item.json.optJSONArray("titleSuffixes")?.let { suffixes ->
|
|
836
757
|
for (index in 0 until suffixes.length()) {
|
|
837
758
|
val suffix = suffixes.optJSONObject(index) ?: continue
|
|
@@ -109,9 +109,6 @@ internal class PackedTitleLineLayout(context: android.content.Context) : LinearL
|
|
|
109
109
|
var packsChildrenAtStart = false
|
|
110
110
|
// OneKey patch: optional cap for the Market subtitle's localized name.
|
|
111
111
|
var leadingTextMaxWidth = Int.MAX_VALUE
|
|
112
|
-
// The text view that takes what the other children leave; the first child
|
|
113
|
-
// when unset, so a glyph may precede the title once this names it.
|
|
114
|
-
var titleChild: TextView? = null
|
|
115
112
|
|
|
116
113
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
117
114
|
if (!packsChildrenAtStart || childCount < 2) {
|
|
@@ -119,13 +116,13 @@ internal class PackedTitleLineLayout(context: android.content.Context) : LinearL
|
|
|
119
116
|
return
|
|
120
117
|
}
|
|
121
118
|
|
|
122
|
-
val title =
|
|
119
|
+
val title = getChildAt(0)
|
|
123
120
|
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
|
|
124
121
|
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
|
|
125
122
|
var accessoryWidth = 0
|
|
126
|
-
for (index in
|
|
123
|
+
for (index in 1 until childCount) {
|
|
127
124
|
val child = getChildAt(index)
|
|
128
|
-
if (child
|
|
125
|
+
if (child.visibility == GONE) continue
|
|
129
126
|
measureChildWithMargins(
|
|
130
127
|
child,
|
|
131
128
|
widthMeasureSpec,
|
package/docs/ROW_TEMPLATES.md
CHANGED
|
@@ -54,14 +54,7 @@ separately from source implementation in STYLE_SPEC §9.
|
|
|
54
54
|
|
|
55
55
|
- **Use:** an identifiable asset, account, wallet, network or setting.
|
|
56
56
|
- **Required data:** `leading`, `title`; optional `subtitle`, `tertiary`, `badges`,
|
|
57
|
-
`trailing`, `leadingAction`, `titleIcon`, Web-only `secondaryTitleIcon`,
|
|
58
|
-
`titleLeadingIcon`, match ranges and structured subtitle segments.
|
|
59
|
-
- **Title runs:** optional `titleSegments` draw 1...4 runs with their own tone and
|
|
60
|
-
weight in place of `title`, on one line. One run may shrink; the others keep
|
|
61
|
-
their width under an optional `maxWidthRatio` cap. `title` stays the spoken
|
|
62
|
-
text. See SPEC.md.
|
|
63
|
-
- **Leading title icon:** optional `titleLeadingIcon` draws a 16-point glyph 6
|
|
64
|
-
points before the title; it is decorative unless labelled.
|
|
57
|
+
`trailing`, `leadingAction`, `titleIcon`, Web-only `secondaryTitleIcon`, match ranges and structured subtitle segments.
|
|
65
58
|
- **Static title icon:** optional 20-point icon immediately after the title with
|
|
66
59
|
a 4-point gap. It has no click action; an optional Web `hoverActionKey`
|
|
67
60
|
emits an icon-anchored tooltip request on hover or touch. A trailing `button` is the fixed small
|
package/docs/SPEC.md
CHANGED
|
@@ -188,93 +188,6 @@ and Web acceptance must cover narrow addresses, dynamic type/fonts, long press
|
|
|
188
188
|
versus tap, keyboard interaction, and the horizontal warning border. Source or
|
|
189
189
|
build success alone is not runtime evidence.
|
|
190
190
|
|
|
191
|
-
### Identity title segments and leading title icon
|
|
192
|
-
|
|
193
|
-
Status: implemented in source for iOS, Android and Web; device and Web runtime
|
|
194
|
-
acceptance is pending. These fields travel in snapshot/patch JSON and do not
|
|
195
|
-
change the Nitro ABI. They serve recipient rows whose title is a wallet and an
|
|
196
|
-
account name in two styles, and address book rows marked by an icon before the
|
|
197
|
-
name.
|
|
198
|
-
|
|
199
|
-
`identity.titleSegments` is an optional list of 1...4 runs drawn on the title
|
|
200
|
-
line in place of `title`:
|
|
201
|
-
|
|
202
|
-
```ts
|
|
203
|
-
titleSegments?: readonly {
|
|
204
|
-
text: string;
|
|
205
|
-
tone?: 'primary' | 'secondary';
|
|
206
|
-
fontWeight?: 'regular' | 'medium';
|
|
207
|
-
shrink?: boolean;
|
|
208
|
-
maxWidthRatio?: number; // (0, 1]
|
|
209
|
-
}[];
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
- `title` stays required. It is the spoken label and the fallback text, so it
|
|
213
|
-
carries the same words as the runs; every renderer exposes `title`, not the
|
|
214
|
-
runs, to accessibility.
|
|
215
|
-
- Each run uses the title's font family, size and line height, including
|
|
216
|
-
`style.title` and the configured fonts. `tone` picks the primary or secondary
|
|
217
|
-
text color and defaults to the title color; `fontWeight` picks the weight and
|
|
218
|
-
defaults to the title weight.
|
|
219
|
-
- The runs share one line. `titleLines` and `style.title.lines` do not apply.
|
|
220
|
-
- Layout. The runs take the whole width the title line leaves after the
|
|
221
|
-
leading title icon and the title accessories (title icon, title image,
|
|
222
|
-
suffixes, badges), so those accessories sit at the line's end rather than
|
|
223
|
-
right after the text. W is that width:
|
|
224
|
-
1. A run's base width is its natural width, capped at `maxWidthRatio x W`
|
|
225
|
-
when given. A run longer than its cap truncates at the cap with a tail
|
|
226
|
-
ellipsis.
|
|
227
|
-
2. At most one run sets `shrink: true`. Every other run keeps its base width.
|
|
228
|
-
3. The shrinking run gets `min(base, W - sum of the other runs)` and
|
|
229
|
-
truncates with a tail ellipsis; it may shrink to zero width.
|
|
230
|
-
4. When the runs that do not shrink exceed W on their own, the line is cut at
|
|
231
|
-
its end. Native renderers end the last visible run with an ellipsis; Web
|
|
232
|
-
clips it. This divergence is accepted: callers avoid the case with
|
|
233
|
-
`maxWidthRatio`.
|
|
234
|
-
- Development validation rejects an empty or oversized list, blank or
|
|
235
|
-
oversized text (the 4096-code-unit text bound), unknown keys, tones or
|
|
236
|
-
weights, a ratio outside `(0, 1]`, more than one shrinking run, `titleMatch`
|
|
237
|
-
on the same row (its ranges index `title`), and
|
|
238
|
-
`presentation: 'walletSidebar'`. Production treats the runs as decoration:
|
|
239
|
-
an invalid list is dropped and the row shows `title`. Renderers draw `title`
|
|
240
|
-
instead of runs on a row that also has `titleMatch`, and ignore runs and the
|
|
241
|
-
leading icon with `presentation: 'walletSidebar'`.
|
|
242
|
-
|
|
243
|
-
`identity.titleLeadingIcon` is an optional `{ name, tintColor?,
|
|
244
|
-
accessibilityLabel? }`: a 16-point glyph before the title text or its runs,
|
|
245
|
-
with a 6-point gap, vertically centered on the title line. It keeps its 16
|
|
246
|
-
points: a long title, or its shrinking run, truncates first. The name comes from
|
|
247
|
-
the same closed icon set as `titleIcon`; an unmapped name leaves an empty
|
|
248
|
-
16-point slot. `tintColor` is a hex `#RRGGBB` or `#RRGGBBAA` color and defaults
|
|
249
|
-
to the theme's `iconSubdued`. Without `accessibilityLabel` the glyph is hidden
|
|
250
|
-
from accessibility. It has no action of its own. Development validation
|
|
251
|
-
rejects unknown keys, a blank name, a malformed color and
|
|
252
|
-
`presentation: 'walletSidebar'`; production drops an invalid icon.
|
|
253
|
-
|
|
254
|
-
Both fields are identity patch fields. Omission in a full snapshot removes
|
|
255
|
-
them; omission in a patch keeps the current value. Rebind and recycle remove the
|
|
256
|
-
previous runs and glyph, so a row without them never shows another row's.
|
|
257
|
-
|
|
258
|
-
| Platform | Title runs | Leading icon |
|
|
259
|
-
| --- | --- | --- |
|
|
260
|
-
| iOS | `NativeListTitleSegmentsLabel` draws the runs at the widths that `NativeListTitleSegmentLayout.swift` allocates for its bounds; it fills the title row in place of the row's filler. | `NativeListIconImageView`, first in the title row stack. A plain title beside it drops below required horizontal compression resistance, as beside `titleIcon`, so the title truncates before the glyph narrows. |
|
|
261
|
-
| Android | `NativeListTitleSegmentsView`, a `TextView` whose text is rebuilt for each measured width from `NativeListTitleSegmentLayout.kt` and which measures to the width it is offered. `PackedTitleLineLayout.titleChild` names the flexible title view, so a view may precede it. | `OneKeyIconView`, first in the title line. |
|
|
262
|
-
| Web | A flex line that fills the title line: the shrinking run is `flex: 0 1 auto; min-width: 0`, the others `flex: 0 0 auto` with a `max-width` percentage for a cap. | An icon element before the title text. |
|
|
263
|
-
|
|
264
|
-
Conformance: `src/validation.ts` (`assertTitleSegments`,
|
|
265
|
-
`assertTitleLeadingIcon`, production sanitizing) with `validation.test.ts`;
|
|
266
|
-
`src/web/templates/IdentityRowRenderer.ts` with `IdentityTitleRunsWeb.test.ts`
|
|
267
|
-
(run styles, flex behavior, accessibility, rebind);
|
|
268
|
-
`ios/NativeListTitleSegmentLayout.swift` with the SwiftPM
|
|
269
|
-
`NativeListTitleSegmentLayoutTests`; the iOS leading glyph beside a long plain
|
|
270
|
-
title with the XCTest `NativeListIdentityTitleIconLayoutTests`;
|
|
271
|
-
`NativeListTitleSegmentLayout.kt` with the
|
|
272
|
-
JVM `NativeListTitleSegmentLayoutTest`. iOS, Android and Web runtime acceptance is pending: recipient rows
|
|
273
|
-
with long wallet and account names in both truncation directions, narrow rows,
|
|
274
|
-
dark mode, dynamic type and configured fonts, and recycling between rows with
|
|
275
|
-
and without runs or a leading icon. Source and build success alone are not
|
|
276
|
-
runtime evidence.
|
|
277
|
-
|
|
278
191
|
### DeFi position row controls
|
|
279
192
|
|
|
280
193
|
Status: implemented in local source, unreleased. Focused validation and Web
|
|
@@ -852,6 +765,26 @@ Existing geometry/typography differences are recorded in
|
|
|
852
765
|
them. iOS uses UICollectionView, Android RecyclerView, and Web a windowed DOM
|
|
853
766
|
engine. See [DESIGN.md](DESIGN.md) for their current ownership boundaries.
|
|
854
767
|
|
|
768
|
+
### iOS account-selector trailing width (OK-65638)
|
|
769
|
+
|
|
770
|
+
Status: implemented. Manual consuming-app verification on an iOS Simulator
|
|
771
|
+
reported normal balance rendering after a local native rebuild. Automated
|
|
772
|
+
XCTest execution and broader device acceptance are tracked separately.
|
|
773
|
+
|
|
774
|
+
For an `identity` row with `presentation: 'accountSelector'`, an empty trailing
|
|
775
|
+
collection or a spinner-only accessory MUST leave the remaining horizontal
|
|
776
|
+
space available to the title and subtitle column. A short title MUST NOT cause
|
|
777
|
+
the balance subtitle to collapse while unused row width remains. The existing
|
|
778
|
+
gap before the trailing column and the shortened address's compression priority
|
|
779
|
+
remain unchanged; content may still truncate when the row is actually too narrow.
|
|
780
|
+
Visible controls keep their intrinsic width without introducing required-width
|
|
781
|
+
conflicts. Rebinding between empty, spinner and single-icon states MUST release
|
|
782
|
+
the previous row's width constraint.
|
|
783
|
+
|
|
784
|
+
`NativeListAccountSelectorLayoutTests` covers balance/address geometry and
|
|
785
|
+
control widths on production UIKit cells, including rebinds. Android and Web
|
|
786
|
+
keep their existing layout and public interfaces.
|
|
787
|
+
|
|
855
788
|
### Android section-index attachment
|
|
856
789
|
|
|
857
790
|
When `sectionIndex.centeredInWindow` is enabled, the Android index rail moves
|
|
@@ -81,8 +81,7 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
81
81
|
let title = NativeListResolvedText(
|
|
82
82
|
item.data.string("title"), style: style.dictionary("title"), size: 16,
|
|
83
83
|
weight: .medium, color: .black, lineHeight: 24,
|
|
84
|
-
lines: item.data.
|
|
85
|
-
? item.data.int("titleLines", default: 1) : 1, fonts: fonts)
|
|
84
|
+
lines: item.data.int("titleLines", default: 1), fonts: fonts)
|
|
86
85
|
let subtitle = NativeListResolvedText(
|
|
87
86
|
item.data.string("subtitle"), style: style.dictionary("subtitle"), size: 14,
|
|
88
87
|
color: .black, lineHeight: 20,
|
|
@@ -111,12 +110,6 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
111
110
|
private let titleRowStack = UIStackView()
|
|
112
111
|
private let titleLabel = NativeListTextLabel()
|
|
113
112
|
private let titleIconView = NativeListIconImageView()
|
|
114
|
-
private let titleLeadingIconView = NativeListIconImageView()
|
|
115
|
-
private let titleSegmentsLabel = NativeListTitleSegmentsLabel()
|
|
116
|
-
// Runs, when shown, take the title's place in the title row.
|
|
117
|
-
private var visibleTitleView: UIView {
|
|
118
|
-
titleSegmentsLabel.isHidden ? titleLabel : titleSegmentsLabel
|
|
119
|
-
}
|
|
120
113
|
private var titleImageSlot: NativeListImageSlot?
|
|
121
114
|
private var titleImageConstraints: [NSLayoutConstraint] = []
|
|
122
115
|
// Absorbs the title row's slack so the title keeps its intrinsic width and the
|
|
@@ -177,9 +170,7 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
177
170
|
mainStack.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
|
178
171
|
titleRowStack.axis = .horizontal
|
|
179
172
|
titleRowStack.alignment = .center
|
|
180
|
-
titleRowStack.addArrangedSubview(titleLeadingIconView)
|
|
181
173
|
titleRowStack.addArrangedSubview(titleLabel)
|
|
182
|
-
titleRowStack.addArrangedSubview(titleSegmentsLabel)
|
|
183
174
|
titleRowStack.addArrangedSubview(titleIconView)
|
|
184
175
|
titleRowStack.addArrangedSubview(badgeLabel)
|
|
185
176
|
titleRowStack.addArrangedSubview(titleRowFiller)
|
|
@@ -194,18 +185,6 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
194
185
|
])
|
|
195
186
|
titleIconView.contentMode = .scaleAspectFit
|
|
196
187
|
titleIconView.setContentHuggingPriority(.required, for: .horizontal)
|
|
197
|
-
titleLeadingIconView.translatesAutoresizingMaskIntoConstraints = false
|
|
198
|
-
let leadingIconWidth = titleLeadingIconView.widthAnchor.constraint(equalToConstant: 16)
|
|
199
|
-
leadingIconWidth.priority = UILayoutPriority(999)
|
|
200
|
-
NSLayoutConstraint.activate([
|
|
201
|
-
leadingIconWidth,
|
|
202
|
-
titleLeadingIconView.heightAnchor.constraint(equalToConstant: 16),
|
|
203
|
-
])
|
|
204
|
-
titleLeadingIconView.contentMode = .scaleAspectFit
|
|
205
|
-
titleLeadingIconView.setContentHuggingPriority(.required, for: .horizontal)
|
|
206
|
-
titleLeadingIconView.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
207
|
-
titleLeadingIconView.isHidden = true
|
|
208
|
-
titleSegmentsLabel.isHidden = true
|
|
209
188
|
[titleRowStack, subtitleLabel, tertiaryLabel].forEach(mainStack.addArrangedSubview)
|
|
210
189
|
visual.translatesAutoresizingMaskIntoConstraints = false
|
|
211
190
|
leadingWidth = visual.widthAnchor.constraint(equalToConstant: 40)
|
|
@@ -270,15 +249,6 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
270
249
|
titleIconView.isHidden = true
|
|
271
250
|
titleIconView.accessibilityLabel = nil
|
|
272
251
|
titleIconView.isAccessibilityElement = false
|
|
273
|
-
titleLeadingIconView.image = nil
|
|
274
|
-
titleLeadingIconView.isHidden = true
|
|
275
|
-
titleLeadingIconView.accessibilityLabel = nil
|
|
276
|
-
titleLeadingIconView.isAccessibilityElement = false
|
|
277
|
-
titleSegmentsLabel.isHidden = true
|
|
278
|
-
titleSegmentsLabel.reset()
|
|
279
|
-
titleRowFiller.isHidden = false
|
|
280
|
-
titleSegmentsLabel.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
281
|
-
titleSegmentsLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
|
282
252
|
titleImageSlot?.view.isHidden = true
|
|
283
253
|
titleImageSlot?.view.accessibilityLabel = nil
|
|
284
254
|
titleImageSlot?.view.isAccessibilityElement = false
|
|
@@ -340,7 +310,7 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
340
310
|
let label = titleIcon.string("accessibilityLabel")
|
|
341
311
|
titleIconView.accessibilityLabel = label.isEmpty ? nil : label
|
|
342
312
|
titleIconView.isAccessibilityElement = !label.isEmpty
|
|
343
|
-
titleRowStack.setCustomSpacing(4, after:
|
|
313
|
+
titleRowStack.setCustomSpacing(4, after: titleLabel)
|
|
344
314
|
// A long title truncates ahead of the 20pt glyph (Android caps the title
|
|
345
315
|
// at row width minus accessories, Web shrinks the title first); with the
|
|
346
316
|
// title at `.required` the icon's 999 width constraint collapsed to 0.
|
|
@@ -372,7 +342,7 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
372
342
|
]
|
|
373
343
|
titleImageConstraints.forEach { $0.priority = UILayoutPriority(999) }
|
|
374
344
|
NSLayoutConstraint.activate(titleImageConstraints)
|
|
375
|
-
titleRowStack.setCustomSpacing(4, after: titleIconView.isHidden ?
|
|
345
|
+
titleRowStack.setCustomSpacing(4, after: titleIconView.isHidden ? titleLabel : titleIconView)
|
|
376
346
|
titleLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
|
377
347
|
slot.bind(image, key: "\(item.key):titleImage", placeholder: "#00000000")
|
|
378
348
|
} else {
|
|
@@ -415,7 +385,7 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
415
385
|
titleRowStack.setCustomSpacing(
|
|
416
386
|
CGFloat(style.double("titleBadgeGap")),
|
|
417
387
|
after: titleImageSlot?.view.isHidden == false ? titleImageSlot!.view
|
|
418
|
-
: titleIconView.isHidden ?
|
|
388
|
+
: titleIconView.isHidden ? titleLabel : titleIconView)
|
|
419
389
|
}
|
|
420
390
|
}
|
|
421
391
|
if style["trailingGap"] != nil { trailingStack.spacing = CGFloat(style.double("trailingGap")) }
|
|
@@ -429,7 +399,6 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
429
399
|
labels.forEach { NativeListTextStyles.applyStyledText($0, text, fonts: fonts) }
|
|
430
400
|
}
|
|
431
401
|
}
|
|
432
|
-
if !titleSegmentsLabel.isHidden { bindTitleRuns(item, theme: theme, style: style) }
|
|
433
402
|
contentInsets = UIEdgeInsets(
|
|
434
403
|
top: topInset, left: leftInset, bottom: -bottomInset, right: -rightInset)
|
|
435
404
|
if root.axis == .horizontal {
|
|
@@ -495,8 +464,6 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
495
464
|
}
|
|
496
465
|
override func recycleContent() {
|
|
497
466
|
visual.recycle()
|
|
498
|
-
titleSegmentsLabel.reset()
|
|
499
|
-
titleLeadingIconView.image = nil
|
|
500
467
|
titleImageSlot?.recycle()
|
|
501
468
|
titleImageSlot?.view.isHidden = true
|
|
502
469
|
titleImageSlot?.view.accessibilityLabel = nil
|
|
@@ -545,30 +512,6 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
545
512
|
visual.bind(
|
|
546
513
|
descriptor, style: image, key: key, theme: currentTheme, isUnread: false, fonts: fonts)
|
|
547
514
|
}
|
|
548
|
-
// Runs take the title's font, size and color unless they set their own tone
|
|
549
|
-
// or weight; the title label holds the final style by now.
|
|
550
|
-
private func bindTitleRuns(_ item: NativeListItem, theme: [String: Any]?, style: [String: Any]) {
|
|
551
|
-
let baseFont = titleLabel.font ?? nativeListFont(ofSize: 16, weight: .medium, fonts: fonts)
|
|
552
|
-
let baseColor = titleLabel.textColor ?? nativeListColor(theme, "primaryText", "#202020")
|
|
553
|
-
let runs = item.data.dictionaries("titleSegments").map { segment in
|
|
554
|
-
let weight = segment.string("fontWeight")
|
|
555
|
-
let tone = segment.string("tone")
|
|
556
|
-
return NativeListTitleSegmentsLabel.Run(
|
|
557
|
-
text: segment.string("text"),
|
|
558
|
-
font: weight.isEmpty
|
|
559
|
-
? baseFont
|
|
560
|
-
: nativeListFont(
|
|
561
|
-
ofSize: baseFont.pointSize, weight: weight == "medium" ? .medium : .regular, fonts: fonts),
|
|
562
|
-
color: tone == "secondary"
|
|
563
|
-
? nativeListColor(theme, "secondaryText", "#646464")
|
|
564
|
-
: tone == "primary" ? nativeListColor(theme, "primaryText", "#202020") : baseColor,
|
|
565
|
-
shrink: segment.bool("shrink"),
|
|
566
|
-
maxWidthRatio: (segment["maxWidthRatio"] as? NSNumber).map { CGFloat(truncating: $0) })
|
|
567
|
-
}
|
|
568
|
-
titleSegmentsLabel.lineHeight = CGFloat(
|
|
569
|
-
style.dictionary("title")?.double("lineHeight", default: 24) ?? 24)
|
|
570
|
-
titleSegmentsLabel.configure(runs, spokenText: item.data.string("title"))
|
|
571
|
-
}
|
|
572
515
|
private func bindIdentity(
|
|
573
516
|
_ item: NativeListItem,
|
|
574
517
|
theme: [String: Any]?,
|
|
@@ -700,36 +643,10 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
700
643
|
}
|
|
701
644
|
root.addArrangedSubview(mainStack)
|
|
702
645
|
show(titleLabel, item.data.string("title"), lines: item.data.int("titleLines", default: 1))
|
|
703
|
-
// Runs replace the drawn title except where `title` itself is highlighted;
|
|
704
|
-
// their content is bound once the title style is final.
|
|
705
|
-
if !item.data.dictionaries("titleSegments").isEmpty,
|
|
706
|
-
item.data.dictionaries("titleMatch").isEmpty
|
|
707
|
-
{
|
|
708
|
-
titleLabel.isHidden = true
|
|
709
|
-
titleSegmentsLabel.isHidden = false
|
|
710
|
-
// The runs fill what the title row leaves, so a ratio is a share of it.
|
|
711
|
-
titleRowFiller.isHidden = true
|
|
712
|
-
titleSegmentsLabel.setContentHuggingPriority(UILayoutPriority(1), for: .horizontal)
|
|
713
|
-
}
|
|
714
|
-
if let icon = item.data.dictionary("titleLeadingIcon") {
|
|
715
|
-
titleLeadingIconView.bindIcon(icon.string("name"))
|
|
716
|
-
titleLeadingIconView.tintColor = UIColor(
|
|
717
|
-
nativeListHex: icon.string("tintColor",
|
|
718
|
-
default: theme?["iconSubdued"] as? String ?? "#8D8D8D"),
|
|
719
|
-
fallback: .gray)
|
|
720
|
-
titleLeadingIconView.isHidden = false
|
|
721
|
-
let label = icon.string("accessibilityLabel")
|
|
722
|
-
titleLeadingIconView.accessibilityLabel = label.isEmpty ? nil : label
|
|
723
|
-
titleLeadingIconView.isAccessibilityElement = !label.isEmpty
|
|
724
|
-
titleRowStack.setCustomSpacing(6, after: titleLeadingIconView)
|
|
725
|
-
// As beside `titleIcon`: a plain title left at `.required` collapses the
|
|
726
|
-
// glyph's 999 width constraint instead of truncating.
|
|
727
|
-
titleLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
|
728
|
-
}
|
|
729
646
|
let titleSuffixes = item.data.dictionaries("titleSuffixes")
|
|
730
647
|
if !titleSuffixes.isEmpty {
|
|
731
|
-
|
|
732
|
-
let minimumTitleWidth =
|
|
648
|
+
titleLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
649
|
+
let minimumTitleWidth = titleLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: 80)
|
|
733
650
|
minimumTitleWidth.priority = UILayoutPriority(751)
|
|
734
651
|
minimumTitleWidth.isActive = true
|
|
735
652
|
selectorConstraints.append(minimumTitleWidth)
|
|
@@ -892,6 +809,13 @@ final class NativeListIdentityCell: NativeListRendererCell {
|
|
|
892
809
|
let width = trailingStack.widthAnchor.constraint(equalTo: accessory.widthAnchor)
|
|
893
810
|
width.isActive = true
|
|
894
811
|
selectorConstraints.append(width)
|
|
812
|
+
} else if item.data.string("presentation") == "accountSelector" {
|
|
813
|
+
// Keep flexible space in the text column when there is no accessory button.
|
|
814
|
+
// Stay below visible controls' compression resistance and the hidden checkbox's width.
|
|
815
|
+
let width = trailingStack.widthAnchor.constraint(equalToConstant: 0)
|
|
816
|
+
width.priority = UILayoutPriority(749)
|
|
817
|
+
width.isActive = true
|
|
818
|
+
selectorConstraints.append(width)
|
|
895
819
|
}
|
|
896
820
|
}
|
|
897
821
|
private func dataTextColor(_ tone: String, theme: [String: Any]?) -> UIColor {
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
import XCTest
|
|
3
|
+
@testable import NativeListModule
|
|
4
|
+
|
|
5
|
+
@MainActor
|
|
6
|
+
final class NativeListAccountSelectorLayoutTests: XCTestCase {
|
|
7
|
+
private func child<T>(_ cell: NativeListIdentityCell, _ label: String, as type: T.Type) throws -> T {
|
|
8
|
+
try XCTUnwrap(Mirror(reflecting: cell).children.first { $0.label == label }?.value as? T)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
private func bind(
|
|
12
|
+
_ cell: NativeListIdentityCell, title: String = "A", balance: String = "$16.30",
|
|
13
|
+
trailing: [[String: Any]] = []
|
|
14
|
+
) throws {
|
|
15
|
+
let item = try NativeListItem(data: [
|
|
16
|
+
"key": "account", "type": "identity", "presentation": "accountSelector",
|
|
17
|
+
"title": title, "height": 60,
|
|
18
|
+
"leading": ["kind": "account", "fallbackText": "A"],
|
|
19
|
+
"subtitleSegments": [
|
|
20
|
+
["text": balance, "tone": "secondary"],
|
|
21
|
+
["text": "0x1234...5678", "tone": "secondary", "separatorBefore": true],
|
|
22
|
+
],
|
|
23
|
+
"trailing": trailing,
|
|
24
|
+
])
|
|
25
|
+
cell.bind(item: item, theme: nil, layout: "linear", selected: false,
|
|
26
|
+
checkboxState: { _, _, fallback in fallback }, fonts: NativeListFontFamilies())
|
|
27
|
+
cell.setNeedsLayout()
|
|
28
|
+
cell.layoutIfNeeded()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private func assertSubtitleFits(
|
|
32
|
+
_ cell: NativeListIdentityCell, file: StaticString = #filePath, line: UInt = #line
|
|
33
|
+
) throws {
|
|
34
|
+
let labels = try child(cell, "semanticSubtitleLabels", as: [UILabel].self)
|
|
35
|
+
XCTAssertEqual(labels.count, 2, file: file, line: line)
|
|
36
|
+
for label in labels {
|
|
37
|
+
XCTAssertGreaterThanOrEqual(
|
|
38
|
+
label.bounds.width + 0.5, label.intrinsicContentSize.width,
|
|
39
|
+
"The balance and address must fit when the row has spare width", file: file, line: line)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
func testEmptyTrailingDoesNotCollapseBalanceToShortTitleWidth() throws {
|
|
44
|
+
for width: CGFloat in [320, 390] {
|
|
45
|
+
let cell = NativeListIdentityCell(frame: CGRect(x: 0, y: 0, width: width, height: 60))
|
|
46
|
+
let window = UIWindow(frame: CGRect(x: 0, y: 0, width: width, height: 200))
|
|
47
|
+
window.addSubview(cell)
|
|
48
|
+
for title in ["A", "返佣5", "Pendle account"] {
|
|
49
|
+
for balance in ["$16.30", "$0", "****"] {
|
|
50
|
+
try bind(cell, title: title, balance: balance)
|
|
51
|
+
window.layoutIfNeeded()
|
|
52
|
+
try assertSubtitleFits(cell)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
func testSpinnerPreservesItsIntrinsicWidthAndSubtitleSpace() throws {
|
|
59
|
+
let cell = NativeListIdentityCell(frame: CGRect(x: 0, y: 0, width: 320, height: 60))
|
|
60
|
+
let window = UIWindow(frame: CGRect(x: 0, y: 0, width: 320, height: 200))
|
|
61
|
+
window.addSubview(cell)
|
|
62
|
+
try bind(cell, trailing: [["kind": "spinner"]])
|
|
63
|
+
window.layoutIfNeeded()
|
|
64
|
+
|
|
65
|
+
try assertSubtitleFits(cell)
|
|
66
|
+
let trailing = try child(cell, "trailingStack", as: NativeListAccessoryStack.self)
|
|
67
|
+
let spinner = try XCTUnwrap(trailing.arrangedSubviews.compactMap { $0 as? UIActivityIndicatorView }.first)
|
|
68
|
+
XCTAssertTrue(spinner.isAnimating)
|
|
69
|
+
XCTAssertFalse(spinner.isHidden)
|
|
70
|
+
XCTAssertGreaterThanOrEqual(spinner.bounds.width + 0.5, spinner.intrinsicContentSize.width)
|
|
71
|
+
XCTAssertGreaterThanOrEqual(trailing.bounds.width + 0.5, spinner.bounds.width)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
func testRebindingBetweenIconEmptyAndSpinnerKeepsControlAndSubtitleWidths() throws {
|
|
75
|
+
let cell = NativeListIdentityCell(frame: CGRect(x: 0, y: 0, width: 320, height: 60))
|
|
76
|
+
let window = UIWindow(frame: CGRect(x: 0, y: 0, width: 320, height: 200))
|
|
77
|
+
window.addSubview(cell)
|
|
78
|
+
for icon in ["DotHorOutline", "PlusSmallOutline"] {
|
|
79
|
+
let descriptor: [String: Any] = ["kind": "icon", "name": icon, "actionKey": "action"]
|
|
80
|
+
let states: [[[String: Any]]] = [[descriptor], [], [["kind": "spinner"]], [descriptor], []]
|
|
81
|
+
for accessories in states {
|
|
82
|
+
try bind(cell, trailing: accessories)
|
|
83
|
+
window.layoutIfNeeded()
|
|
84
|
+
try assertSubtitleFits(cell)
|
|
85
|
+
let trailing = try child(cell, "trailingStack", as: NativeListAccessoryStack.self)
|
|
86
|
+
if accessories.first?["kind"] as? String == "icon" {
|
|
87
|
+
let control = try XCTUnwrap(trailing.firstVisibleControl)
|
|
88
|
+
XCTAssertGreaterThan(control.bounds.width, 0)
|
|
89
|
+
XCTAssertEqual(trailing.bounds.width, control.bounds.width, accuracy: 0.5)
|
|
90
|
+
} else {
|
|
91
|
+
XCTAssertNil(trailing.firstVisibleControl)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -78,36 +78,4 @@ final class NativeListIdentityTitleIconLayoutTests: XCTestCase {
|
|
|
78
78
|
XCTAssertGreaterThan(badgeFrame.minX, iconFrame.maxX)
|
|
79
79
|
XCTAssertLessThanOrEqual(badgeFrame.maxX, cell.bounds.width)
|
|
80
80
|
}
|
|
81
|
-
|
|
82
|
-
// An address book row: a plain title after the 16pt `titleLeadingIcon`. A
|
|
83
|
-
// long name truncates after the glyph instead of squeezing it to nothing.
|
|
84
|
-
func testLongPlainTitleTruncatesAfterTheLeadingIcon() throws {
|
|
85
|
-
let cell = NativeListIdentityCell(frame: CGRect(x: 0, y: 0, width: 320, height: 60))
|
|
86
|
-
let window = UIWindow(frame: CGRect(x: 0, y: 0, width: 320, height: 200))
|
|
87
|
-
window.addSubview(cell)
|
|
88
|
-
let leadingIcon = try child(cell, "titleLeadingIconView", as: UIImageView.self)
|
|
89
|
-
let titleLabel = try child(cell, "titleLabel", as: UILabel.self)
|
|
90
|
-
let item = try NativeListItem(data: [
|
|
91
|
-
"key": "book", "type": "identity",
|
|
92
|
-
"title": "A very long address book name that cannot possibly fit on one line",
|
|
93
|
-
"height": 60,
|
|
94
|
-
"leading": ["kind": "token", "fallbackIcon": ["name": "CryptoCoinOutline"]],
|
|
95
|
-
"titleLeadingIcon": ["name": "BookOpenOutline"],
|
|
96
|
-
"subtitle": "0x1234...abcd",
|
|
97
|
-
])
|
|
98
|
-
cell.bind(item: item, theme: nil, layout: "linear", selected: false,
|
|
99
|
-
checkboxState: { _, _, fallback in fallback }, fonts: NativeListFontFamilies())
|
|
100
|
-
cell.layoutIfNeeded()
|
|
101
|
-
|
|
102
|
-
let iconFrame = leadingIcon.convert(leadingIcon.bounds, to: cell)
|
|
103
|
-
let labelFrame = titleLabel.convert(titleLabel.bounds, to: cell)
|
|
104
|
-
XCTAssertFalse(leadingIcon.isHidden)
|
|
105
|
-
XCTAssertEqual(leadingIcon.bounds.width, 16, accuracy: 0.5,
|
|
106
|
-
"the leading glyph keeps its 16pt width")
|
|
107
|
-
XCTAssertEqual(labelFrame.minX, iconFrame.maxX + 6, accuracy: 1,
|
|
108
|
-
"the title follows the glyph with the 6pt gap")
|
|
109
|
-
XCTAssertLessThan(titleLabel.bounds.width, titleLabel.intrinsicContentSize.width,
|
|
110
|
-
"the title truncates instead of the glyph")
|
|
111
|
-
XCTAssertLessThanOrEqual(labelFrame.maxX, cell.bounds.width)
|
|
112
|
-
}
|
|
113
81
|
}
|
package/lib/module/models.js
CHANGED
|
@@ -39,8 +39,6 @@ export const ROW_BOX_STYLE_KEYS_BY_TYPE = {
|
|
|
39
39
|
|
|
40
40
|
// OneKey patch: preserve compact very-small-balance digit runs.
|
|
41
41
|
|
|
42
|
-
/** One run of an identity title line; see docs/SPEC.md. */
|
|
43
|
-
|
|
44
42
|
/** Native Market quote row. All values are already localized/formatted strings. */
|
|
45
43
|
|
|
46
44
|
/** Compact KPI card for dashboards and grids. */
|