@onekeyfe/react-native-native-list 3.0.157-alpha.249 → 3.0.157-alpha.251
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -5
- package/android/src/main/java/com/onekey/nativelist/NativeListAccessoryStack.kt +711 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListActionRenderer.kt +169 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListActivityRenderer.kt +243 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListAdapter.kt +20 -6
- package/android/src/main/java/com/onekey/nativelist/NativeListDataRowRenderer.kt +487 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListIdentityRenderer.kt +820 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListImageSlot.kt +147 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListLeadingVisual.kt +453 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListMarketRenderer.kt +997 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListMediaTileRenderer.kt +234 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListMessageRenderer.kt +232 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListMetricCardRenderer.kt +551 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListModels.kt +24 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListPrimitives.kt +475 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListRailRenderer.kt +143 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListRendererRegistry.kt +42 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListRendererRowView.kt +415 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListResolvedText.kt +163 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListRowHost.kt +70 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListSectionHeaderRenderer.kt +748 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListSystemRenderer.kt +464 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListTextView.kt +25 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListView.kt +174 -104
- package/android/src/main/java/com/onekey/nativelist/NativeListWalletGroupRenderer.kt +407 -0
- package/android/src/test/java/com/margelo/nitro/nativelist/NativeListSelectorBackgroundColorTest.kt +34 -0
- package/docs/DESIGN.md +518 -9
- package/docs/MIGRATION_BASELINE.md +117 -0
- package/docs/ROW_TEMPLATES.md +359 -0
- package/docs/SPEC.md +358 -0
- package/docs/STYLE_SPEC.md +978 -0
- package/docs/images/list-capabilities.svg +39 -0
- package/docs/images/row-action.svg +17 -0
- package/docs/images/row-activity.svg +24 -0
- package/docs/images/row-data.svg +24 -0
- package/docs/images/row-identity.svg +25 -0
- package/docs/images/row-market.svg +23 -0
- package/docs/images/row-media-tile.svg +26 -0
- package/docs/images/row-message.svg +20 -0
- package/docs/images/row-metric-card.svg +33 -0
- package/docs/images/row-rail.svg +18 -0
- package/docs/images/row-section-header.svg +19 -0
- package/docs/images/row-style-regions.svg +30 -0
- package/docs/images/row-system.svg +30 -0
- package/docs/images/row-wallet-group.svg +26 -0
- package/ios/NativeListAccessoryStack.swift +680 -0
- package/ios/NativeListActionRenderer.swift +108 -0
- package/ios/NativeListActivityRenderer.swift +179 -0
- package/ios/NativeListDataRowRenderer.swift +444 -0
- package/ios/NativeListIdentityRenderer.swift +685 -0
- package/ios/NativeListImageSlot.swift +140 -0
- package/ios/NativeListLeadingVisual.swift +372 -0
- package/ios/NativeListMarketRenderer.swift +698 -0
- package/ios/NativeListMediaTileRenderer.swift +184 -0
- package/ios/NativeListMessageRenderer.swift +168 -0
- package/ios/NativeListMetricCardRenderer.swift +420 -0
- package/ios/NativeListModels.swift +32 -0
- package/ios/NativeListRailRenderer.swift +152 -0
- package/ios/NativeListRendererCell.swift +311 -0
- package/ios/NativeListRendererRegistry.swift +50 -0
- package/ios/NativeListResolvedText.swift +122 -0
- package/ios/NativeListRowHost.swift +63 -0
- package/ios/NativeListSectionHeaderRenderer.swift +708 -0
- package/ios/NativeListSystemRenderer.swift +568 -0
- package/ios/NativeListTextLabel.swift +42 -0
- package/ios/NativeListTextStyles.swift +236 -0
- package/ios/NativeListWalletGroupRenderer.swift +255 -0
- package/ios/RNCNativeListView.swift +111 -180
- package/lib/module/models.js +54 -0
- package/lib/module/validation.js +324 -26
- package/lib/module/web/NativeListWebEngine.js +148 -936
- package/lib/module/web/templates/ActionRowRenderer.js +97 -0
- package/lib/module/web/templates/ActivityRowRenderer.js +95 -0
- package/lib/module/web/templates/DataRowRenderer.js +145 -0
- package/lib/module/web/templates/IdentityRowRenderer.js +289 -0
- package/lib/module/web/templates/MarketRowRenderer.js +310 -0
- package/lib/module/web/templates/MediaTileRowRenderer.js +132 -0
- package/lib/module/web/templates/MessageRowRenderer.js +165 -0
- package/lib/module/web/templates/MetricCardRowRenderer.js +172 -0
- package/lib/module/web/templates/RailRowRenderer.js +98 -0
- package/lib/module/web/templates/RowContainerStyle.js +100 -0
- package/lib/module/web/templates/RowElements.js +93 -0
- package/lib/module/web/templates/RowRendererRegistry.js +70 -0
- package/lib/module/web/templates/RowText.js +121 -0
- package/lib/module/web/templates/RowVisual.js +43 -0
- package/lib/module/web/templates/SectionHeaderRowRenderer.js +146 -0
- package/lib/module/web/templates/SystemRowRenderer.js +161 -0
- package/lib/module/web/templates/WalletGroupRowRenderer.js +96 -0
- package/lib/typescript/src/models.d.ts +166 -13
- package/lib/typescript/src/web/NativeListWebEngine.d.ts +18 -16
- package/lib/typescript/src/web/templates/ActionRowRenderer.d.ts +15 -0
- package/lib/typescript/src/web/templates/ActivityRowRenderer.d.ts +15 -0
- package/lib/typescript/src/web/templates/DataRowRenderer.d.ts +15 -0
- package/lib/typescript/src/web/templates/IdentityRowRenderer.d.ts +16 -0
- package/lib/typescript/src/web/templates/MarketRowRenderer.d.ts +30 -0
- package/lib/typescript/src/web/templates/MediaTileRowRenderer.d.ts +15 -0
- package/lib/typescript/src/web/templates/MessageRowRenderer.d.ts +44 -0
- package/lib/typescript/src/web/templates/MetricCardRowRenderer.d.ts +15 -0
- package/lib/typescript/src/web/templates/RailRowRenderer.d.ts +28 -0
- package/lib/typescript/src/web/templates/RowContainerStyle.d.ts +9 -0
- package/lib/typescript/src/web/templates/RowElements.d.ts +38 -0
- package/lib/typescript/src/web/templates/RowRendererRegistry.d.ts +83 -0
- package/lib/typescript/src/web/templates/RowText.d.ts +9 -0
- package/lib/typescript/src/web/templates/RowVisual.d.ts +20 -0
- package/lib/typescript/src/web/templates/SectionHeaderRowRenderer.d.ts +15 -0
- package/lib/typescript/src/web/templates/SystemRowRenderer.d.ts +16 -0
- package/lib/typescript/src/web/templates/WalletGroupRowRenderer.d.ts +20 -0
- package/package.json +3 -3
- package/src/models.ts +281 -19
- package/src/validation.ts +501 -46
- package/src/web/NativeListWebEngine.ts +361 -1932
- package/src/web/templates/ActionRowRenderer.ts +123 -0
- package/src/web/templates/ActivityRowRenderer.ts +159 -0
- package/src/web/templates/DataRowRenderer.ts +212 -0
- package/src/web/templates/IdentityRowRenderer.ts +527 -0
- package/src/web/templates/MarketRowRenderer.ts +467 -0
- package/src/web/templates/MediaTileRowRenderer.ts +182 -0
- package/src/web/templates/MessageRowRenderer.ts +306 -0
- package/src/web/templates/MetricCardRowRenderer.ts +337 -0
- package/src/web/templates/RailRowRenderer.ts +136 -0
- package/src/web/templates/RowContainerStyle.ts +131 -0
- package/src/web/templates/RowElements.ts +160 -0
- package/src/web/templates/RowRendererRegistry.ts +111 -0
- package/src/web/templates/RowText.ts +181 -0
- package/src/web/templates/RowVisual.ts +115 -0
- package/src/web/templates/SectionHeaderRowRenderer.ts +243 -0
- package/src/web/templates/SystemRowRenderer.ts +282 -0
- package/src/web/templates/WalletGroupRowRenderer.ts +131 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListRowView.kt +0 -4538
- package/ios/NativeListCell.swift +0 -4300
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
final class NativeListDataRowCell: NativeListRendererCell {
|
|
4
|
+
override class func measure(
|
|
5
|
+
_ item: NativeListItem, width: CGFloat, theme: [String: Any]?, layout: String
|
|
6
|
+
) -> CGFloat? {
|
|
7
|
+
let secondary = item.data.dictionaries("columns").contains {
|
|
8
|
+
!$0.string("secondaryText").isEmpty
|
|
9
|
+
}
|
|
10
|
+
return secondary ? 60 : layout == "table" ? 48 : 56
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
private let visual = NativeListLeadingVisual()
|
|
14
|
+
private let favorite = UIImageView()
|
|
15
|
+
private let accessories = NativeListAccessoryStack()
|
|
16
|
+
private let columns = UIStackView()
|
|
17
|
+
private let cells = (0..<4).map { _ in NativeListTableColumnView() }
|
|
18
|
+
private var rowConstraints: [NSLayoutConstraint] = []
|
|
19
|
+
override func unselectedBackground(
|
|
20
|
+
_ item: NativeListItem, theme: [String: Any]?, layout: String, itemIndex: Int?
|
|
21
|
+
) -> UIColor {
|
|
22
|
+
let index = item.data["index"] == nil ? itemIndex : item.data.int("index")
|
|
23
|
+
return layout == "table" && index.map({ $0 % 2 == 0 }) == true
|
|
24
|
+
? nativeListColor(theme, "subduedBackground", "#F9F9F9")
|
|
25
|
+
: super.unselectedBackground(item, theme: theme, layout: layout, itemIndex: itemIndex)
|
|
26
|
+
}
|
|
27
|
+
override var assetFields: [String] { ["leading"] }
|
|
28
|
+
override init(frame: CGRect) {
|
|
29
|
+
super.init(frame: frame)
|
|
30
|
+
root.axis = .horizontal
|
|
31
|
+
favorite.contentMode = .scaleAspectFit
|
|
32
|
+
columns.axis = .horizontal
|
|
33
|
+
columns.alignment = .center
|
|
34
|
+
columns.spacing = 8
|
|
35
|
+
cells.forEach(columns.addArrangedSubview)
|
|
36
|
+
[favorite, visual, accessories, columns].forEach(root.addArrangedSubview)
|
|
37
|
+
accessories.onAction = { [weak self] key, view, slot, target in
|
|
38
|
+
self?.emitAction(key, from: view, source: "trailingAccessory", slot: slot, target: target)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
|
42
|
+
override func bindContent(
|
|
43
|
+
_ item: NativeListItem, theme: [String: Any]?, layout: String,
|
|
44
|
+
checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String
|
|
45
|
+
) {
|
|
46
|
+
let data = item.data
|
|
47
|
+
let style = data.dictionary("style") ?? [:]
|
|
48
|
+
let image = style.dictionary("image") ?? [:]
|
|
49
|
+
root.spacing = layout == "table" ? 10 : 8
|
|
50
|
+
root.alignment =
|
|
51
|
+
style.dictionary("container")?.string("contentVerticalAlignment") == "top"
|
|
52
|
+
? .top
|
|
53
|
+
: style.dictionary("container")?.string("contentVerticalAlignment") == "bottom"
|
|
54
|
+
? .bottom : .center
|
|
55
|
+
let hp = CGFloat(style.double("horizontalPadding", default: layout == "table" ? 20 : 12))
|
|
56
|
+
let vp = CGFloat(style.double("verticalPadding", default: layout == "table" ? 10 : 8))
|
|
57
|
+
contentInsets = UIEdgeInsets(top: vp, left: hp, bottom: vp, right: hp)
|
|
58
|
+
favorite.isHidden = !data.bool("favorite") && !data.bool("favoriteActive")
|
|
59
|
+
favorite.image = nativeListIcon(
|
|
60
|
+
named: data.bool("favoriteActive") ? "StarSolid" : "StarOutline")
|
|
61
|
+
favorite.tintColor = nativeListColor(
|
|
62
|
+
theme, data.bool("favoriteActive") ? "icon" : "iconSubdued",
|
|
63
|
+
data.bool("favoriteActive") ? "#646464" : "#8D8D8D")
|
|
64
|
+
root.setCustomSpacing(layout == "table" ? 8 : root.spacing, after: favorite)
|
|
65
|
+
visual.isHidden = data.dictionary("leading") == nil
|
|
66
|
+
if let leading = data.dictionary("leading") {
|
|
67
|
+
visual.bind(leading, style: image, key: item.key, theme: theme, isUnread: false)
|
|
68
|
+
} else {
|
|
69
|
+
visual.recycle()
|
|
70
|
+
}
|
|
71
|
+
root.setCustomSpacing(
|
|
72
|
+
CGFloat(style.double("leadingGap", default: Double(root.spacing))), after: visual)
|
|
73
|
+
var descriptors: [[String: Any]] = []
|
|
74
|
+
if data["index"] != nil {
|
|
75
|
+
descriptors.append(["kind": "value", "text": String(data.int("index"))])
|
|
76
|
+
}
|
|
77
|
+
if var checkbox = data.dictionary("checkbox") {
|
|
78
|
+
// Legacy bound the checkbox field directly; it does not need an explicit kind.
|
|
79
|
+
if checkbox["kind"] == nil { checkbox["kind"] = "checkbox" }
|
|
80
|
+
descriptors.append(checkbox)
|
|
81
|
+
}
|
|
82
|
+
var accessoryStyle = style
|
|
83
|
+
accessoryStyle["value"] = style.dictionary("index")
|
|
84
|
+
accessories.bind(
|
|
85
|
+
item, descriptors: descriptors, theme: theme, style: accessoryStyle,
|
|
86
|
+
checkboxState: checkboxState)
|
|
87
|
+
accessories.isHidden = descriptors.isEmpty
|
|
88
|
+
NSLayoutConstraint.deactivate(rowConstraints)
|
|
89
|
+
rowConstraints = [
|
|
90
|
+
favorite.widthAnchor.constraint(equalToConstant: 20),
|
|
91
|
+
favorite.heightAnchor.constraint(equalToConstant: 20),
|
|
92
|
+
visual.widthAnchor.constraint(equalToConstant: CGFloat(image.double("width", default: 40))),
|
|
93
|
+
visual.heightAnchor.constraint(equalToConstant: CGFloat(image.double("height", default: 40))),
|
|
94
|
+
]
|
|
95
|
+
let values = Array(data.dictionaries("columns").prefix(4))
|
|
96
|
+
for (index, cell) in cells.enumerated() {
|
|
97
|
+
cell.isHidden = index >= values.count
|
|
98
|
+
guard index < values.count else {
|
|
99
|
+
cell.reset()
|
|
100
|
+
continue
|
|
101
|
+
}
|
|
102
|
+
let badges = index == 0 ? data.dictionaries("badges") : []
|
|
103
|
+
if layout == "table" {
|
|
104
|
+
cell.bind(column: values[index], badges: badges, theme: theme)
|
|
105
|
+
} else {
|
|
106
|
+
cell.bindLinear(column: values[index], badges: badges, theme: theme, style: style)
|
|
107
|
+
}
|
|
108
|
+
cell.applyStyle(style, text: NativeListTextStyles.applyStyledText)
|
|
109
|
+
if index > 0 {
|
|
110
|
+
rowConstraints.append(
|
|
111
|
+
cell.widthAnchor.constraint(
|
|
112
|
+
equalTo: cells[0].widthAnchor,
|
|
113
|
+
multiplier: CGFloat(max(1, values[index].int("weight", default: 1)))
|
|
114
|
+
/ CGFloat(max(1, values[0].int("weight", default: 1)))))
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
NSLayoutConstraint.activate(rowConstraints)
|
|
118
|
+
}
|
|
119
|
+
override func bindSelectionContent(
|
|
120
|
+
_ item: NativeListItem,
|
|
121
|
+
checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String
|
|
122
|
+
) { accessories.updateSelection(item, checkboxState: checkboxState) }
|
|
123
|
+
override func recycleContent() {
|
|
124
|
+
visual.recycle()
|
|
125
|
+
accessories.reset()
|
|
126
|
+
cells.forEach { $0.reset() }
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private final class NativeListTableColumnView: UIStackView {
|
|
131
|
+
private let primaryLine = UIStackView()
|
|
132
|
+
private let primaryLabel = NativeListTextLabel()
|
|
133
|
+
private let badgesStack = UIStackView()
|
|
134
|
+
private let secondaryLine = UIStackView()
|
|
135
|
+
private let secondaryLeadingLabel = NativeListTextLabel()
|
|
136
|
+
private let secondaryLabel = NativeListTextLabel()
|
|
137
|
+
private var linear = false
|
|
138
|
+
|
|
139
|
+
override init(frame: CGRect) {
|
|
140
|
+
super.init(frame: frame)
|
|
141
|
+
axis = .vertical
|
|
142
|
+
alignment = .leading
|
|
143
|
+
distribution = .fill
|
|
144
|
+
spacing = 4
|
|
145
|
+
|
|
146
|
+
primaryLine.axis = .horizontal
|
|
147
|
+
primaryLine.alignment = .center
|
|
148
|
+
primaryLine.spacing = 6
|
|
149
|
+
primaryLabel.numberOfLines = 1
|
|
150
|
+
primaryLabel.lineBreakMode = .byTruncatingTail
|
|
151
|
+
primaryLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
152
|
+
badgesStack.axis = .horizontal
|
|
153
|
+
badgesStack.alignment = .center
|
|
154
|
+
badgesStack.spacing = 4
|
|
155
|
+
badgesStack.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
156
|
+
primaryLine.addArrangedSubview(primaryLabel)
|
|
157
|
+
primaryLine.addArrangedSubview(badgesStack)
|
|
158
|
+
|
|
159
|
+
secondaryLine.axis = .horizontal
|
|
160
|
+
secondaryLine.alignment = .center
|
|
161
|
+
secondaryLine.spacing = 4
|
|
162
|
+
for label in [secondaryLeadingLabel, secondaryLabel] {
|
|
163
|
+
label.numberOfLines = 1
|
|
164
|
+
label.lineBreakMode = .byTruncatingTail
|
|
165
|
+
secondaryLine.addArrangedSubview(label)
|
|
166
|
+
}
|
|
167
|
+
secondaryLeadingLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
168
|
+
secondaryLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
|
|
169
|
+
secondaryLeadingLabel.widthAnchor.constraint(lessThanOrEqualToConstant: 120).isActive = true
|
|
170
|
+
addArrangedSubview(primaryLine)
|
|
171
|
+
addArrangedSubview(secondaryLine)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
required init(coder: NSCoder) {
|
|
175
|
+
fatalError("init(coder:) has not been implemented")
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
func reset() {
|
|
179
|
+
linear = false
|
|
180
|
+
for label in [primaryLabel, secondaryLeadingLabel, secondaryLabel] {
|
|
181
|
+
label.rowVerticalAlignment = nil
|
|
182
|
+
label.rowOffsetY = 0
|
|
183
|
+
label.lineBreakMode = .byTruncatingTail
|
|
184
|
+
}
|
|
185
|
+
spacing = 4
|
|
186
|
+
primaryLine.spacing = 6
|
|
187
|
+
primaryLabel.numberOfLines = 1
|
|
188
|
+
secondaryLeadingLabel.numberOfLines = 1
|
|
189
|
+
secondaryLabel.numberOfLines = 1
|
|
190
|
+
primaryLabel.attributedText = nil
|
|
191
|
+
secondaryLeadingLabel.attributedText = nil
|
|
192
|
+
secondaryLabel.attributedText = nil
|
|
193
|
+
secondaryLeadingLabel.isHidden = true
|
|
194
|
+
secondaryLabel.isHidden = true
|
|
195
|
+
secondaryLine.isHidden = true
|
|
196
|
+
badgesStack.arrangedSubviews.forEach {
|
|
197
|
+
badgesStack.removeArrangedSubview($0)
|
|
198
|
+
$0.removeFromSuperview()
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
func bind(
|
|
203
|
+
column: [String: Any],
|
|
204
|
+
badges: [[String: Any]],
|
|
205
|
+
theme: [String: Any]?
|
|
206
|
+
) {
|
|
207
|
+
reset()
|
|
208
|
+
let textAlignment: NSTextAlignment
|
|
209
|
+
switch column.string("alignment") {
|
|
210
|
+
case "end":
|
|
211
|
+
alignment = .trailing
|
|
212
|
+
textAlignment = .right
|
|
213
|
+
case "center":
|
|
214
|
+
alignment = .center
|
|
215
|
+
textAlignment = .center
|
|
216
|
+
default:
|
|
217
|
+
alignment = .leading
|
|
218
|
+
textAlignment = .left
|
|
219
|
+
}
|
|
220
|
+
primaryLabel.textAlignment = textAlignment
|
|
221
|
+
secondaryLeadingLabel.textAlignment = textAlignment
|
|
222
|
+
secondaryLabel.textAlignment = textAlignment
|
|
223
|
+
primaryLabel.font = nativeListFont(ofSize: 14, weight: .medium)
|
|
224
|
+
secondaryLeadingLabel.font = nativeListFont(ofSize: 12)
|
|
225
|
+
secondaryLabel.font = nativeListFont(ofSize: 12)
|
|
226
|
+
primaryLabel.attributedText = line(
|
|
227
|
+
column.string("text"),
|
|
228
|
+
font: nativeListFont(ofSize: 14, weight: .medium),
|
|
229
|
+
color: textColor(column.string("tone"), theme: theme),
|
|
230
|
+
height: 20
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
for badge in badges.prefix(2) {
|
|
234
|
+
let label = NativeListInsetLabel()
|
|
235
|
+
label.horizontalInset = 6
|
|
236
|
+
label.text = badge.string("text")
|
|
237
|
+
label.font = nativeListFont(ofSize: 10)
|
|
238
|
+
label.textColor = nativeListColor(theme, "info", "#0D74CE")
|
|
239
|
+
label.textAlignment = .center
|
|
240
|
+
label.backgroundColor = UIColor(nativeListHex: "#008FF519", fallback: .systemBlue)
|
|
241
|
+
label.layer.cornerRadius = 4
|
|
242
|
+
label.clipsToBounds = true
|
|
243
|
+
label.translatesAutoresizingMaskIntoConstraints = false
|
|
244
|
+
label.heightAnchor.constraint(equalToConstant: 16).isActive = true
|
|
245
|
+
badgesStack.addArrangedSubview(label)
|
|
246
|
+
}
|
|
247
|
+
badgesStack.isHidden = badges.isEmpty
|
|
248
|
+
|
|
249
|
+
let secondaryLeading = column.string("secondaryLeadingText")
|
|
250
|
+
if !secondaryLeading.isEmpty {
|
|
251
|
+
secondaryLeadingLabel.attributedText = line(
|
|
252
|
+
secondaryLeading,
|
|
253
|
+
font: nativeListFont(ofSize: 12),
|
|
254
|
+
color: nativeListColor(theme, "secondaryText", "#646464"),
|
|
255
|
+
height: 16
|
|
256
|
+
)
|
|
257
|
+
secondaryLeadingLabel.isHidden = false
|
|
258
|
+
secondaryLine.isHidden = false
|
|
259
|
+
}
|
|
260
|
+
let secondary = column.string("secondaryText")
|
|
261
|
+
if !secondary.isEmpty {
|
|
262
|
+
secondaryLabel.attributedText = line(
|
|
263
|
+
secondary,
|
|
264
|
+
font: nativeListFont(ofSize: 12),
|
|
265
|
+
color: textColor(
|
|
266
|
+
column.string("secondaryTone", default: "secondary"),
|
|
267
|
+
theme: theme
|
|
268
|
+
),
|
|
269
|
+
height: 16
|
|
270
|
+
)
|
|
271
|
+
secondaryLabel.isHidden = false
|
|
272
|
+
secondaryLine.isHidden = false
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/// Legacy linear layout: one label per column holding the primary text, up to two inline
|
|
277
|
+
/// badge runs and the secondary text on a second line. `secondaryLeadingText` is not shown.
|
|
278
|
+
/// Unstyled output is exactly the legacy label. `style.columns` / `style.columnSecondary`
|
|
279
|
+
/// map onto their attributed ranges (font, weight, color) and paragraphs (lineHeight,
|
|
280
|
+
/// alignment); `lines`/`truncate` and vertical alignment/offset act on the single label.
|
|
281
|
+
func bindLinear(
|
|
282
|
+
column: [String: Any], badges: [[String: Any]], theme: [String: Any]?,
|
|
283
|
+
style: [String: Any] = [:]
|
|
284
|
+
) {
|
|
285
|
+
reset()
|
|
286
|
+
linear = true
|
|
287
|
+
alignment = .fill
|
|
288
|
+
badgesStack.isHidden = true
|
|
289
|
+
let primaryStyle = style.dictionary("columns") ?? [:]
|
|
290
|
+
let secondaryStyle = style.dictionary("columnSecondary") ?? [:]
|
|
291
|
+
func font(_ textStyle: [String: Any], size: CGFloat, weight: NativeListFontWeight) -> UIFont {
|
|
292
|
+
guard textStyle["fontSize"] != nil || textStyle["fontWeight"] != nil else {
|
|
293
|
+
return nativeListFont(ofSize: size, weight: weight)
|
|
294
|
+
}
|
|
295
|
+
return nativeListFont(
|
|
296
|
+
ofSize: CGFloat(textStyle.double("fontSize", default: Double(size))),
|
|
297
|
+
weight: NativeListTextStyles.marketFontWeight(
|
|
298
|
+
textStyle.string("fontWeight"), fallback: weight))
|
|
299
|
+
}
|
|
300
|
+
func color(_ textStyle: [String: Any], _ fallback: UIColor) -> UIColor {
|
|
301
|
+
(textStyle["color"] as? String).map { UIColor(nativeListHex: $0, fallback: fallback) }
|
|
302
|
+
?? fallback
|
|
303
|
+
}
|
|
304
|
+
let direction = primaryLabel.effectiveUserInterfaceLayoutDirection
|
|
305
|
+
/// Paragraph attributes only when the style asks for them, so unstyled text stays legacy.
|
|
306
|
+
func paragraph(_ textStyle: [String: Any], font: UIFont, spacing: CGFloat?)
|
|
307
|
+
-> [NSAttributedString.Key: Any]
|
|
308
|
+
{
|
|
309
|
+
guard textStyle["lineHeight"] != nil || textStyle["alignment"] != nil || spacing != nil
|
|
310
|
+
else { return [:] }
|
|
311
|
+
let paragraph = NSMutableParagraphStyle()
|
|
312
|
+
paragraph.alignment = NativeListTextStyles.marketTextAlignment(
|
|
313
|
+
textStyle.string("alignment"), direction: direction)
|
|
314
|
+
if textStyle["alignment"] == nil { paragraph.alignment = primaryLabel.textAlignment }
|
|
315
|
+
var attributes: [NSAttributedString.Key: Any] = [.paragraphStyle: paragraph]
|
|
316
|
+
if textStyle["lineHeight"] != nil {
|
|
317
|
+
let box = CGFloat(textStyle.double("lineHeight"))
|
|
318
|
+
paragraph.minimumLineHeight = box
|
|
319
|
+
paragraph.maximumLineHeight = box
|
|
320
|
+
attributes[.baselineOffset] = max(0, (box - font.lineHeight) / 2)
|
|
321
|
+
}
|
|
322
|
+
if let spacing { paragraph.paragraphSpacing = spacing }
|
|
323
|
+
return attributes
|
|
324
|
+
}
|
|
325
|
+
// Legacy physical alignment from the column data; set before the attributed text so that
|
|
326
|
+
// unstyled paragraphs inherit it.
|
|
327
|
+
primaryLabel.textAlignment =
|
|
328
|
+
column.string("alignment") == "end"
|
|
329
|
+
? .right : column.string("alignment") == "center" ? .center : .left
|
|
330
|
+
let secondary = column.string("secondaryText")
|
|
331
|
+
let primaryFont = font(primaryStyle, size: 14, weight: .medium)
|
|
332
|
+
let attributed = NSMutableAttributedString(
|
|
333
|
+
string: column.string("text"),
|
|
334
|
+
attributes: [
|
|
335
|
+
.font: primaryFont,
|
|
336
|
+
.foregroundColor: color(primaryStyle, textColor(column.string("tone"), theme: theme)),
|
|
337
|
+
])
|
|
338
|
+
let primaryLength = attributed.length
|
|
339
|
+
// Every badge run is the legacy " text " pill: both lead spaces sit inside the badge
|
|
340
|
+
// background and are its left padding. `titleBadgeGap` adds an exact, unfilled gap between
|
|
341
|
+
// the title and the first pill as kern on the title's last composed character; unstyled
|
|
342
|
+
// rows add no kern and stay byte-identical to legacy.
|
|
343
|
+
if style["titleBadgeGap"] != nil, !badges.isEmpty, primaryLength > 0 {
|
|
344
|
+
attributed.addAttribute(
|
|
345
|
+
.kern, value: CGFloat(style.double("titleBadgeGap")),
|
|
346
|
+
range: (attributed.string as NSString).rangeOfComposedCharacterSequence(
|
|
347
|
+
at: primaryLength - 1))
|
|
348
|
+
}
|
|
349
|
+
for badge in badges.prefix(2) {
|
|
350
|
+
attributed.append(
|
|
351
|
+
NSAttributedString(
|
|
352
|
+
string: " \(badge.string("text")) ",
|
|
353
|
+
attributes: [
|
|
354
|
+
.font: nativeListFont(ofSize: 12, weight: .medium),
|
|
355
|
+
.foregroundColor: nativeListColor(theme, "info", "#0D74CE"),
|
|
356
|
+
.backgroundColor: UIColor(nativeListHex: "#008FF519", fallback: .systemBlue),
|
|
357
|
+
]))
|
|
358
|
+
}
|
|
359
|
+
let primaryParagraph = paragraph(
|
|
360
|
+
primaryStyle, font: primaryFont,
|
|
361
|
+
spacing: !secondary.isEmpty && style["lineGap"] != nil
|
|
362
|
+
? CGFloat(style.double("lineGap")) : nil)
|
|
363
|
+
if !primaryParagraph.isEmpty {
|
|
364
|
+
attributed.addAttributes(
|
|
365
|
+
primaryParagraph, range: NSRange(location: 0, length: attributed.length))
|
|
366
|
+
}
|
|
367
|
+
var lines = min(3, max(1, primaryStyle.int("lines", default: 1)))
|
|
368
|
+
if !secondary.isEmpty {
|
|
369
|
+
let secondaryFont = font(secondaryStyle, size: 12, weight: .regular)
|
|
370
|
+
var attributes: [NSAttributedString.Key: Any] = [
|
|
371
|
+
.font: secondaryFont,
|
|
372
|
+
.foregroundColor: color(
|
|
373
|
+
secondaryStyle,
|
|
374
|
+
textColor(column.string("secondaryTone", default: "secondary"), theme: theme)),
|
|
375
|
+
]
|
|
376
|
+
attributes.merge(paragraph(secondaryStyle, font: secondaryFont, spacing: nil)) { $1 }
|
|
377
|
+
attributed.append(NSAttributedString(string: "\n\(secondary)", attributes: attributes))
|
|
378
|
+
lines = min(3, lines + max(1, secondaryStyle.int("lines", default: 1)))
|
|
379
|
+
}
|
|
380
|
+
primaryLabel.font = nativeListFont(ofSize: 12)
|
|
381
|
+
primaryLabel.numberOfLines = lines
|
|
382
|
+
primaryLabel.attributedText = attributed
|
|
383
|
+
let truncation =
|
|
384
|
+
primaryStyle["truncate"] != nil || primaryStyle["lines"] != nil
|
|
385
|
+
? primaryStyle
|
|
386
|
+
: secondaryStyle["truncate"] != nil || secondaryStyle["lines"] != nil ? secondaryStyle : nil
|
|
387
|
+
primaryLabel.lineBreakMode =
|
|
388
|
+
truncation.map { NativeListTextStyles.styledLineBreakMode($0, lines: lines) }
|
|
389
|
+
?? .byTruncatingTail
|
|
390
|
+
if let vertical = (primaryStyle["verticalAlignment"] ?? secondaryStyle["verticalAlignment"])
|
|
391
|
+
as? String
|
|
392
|
+
{
|
|
393
|
+
primaryLabel.rowVerticalAlignment = vertical
|
|
394
|
+
}
|
|
395
|
+
primaryLabel.rowOffsetY = CGFloat(
|
|
396
|
+
primaryStyle.double("offsetY", default: secondaryStyle.double("offsetY")))
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
func applyStyle(_ style: [String: Any], text: (UILabel, [String: Any]) -> Void) {
|
|
400
|
+
// The linear single label already mapped its styles onto attributed ranges.
|
|
401
|
+
guard !linear else { return }
|
|
402
|
+
if let primary = style.dictionary("columns") {
|
|
403
|
+
if primary["alignment"] != nil { alignment = .fill }
|
|
404
|
+
text(primaryLabel, primary)
|
|
405
|
+
}
|
|
406
|
+
if let secondary = style.dictionary("columnSecondary") {
|
|
407
|
+
if secondary["alignment"] != nil { alignment = .fill }
|
|
408
|
+
text(secondaryLeadingLabel, secondary)
|
|
409
|
+
text(secondaryLabel, secondary)
|
|
410
|
+
}
|
|
411
|
+
if style["lineGap"] != nil { spacing = CGFloat(style.double("lineGap")) }
|
|
412
|
+
if style["titleBadgeGap"] != nil {
|
|
413
|
+
primaryLine.spacing = CGFloat(style.double("titleBadgeGap"))
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
private func line(_ text: String, font: UIFont, color: UIColor, height: CGFloat)
|
|
418
|
+
-> NSAttributedString
|
|
419
|
+
{
|
|
420
|
+
let paragraph = NSMutableParagraphStyle()
|
|
421
|
+
paragraph.minimumLineHeight = height
|
|
422
|
+
paragraph.maximumLineHeight = height
|
|
423
|
+
return NSAttributedString(
|
|
424
|
+
string: text,
|
|
425
|
+
attributes: [
|
|
426
|
+
.font: font,
|
|
427
|
+
.foregroundColor: color,
|
|
428
|
+
.paragraphStyle: paragraph,
|
|
429
|
+
]
|
|
430
|
+
)
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
private func textColor(_ tone: String, theme: [String: Any]?) -> UIColor {
|
|
434
|
+
switch tone {
|
|
435
|
+
// OneKey patch: account warnings and hidden balances use existing theme tokens.
|
|
436
|
+
case "disabled": return nativeListColor(theme, "disabledText", "#8D8D8D")
|
|
437
|
+
case "caution": return nativeListColor(theme, "caution", "#AB6400")
|
|
438
|
+
case "secondary": return nativeListColor(theme, "secondaryText", "#646464")
|
|
439
|
+
case "positive": return nativeListColor(theme, "positive", "#218358")
|
|
440
|
+
case "negative": return nativeListColor(theme, "negative", "#CE2C31")
|
|
441
|
+
default: return nativeListColor(theme, "primaryText", "#202020")
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|