@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.
- 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 +8 -84
- package/ios/NativeListIdentityRenderer.swift +6 -86
- package/lib/module/models.js +0 -2
- package/lib/module/validation.js +5 -71
- 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 +4 -121
- 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
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
package com.margelo.nitro.nativelist
|
|
2
|
-
|
|
3
|
-
import android.graphics.Typeface
|
|
4
|
-
import android.text.SpannableStringBuilder
|
|
5
|
-
import android.text.Spanned
|
|
6
|
-
import android.text.TextPaint
|
|
7
|
-
import android.text.TextUtils
|
|
8
|
-
import android.text.style.ForegroundColorSpan
|
|
9
|
-
import android.text.style.MetricAffectingSpan
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Identity title runs on one line. The text is rebuilt for each measured width
|
|
13
|
-
* from the widths `nativeListTitleSegmentWidths` allocates, so a run gives up
|
|
14
|
-
* width without the others; accessibility reads the row's `title` instead.
|
|
15
|
-
*/
|
|
16
|
-
internal class NativeListTitleSegmentsView(context: android.content.Context) :
|
|
17
|
-
NativeListTextView(context) {
|
|
18
|
-
class Run(
|
|
19
|
-
val text: String,
|
|
20
|
-
// Null keeps the view's typeface, which follows the title's.
|
|
21
|
-
val typeface: Typeface?,
|
|
22
|
-
val color: Int,
|
|
23
|
-
val shrink: Boolean,
|
|
24
|
-
val maxWidthRatio: Float?,
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
private var runs: List<Run> = emptyList()
|
|
28
|
-
private var builtForWidth = -1
|
|
29
|
-
|
|
30
|
-
fun setRuns(runs: List<Run>, spokenText: String) {
|
|
31
|
-
this.runs = runs
|
|
32
|
-
builtForWidth = -1
|
|
33
|
-
contentDescription = spokenText.takeIf(String::isNotEmpty)
|
|
34
|
-
text = runs.joinToString("") { it.text }
|
|
35
|
-
requestLayout()
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
fun clearRuns() {
|
|
39
|
-
runs = emptyList()
|
|
40
|
-
builtForWidth = -1
|
|
41
|
-
contentDescription = null
|
|
42
|
-
text = ""
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
46
|
-
if (runs.isEmpty()) {
|
|
47
|
-
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
|
48
|
-
return
|
|
49
|
-
}
|
|
50
|
-
var outer =
|
|
51
|
-
if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.UNSPECIFIED) Int.MAX_VALUE
|
|
52
|
-
else MeasureSpec.getSize(widthMeasureSpec)
|
|
53
|
-
if (maxWidth in 0 until outer) outer = maxWidth
|
|
54
|
-
val available =
|
|
55
|
-
if (outer == Int.MAX_VALUE) outer
|
|
56
|
-
else (outer - compoundPaddingLeft - compoundPaddingRight).coerceAtLeast(0)
|
|
57
|
-
if (available != builtForWidth) {
|
|
58
|
-
builtForWidth = available
|
|
59
|
-
// The same width rebuilds the same text, so the relayout this asks for
|
|
60
|
-
// measures once more and stops.
|
|
61
|
-
setText(build(available), BufferType.SPANNABLE)
|
|
62
|
-
}
|
|
63
|
-
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
|
64
|
-
// The runs fill what the title line leaves, so a ratio is a share of it.
|
|
65
|
-
if (outer != Int.MAX_VALUE) setMeasuredDimension(outer, measuredHeight)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
private fun build(available: Int): CharSequence {
|
|
69
|
-
val paints = runs.map { run -> TextPaint(paint).apply { run.typeface?.let { typeface = it } } }
|
|
70
|
-
val natural = runs.mapIndexed { index, run -> paints[index].measureText(run.text) }
|
|
71
|
-
val widths =
|
|
72
|
-
nativeListTitleSegmentWidths(
|
|
73
|
-
natural,
|
|
74
|
-
runs.map { it.shrink },
|
|
75
|
-
runs.map { it.maxWidthRatio },
|
|
76
|
-
if (available == Int.MAX_VALUE) Float.MAX_VALUE else available.toFloat(),
|
|
77
|
-
)
|
|
78
|
-
val built = SpannableStringBuilder()
|
|
79
|
-
runs.forEachIndexed { index, run ->
|
|
80
|
-
val width = widths[index]
|
|
81
|
-
if (width <= 0f) return@forEachIndexed
|
|
82
|
-
val shown =
|
|
83
|
-
if (width >= natural[index]) run.text
|
|
84
|
-
else TextUtils.ellipsize(run.text, paints[index], width, TextUtils.TruncateAt.END)
|
|
85
|
-
if (shown.isEmpty()) return@forEachIndexed
|
|
86
|
-
val start = built.length
|
|
87
|
-
built.append(shown)
|
|
88
|
-
built.setSpan(ForegroundColorSpan(run.color), start, built.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
89
|
-
run.typeface?.let {
|
|
90
|
-
built.setSpan(TypefaceRunSpan(it), start, built.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return built
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
private class TypefaceRunSpan(private val typeface: Typeface) : MetricAffectingSpan() {
|
|
98
|
-
override fun updateMeasureState(paint: TextPaint) {
|
|
99
|
-
paint.typeface = typeface
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
override fun updateDrawState(paint: TextPaint) {
|
|
103
|
-
paint.typeface = typeface
|
|
104
|
-
}
|
|
105
|
-
}
|
package/android/src/test/java/com/margelo/nitro/nativelist/NativeListTitleSegmentLayoutTest.kt
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
package com.margelo.nitro.nativelist
|
|
2
|
-
|
|
3
|
-
import org.junit.Assert.assertEquals
|
|
4
|
-
import org.junit.Test
|
|
5
|
-
|
|
6
|
-
class NativeListTitleSegmentLayoutTest {
|
|
7
|
-
// Wallet / Account as recipient rows send it: the wallet shrinks, the
|
|
8
|
-
// account is capped at 70% of the line.
|
|
9
|
-
private fun walletFirst(wallet: Float, account: Float, line: Float) =
|
|
10
|
-
nativeListTitleSegmentWidths(
|
|
11
|
-
listOf(wallet, 20f, account),
|
|
12
|
-
listOf(true, false, false),
|
|
13
|
-
listOf(null, null, 0.7f),
|
|
14
|
-
line,
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
private fun assertWidths(expected: List<Float>, actual: List<Float>) {
|
|
18
|
-
assertEquals(expected.size, actual.size)
|
|
19
|
-
expected.zip(actual).forEach { (want, got) -> assertEquals(want, got, 0.001f) }
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
@Test
|
|
23
|
-
fun keepsNaturalWidthsWhenTheyFit() = assertWidths(listOf(80f, 20f, 90f), walletFirst(80f, 90f, 300f))
|
|
24
|
-
|
|
25
|
-
@Test
|
|
26
|
-
fun theShrinkingRunGivesWayFirst() = assertWidths(listOf(130f, 20f, 100f), walletFirst(200f, 100f, 250f))
|
|
27
|
-
|
|
28
|
-
@Test
|
|
29
|
-
fun aCappedRunStopsAtItsShareOfTheLine() = assertWidths(listOf(70f, 20f, 210f), walletFirst(100f, 400f, 300f))
|
|
30
|
-
|
|
31
|
-
@Test
|
|
32
|
-
fun theAccountShrinksWhenTheWalletMustStayWhole() =
|
|
33
|
-
assertWidths(
|
|
34
|
-
listOf(210f, 20f, 70f),
|
|
35
|
-
nativeListTitleSegmentWidths(
|
|
36
|
-
listOf(300f, 20f, 150f),
|
|
37
|
-
listOf(false, false, true),
|
|
38
|
-
listOf(0.7f, null, null),
|
|
39
|
-
300f,
|
|
40
|
-
),
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
@Test
|
|
44
|
-
fun runsThatDoNotShrinkAreCutFromTheEnd() {
|
|
45
|
-
assertWidths(
|
|
46
|
-
listOf(100f, 50f, 0f),
|
|
47
|
-
nativeListTitleSegmentWidths(
|
|
48
|
-
listOf(100f, 100f, 100f),
|
|
49
|
-
listOf(false, false, true),
|
|
50
|
-
listOf(null, null, null),
|
|
51
|
-
150f,
|
|
52
|
-
),
|
|
53
|
-
)
|
|
54
|
-
assertWidths(
|
|
55
|
-
listOf(100f, 20f),
|
|
56
|
-
nativeListTitleSegmentWidths(listOf(100f, 100f), listOf(false, false), listOf(null, null), 120f),
|
|
57
|
-
)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
@Test
|
|
61
|
-
fun aNonPositiveLineLeavesNothing() {
|
|
62
|
-
assertWidths(listOf(0f, 0f, 0f), walletFirst(80f, 90f, 0f))
|
|
63
|
-
assertWidths(listOf(0f, 0f, 0f), walletFirst(80f, 90f, -10f))
|
|
64
|
-
}
|
|
65
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import Foundation
|
|
2
|
-
|
|
3
|
-
/// Widths for identity title runs (docs/SPEC.md, "Identity title segments
|
|
4
|
-
/// and leading title icon"). A run's base width is its natural width under
|
|
5
|
-
/// its cap; the one shrinking run takes what the others leave; runs that do
|
|
6
|
-
/// not shrink and still overflow are cut from the end of the line.
|
|
7
|
-
func nativeListTitleSegmentWidths(
|
|
8
|
-
natural: [CGFloat],
|
|
9
|
-
shrink: [Bool],
|
|
10
|
-
maxWidthRatio: [CGFloat?],
|
|
11
|
-
available: CGFloat
|
|
12
|
-
) -> [CGFloat] {
|
|
13
|
-
let line = max(0, available)
|
|
14
|
-
var widths = natural.indices.map { index -> CGFloat in
|
|
15
|
-
let width = max(0, natural[index])
|
|
16
|
-
guard index < maxWidthRatio.count, let ratio = maxWidthRatio[index] else { return width }
|
|
17
|
-
return min(width, ratio * line)
|
|
18
|
-
}
|
|
19
|
-
if let shrinking = shrink.firstIndex(of: true), shrinking < widths.count {
|
|
20
|
-
let others = widths.indices.reduce(CGFloat(0)) { $1 == shrinking ? $0 : $0 + widths[$1] }
|
|
21
|
-
widths[shrinking] = max(0, min(widths[shrinking], line - others))
|
|
22
|
-
}
|
|
23
|
-
var overflow = widths.reduce(0, +) - line
|
|
24
|
-
var index = widths.count - 1
|
|
25
|
-
while overflow > 0, index >= 0 {
|
|
26
|
-
let cut = min(widths[index], overflow)
|
|
27
|
-
widths[index] -= cut
|
|
28
|
-
overflow -= cut
|
|
29
|
-
index -= 1
|
|
30
|
-
}
|
|
31
|
-
return widths
|
|
32
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import UIKit
|
|
2
|
-
|
|
3
|
-
/// Draws identity title runs on one line, each at the width that
|
|
4
|
-
/// `nativeListTitleSegmentWidths` allocates for the current bounds. Its
|
|
5
|
-
/// intrinsic width is the runs' natural width, so the title row stack can
|
|
6
|
-
/// give it less, and accessibility reads the row's `title` instead of runs.
|
|
7
|
-
final class NativeListTitleSegmentsLabel: UIView {
|
|
8
|
-
struct Run {
|
|
9
|
-
let text: String
|
|
10
|
-
let font: UIFont
|
|
11
|
-
let color: UIColor
|
|
12
|
-
let shrink: Bool
|
|
13
|
-
let maxWidthRatio: CGFloat?
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
private var runs: [Run] = []
|
|
17
|
-
private var naturalWidths: [CGFloat] = []
|
|
18
|
-
var lineHeight: CGFloat = 24 {
|
|
19
|
-
didSet {
|
|
20
|
-
invalidateIntrinsicContentSize()
|
|
21
|
-
setNeedsDisplay()
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
override init(frame: CGRect) {
|
|
26
|
-
super.init(frame: frame)
|
|
27
|
-
isOpaque = false
|
|
28
|
-
backgroundColor = .clear
|
|
29
|
-
contentMode = .redraw
|
|
30
|
-
isAccessibilityElement = true
|
|
31
|
-
accessibilityTraits = .staticText
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
|
35
|
-
|
|
36
|
-
func configure(_ runs: [Run], spokenText: String) {
|
|
37
|
-
self.runs = runs
|
|
38
|
-
naturalWidths = runs.map {
|
|
39
|
-
ceil(($0.text as NSString).size(withAttributes: [.font: $0.font]).width)
|
|
40
|
-
}
|
|
41
|
-
accessibilityLabel = spokenText.isEmpty ? nil : spokenText
|
|
42
|
-
invalidateIntrinsicContentSize()
|
|
43
|
-
setNeedsDisplay()
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
func reset() {
|
|
47
|
-
configure([], spokenText: "")
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
override var intrinsicContentSize: CGSize {
|
|
51
|
-
CGSize(width: naturalWidths.reduce(0, +), height: lineHeight)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
override func layoutSubviews() {
|
|
55
|
-
super.layoutSubviews()
|
|
56
|
-
setNeedsDisplay()
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
override func draw(_ rect: CGRect) {
|
|
60
|
-
guard !runs.isEmpty else { return }
|
|
61
|
-
let widths = nativeListTitleSegmentWidths(
|
|
62
|
-
natural: naturalWidths, shrink: runs.map(\.shrink),
|
|
63
|
-
maxWidthRatio: runs.map(\.maxWidthRatio), available: bounds.width)
|
|
64
|
-
let isRightToLeft = effectiveUserInterfaceLayoutDirection == .rightToLeft
|
|
65
|
-
var x: CGFloat = isRightToLeft ? bounds.width : 0
|
|
66
|
-
for (run, width) in zip(runs, widths) where width > 0 {
|
|
67
|
-
let paragraph = NSMutableParagraphStyle()
|
|
68
|
-
paragraph.lineBreakMode = .byTruncatingTail
|
|
69
|
-
let text = NSAttributedString(
|
|
70
|
-
string: run.text,
|
|
71
|
-
attributes: [.font: run.font, .foregroundColor: run.color, .paragraphStyle: paragraph])
|
|
72
|
-
let height = ceil(run.font.lineHeight)
|
|
73
|
-
let originX = isRightToLeft ? x - width : x
|
|
74
|
-
text.draw(
|
|
75
|
-
with: CGRect(x: originX, y: (bounds.height - height) / 2, width: width, height: height),
|
|
76
|
-
options: [.usesLineFragmentOrigin, .truncatesLastVisibleLine], context: nil)
|
|
77
|
-
x += isRightToLeft ? -width : width
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|