@onekeyfe/react-native-native-list 3.0.157-alpha.249 → 3.0.157-alpha.250

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.
Files changed (130) hide show
  1. package/README.md +21 -5
  2. package/android/src/main/java/com/onekey/nativelist/NativeListAccessoryStack.kt +711 -0
  3. package/android/src/main/java/com/onekey/nativelist/NativeListActionRenderer.kt +169 -0
  4. package/android/src/main/java/com/onekey/nativelist/NativeListActivityRenderer.kt +243 -0
  5. package/android/src/main/java/com/onekey/nativelist/NativeListAdapter.kt +20 -6
  6. package/android/src/main/java/com/onekey/nativelist/NativeListDataRowRenderer.kt +487 -0
  7. package/android/src/main/java/com/onekey/nativelist/NativeListIdentityRenderer.kt +820 -0
  8. package/android/src/main/java/com/onekey/nativelist/NativeListImageSlot.kt +147 -0
  9. package/android/src/main/java/com/onekey/nativelist/NativeListLeadingVisual.kt +453 -0
  10. package/android/src/main/java/com/onekey/nativelist/NativeListMarketRenderer.kt +997 -0
  11. package/android/src/main/java/com/onekey/nativelist/NativeListMediaTileRenderer.kt +234 -0
  12. package/android/src/main/java/com/onekey/nativelist/NativeListMessageRenderer.kt +232 -0
  13. package/android/src/main/java/com/onekey/nativelist/NativeListMetricCardRenderer.kt +551 -0
  14. package/android/src/main/java/com/onekey/nativelist/NativeListModels.kt +24 -0
  15. package/android/src/main/java/com/onekey/nativelist/NativeListPrimitives.kt +475 -0
  16. package/android/src/main/java/com/onekey/nativelist/NativeListRailRenderer.kt +143 -0
  17. package/android/src/main/java/com/onekey/nativelist/NativeListRendererRegistry.kt +42 -0
  18. package/android/src/main/java/com/onekey/nativelist/NativeListRendererRowView.kt +415 -0
  19. package/android/src/main/java/com/onekey/nativelist/NativeListResolvedText.kt +163 -0
  20. package/android/src/main/java/com/onekey/nativelist/NativeListRowHost.kt +70 -0
  21. package/android/src/main/java/com/onekey/nativelist/NativeListSectionHeaderRenderer.kt +748 -0
  22. package/android/src/main/java/com/onekey/nativelist/NativeListSystemRenderer.kt +464 -0
  23. package/android/src/main/java/com/onekey/nativelist/NativeListTextView.kt +25 -0
  24. package/android/src/main/java/com/onekey/nativelist/NativeListView.kt +174 -104
  25. package/android/src/main/java/com/onekey/nativelist/NativeListWalletGroupRenderer.kt +407 -0
  26. package/android/src/test/java/com/margelo/nitro/nativelist/NativeListSelectorBackgroundColorTest.kt +34 -0
  27. package/docs/DESIGN.md +518 -9
  28. package/docs/MIGRATION_BASELINE.md +117 -0
  29. package/docs/ROW_TEMPLATES.md +359 -0
  30. package/docs/SPEC.md +358 -0
  31. package/docs/STYLE_SPEC.md +978 -0
  32. package/docs/images/list-capabilities.svg +39 -0
  33. package/docs/images/row-action.svg +17 -0
  34. package/docs/images/row-activity.svg +24 -0
  35. package/docs/images/row-data.svg +24 -0
  36. package/docs/images/row-identity.svg +25 -0
  37. package/docs/images/row-market.svg +23 -0
  38. package/docs/images/row-media-tile.svg +26 -0
  39. package/docs/images/row-message.svg +20 -0
  40. package/docs/images/row-metric-card.svg +33 -0
  41. package/docs/images/row-rail.svg +18 -0
  42. package/docs/images/row-section-header.svg +19 -0
  43. package/docs/images/row-style-regions.svg +30 -0
  44. package/docs/images/row-system.svg +30 -0
  45. package/docs/images/row-wallet-group.svg +26 -0
  46. package/ios/NativeListAccessoryStack.swift +680 -0
  47. package/ios/NativeListActionRenderer.swift +108 -0
  48. package/ios/NativeListActivityRenderer.swift +179 -0
  49. package/ios/NativeListDataRowRenderer.swift +444 -0
  50. package/ios/NativeListIdentityRenderer.swift +685 -0
  51. package/ios/NativeListImageSlot.swift +140 -0
  52. package/ios/NativeListLeadingVisual.swift +372 -0
  53. package/ios/NativeListMarketRenderer.swift +698 -0
  54. package/ios/NativeListMediaTileRenderer.swift +184 -0
  55. package/ios/NativeListMessageRenderer.swift +168 -0
  56. package/ios/NativeListMetricCardRenderer.swift +420 -0
  57. package/ios/NativeListModels.swift +32 -0
  58. package/ios/NativeListRailRenderer.swift +152 -0
  59. package/ios/NativeListRendererCell.swift +311 -0
  60. package/ios/NativeListRendererRegistry.swift +50 -0
  61. package/ios/NativeListResolvedText.swift +122 -0
  62. package/ios/NativeListRowHost.swift +63 -0
  63. package/ios/NativeListSectionHeaderRenderer.swift +708 -0
  64. package/ios/NativeListSystemRenderer.swift +568 -0
  65. package/ios/NativeListTextLabel.swift +42 -0
  66. package/ios/NativeListTextStyles.swift +236 -0
  67. package/ios/NativeListWalletGroupRenderer.swift +255 -0
  68. package/ios/RNCNativeListView.swift +111 -180
  69. package/lib/module/models.js +54 -0
  70. package/lib/module/validation.js +324 -26
  71. package/lib/module/web/NativeListWebEngine.js +148 -936
  72. package/lib/module/web/templates/ActionRowRenderer.js +97 -0
  73. package/lib/module/web/templates/ActivityRowRenderer.js +95 -0
  74. package/lib/module/web/templates/DataRowRenderer.js +145 -0
  75. package/lib/module/web/templates/IdentityRowRenderer.js +289 -0
  76. package/lib/module/web/templates/MarketRowRenderer.js +310 -0
  77. package/lib/module/web/templates/MediaTileRowRenderer.js +132 -0
  78. package/lib/module/web/templates/MessageRowRenderer.js +165 -0
  79. package/lib/module/web/templates/MetricCardRowRenderer.js +172 -0
  80. package/lib/module/web/templates/RailRowRenderer.js +98 -0
  81. package/lib/module/web/templates/RowContainerStyle.js +100 -0
  82. package/lib/module/web/templates/RowElements.js +93 -0
  83. package/lib/module/web/templates/RowRendererRegistry.js +70 -0
  84. package/lib/module/web/templates/RowText.js +121 -0
  85. package/lib/module/web/templates/RowVisual.js +43 -0
  86. package/lib/module/web/templates/SectionHeaderRowRenderer.js +146 -0
  87. package/lib/module/web/templates/SystemRowRenderer.js +161 -0
  88. package/lib/module/web/templates/WalletGroupRowRenderer.js +96 -0
  89. package/lib/typescript/src/models.d.ts +166 -13
  90. package/lib/typescript/src/web/NativeListWebEngine.d.ts +18 -16
  91. package/lib/typescript/src/web/templates/ActionRowRenderer.d.ts +15 -0
  92. package/lib/typescript/src/web/templates/ActivityRowRenderer.d.ts +15 -0
  93. package/lib/typescript/src/web/templates/DataRowRenderer.d.ts +15 -0
  94. package/lib/typescript/src/web/templates/IdentityRowRenderer.d.ts +16 -0
  95. package/lib/typescript/src/web/templates/MarketRowRenderer.d.ts +30 -0
  96. package/lib/typescript/src/web/templates/MediaTileRowRenderer.d.ts +15 -0
  97. package/lib/typescript/src/web/templates/MessageRowRenderer.d.ts +44 -0
  98. package/lib/typescript/src/web/templates/MetricCardRowRenderer.d.ts +15 -0
  99. package/lib/typescript/src/web/templates/RailRowRenderer.d.ts +28 -0
  100. package/lib/typescript/src/web/templates/RowContainerStyle.d.ts +9 -0
  101. package/lib/typescript/src/web/templates/RowElements.d.ts +38 -0
  102. package/lib/typescript/src/web/templates/RowRendererRegistry.d.ts +83 -0
  103. package/lib/typescript/src/web/templates/RowText.d.ts +9 -0
  104. package/lib/typescript/src/web/templates/RowVisual.d.ts +20 -0
  105. package/lib/typescript/src/web/templates/SectionHeaderRowRenderer.d.ts +15 -0
  106. package/lib/typescript/src/web/templates/SystemRowRenderer.d.ts +16 -0
  107. package/lib/typescript/src/web/templates/WalletGroupRowRenderer.d.ts +20 -0
  108. package/package.json +3 -3
  109. package/src/models.ts +281 -19
  110. package/src/validation.ts +501 -46
  111. package/src/web/NativeListWebEngine.ts +361 -1932
  112. package/src/web/templates/ActionRowRenderer.ts +123 -0
  113. package/src/web/templates/ActivityRowRenderer.ts +159 -0
  114. package/src/web/templates/DataRowRenderer.ts +212 -0
  115. package/src/web/templates/IdentityRowRenderer.ts +527 -0
  116. package/src/web/templates/MarketRowRenderer.ts +467 -0
  117. package/src/web/templates/MediaTileRowRenderer.ts +182 -0
  118. package/src/web/templates/MessageRowRenderer.ts +306 -0
  119. package/src/web/templates/MetricCardRowRenderer.ts +337 -0
  120. package/src/web/templates/RailRowRenderer.ts +136 -0
  121. package/src/web/templates/RowContainerStyle.ts +131 -0
  122. package/src/web/templates/RowElements.ts +160 -0
  123. package/src/web/templates/RowRendererRegistry.ts +111 -0
  124. package/src/web/templates/RowText.ts +181 -0
  125. package/src/web/templates/RowVisual.ts +115 -0
  126. package/src/web/templates/SectionHeaderRowRenderer.ts +243 -0
  127. package/src/web/templates/SystemRowRenderer.ts +282 -0
  128. package/src/web/templates/WalletGroupRowRenderer.ts +131 -0
  129. package/android/src/main/java/com/onekey/nativelist/NativeListRowView.kt +0 -4538
  130. package/ios/NativeListCell.swift +0 -4300
@@ -0,0 +1,407 @@
1
+ package com.margelo.nitro.nativelist
2
+
3
+ import android.animation.Animator
4
+ import android.animation.AnimatorListenerAdapter
5
+ import android.animation.TimeInterpolator
6
+ import android.animation.ValueAnimator
7
+ import android.graphics.drawable.GradientDrawable
8
+ import android.view.Gravity
9
+ import android.view.View
10
+ import android.widget.FrameLayout
11
+ import android.widget.LinearLayout
12
+ import android.widget.TextView
13
+ import com.facebook.react.uimanager.ThemedReactContext
14
+ import org.json.JSONObject
15
+
16
+ /** Composite only; each keyed member owns an independent Identity binding. */
17
+ internal class NativeListWalletGroupRowView(context: ThemedReactContext) :
18
+ NativeListRendererRowView(context) {
19
+ private val membersColumn = LinearLayout(context).apply { orientation = VERTICAL }
20
+ private val memberViews = linkedMapOf<String, NativeListIdentityRowView>()
21
+ private var members = emptyList<NativeListItem>()
22
+ private val compactContainer =
23
+ FrameLayout(context).apply {
24
+ visibility = GONE
25
+ importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
26
+ }
27
+ private var compactParent: NativeListIdentityRowView? = null
28
+ private val dragBadge = TextView(context)
29
+ private var compact = false
30
+ private var expandedHeight = 0
31
+ private var animatedHeight: Int? = null
32
+ private var expandAnimator: ValueAnimator? = null
33
+ private var currentTheme: JSONObject? = null
34
+ private var currentLayout = "linear"
35
+ override val defaultCornerRadius = 12
36
+ override val pressChangesBackground = false
37
+ private var containerStyle = JSONObject()
38
+ private var compactHeight = 0
39
+ override val showsSelection = false
40
+
41
+ override fun usesSourceScale(item: NativeListItem, provided: Boolean) =
42
+ item.usesSelectorSourceScale || provided
43
+
44
+ override fun unselectedBackground(
45
+ item: NativeListItem,
46
+ theme: JSONObject?,
47
+ layout: String,
48
+ itemIndex: Int?,
49
+ ) = parseNativeListColor(theme?.optString("subduedBackground", "#F9F9F9") ?: "#F9F9F9")
50
+
51
+ override fun resolvedMeasureHeight(explicit: Int?): Int? =
52
+ if (compact) compactHeight else animatedHeight ?: explicit ?: expandedHeight
53
+
54
+ override fun accessibilityText(item: NativeListItem) =
55
+ item.json.optString(
56
+ "accessibilityLabel",
57
+ item.json.optJSONObject("parent")?.optString("title") ?: "",
58
+ )
59
+
60
+ private fun memberItems(item: NativeListItem): List<NativeListItem> {
61
+ val children = item.json.getJSONArray("children")
62
+ return listOf(NativeListItem.parse(item.json.getJSONObject("parent"))) +
63
+ (0 until children.length()).map { NativeListItem.parse(children.getJSONObject(it)) }
64
+ }
65
+
66
+ private fun defaultMemberHeight(item: NativeListItem) =
67
+ item.json.optInt("height", if ((item.json.optJSONArray("badges")?.length() ?: 0) > 0) 92 else 68)
68
+
69
+ private fun memberHeight(item: NativeListItem) =
70
+ item.styledHeight?.let(::stylePx) ?: dp(defaultMemberHeight(item))
71
+
72
+ init {
73
+ orientation = VERTICAL
74
+ addView(membersColumn, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT))
75
+ addView(compactContainer, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT))
76
+ dragBadge.gravity = Gravity.CENTER
77
+ dragBadge.typeface = NativeListFonts.semibold(context)
78
+ compactContainer.addView(dragBadge)
79
+ }
80
+
81
+ override fun bindContent(
82
+ item: NativeListItem,
83
+ theme: JSONObject?,
84
+ layout: String,
85
+ checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String,
86
+ ) {
87
+ currentTheme = theme
88
+ currentLayout = layout
89
+ members = memberItems(item)
90
+ val keys = members.map { it.key }.toSet()
91
+ memberViews.keys
92
+ .toList()
93
+ .filter { it !in keys }
94
+ .forEach { key ->
95
+ memberViews.remove(key)?.let {
96
+ it.dispose()
97
+ membersColumn.removeView(it)
98
+ }
99
+ }
100
+ val style = item.json.optJSONObject("style")
101
+ containerStyle = style?.optJSONObject("container") ?: JSONObject()
102
+ val inset = if (members.first().json.has("height")) dp(1) else 0
103
+ val horizontal =
104
+ style
105
+ ?.takeIf { it.has("horizontalPadding") }
106
+ ?.let { stylePx(it.optDouble("horizontalPadding")) } ?: inset
107
+ val styledVertical =
108
+ style?.takeIf { it.has("verticalPadding") }?.let { stylePx(it.optDouble("verticalPadding")) }
109
+ val vertical = styledVertical ?: inset
110
+ membersColumn.setPadding(horizontal, vertical, horizontal, vertical)
111
+ setPadding(0, 0, 0, 0)
112
+ gravity =
113
+ Gravity.FILL_HORIZONTAL or
114
+ when (style?.optJSONObject("container")?.optString("contentVerticalAlignment")) {
115
+ "bottom" -> Gravity.BOTTOM
116
+ "center" -> Gravity.CENTER_VERTICAL
117
+ else -> Gravity.TOP
118
+ }
119
+ expandedHeight =
120
+ item.styledHeight?.let(::stylePx)
121
+ ?: modelHeight(item, layout)
122
+ ?: if (styledVertical == null && members.none { it.styledHeight != null }) {
123
+ // Legacy default: one rounding pass over the whole unstyled group; the
124
+ // row size modifier can only raise the minimum.
125
+ val base =
126
+ members.sumOf(::defaultMemberHeight) + (members.size - 1) * 12 + if (inset > 0) 2 else 0
127
+ val sizeDelta =
128
+ when (item.json.optString("size")) {
129
+ "small" -> -8
130
+ "large" -> 12
131
+ else -> 0
132
+ }
133
+ maxOf(dp(base), dp((base + sizeDelta).coerceAtLeast(0)))
134
+ } else members.sumOf(::memberHeight) + dp((members.size - 1) * 12) + vertical * 2
135
+ members.forEachIndexed { index, member ->
136
+ val view =
137
+ memberViews.getOrPut(member.key) {
138
+ NativeListIdentityRowView(reactContext).also { view ->
139
+ // Legacy: members own their disabled state; the group never gates them.
140
+ view.onRowPress = { source, origin -> onRowPress?.invoke(source, origin) }
141
+ view.onAction = { source, key, target, origin ->
142
+ onAction?.invoke(source, key, target, origin)
143
+ }
144
+ view.onBindingInvalidated = { source, epoch ->
145
+ onBindingInvalidated?.invoke(source, epoch)
146
+ }
147
+ }
148
+ }
149
+ view.listStyle = listStyle
150
+ view.bind(
151
+ member,
152
+ theme,
153
+ layout,
154
+ "vertical",
155
+ null,
156
+ member.json.optBoolean("selected"),
157
+ checkboxState,
158
+ sourceScale,
159
+ )
160
+ if (membersColumn.indexOfChild(view) != index) {
161
+ membersColumn.removeView(view)
162
+ membersColumn.addView(view, index)
163
+ }
164
+ view.layoutParams =
165
+ LayoutParams(LayoutParams.MATCH_PARENT, memberHeight(member)).apply {
166
+ if (index > 0) topMargin = dp(12)
167
+ }
168
+ }
169
+ if (compactParent == null) {
170
+ compactParent =
171
+ NativeListIdentityRowView(reactContext).also {
172
+ compactContainer.addView(
173
+ it,
174
+ 0,
175
+ FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT),
176
+ )
177
+ }
178
+ }
179
+ compactParent!!.listStyle = listStyle
180
+ val parent = members.first()
181
+ compactParent!!.bind(
182
+ parent,
183
+ theme,
184
+ layout,
185
+ "vertical",
186
+ null,
187
+ parent.json.optBoolean("selected"),
188
+ checkboxState,
189
+ sourceScale,
190
+ )
191
+ // Legacy compact drag: the parent keeps its own height (92 with badges) plus
192
+ // the group inset, never less than the 68dp allocation.
193
+ compactHeight = maxOf(dp(68), memberHeight(parent) + vertical * 2)
194
+ compactContainer.setPadding(horizontal, vertical, horizontal, vertical)
195
+ compactContainer.layoutParams.height = compactHeight
196
+ val count = members.drop(1).count { it.json.optBoolean("draggable", true) }
197
+ dragBadge.text = "+$count"
198
+ dragBadge.visibility = if (count == 0) GONE else VISIBLE
199
+ dragBadge.textSize = if (sourceScale) 12f else NativeListScale.font(resources, 12f)
200
+ dragBadge.setPadding(dp(6), 0, dp(6), 0)
201
+ dragBadge.minWidth = dp(24)
202
+ dragBadge.setTextColor(
203
+ parseNativeListColor(theme?.optString("inverseText", "#FCFCFC") ?: "#FCFCFC")
204
+ )
205
+ dragBadge.background =
206
+ GradientDrawable().apply {
207
+ setColor(
208
+ parseNativeListColor(theme?.optString("inverseBackground", "#000000DF") ?: "#000000DF")
209
+ )
210
+ setStroke(
211
+ dp(1),
212
+ parseNativeListColor(theme?.optString("rowBackground", "#FFFFFF") ?: "#FFFFFF"),
213
+ )
214
+ cornerRadius = dp(12).toFloat()
215
+ }
216
+ dragBadge.layoutParams =
217
+ FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, dp(24), Gravity.END or Gravity.BOTTOM)
218
+ .apply {
219
+ // Legacy: the badge is inset 4dp from the group's own edges.
220
+ rightMargin = dp(4) - horizontal
221
+ bottomMargin = dp(4) - vertical
222
+ }
223
+ }
224
+
225
+ override fun bindSelectionContent(
226
+ item: NativeListItem,
227
+ checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String,
228
+ ) {
229
+ members = memberItems(item)
230
+ members.forEach { member ->
231
+ memberViews[member.key]?.bindSelection(
232
+ member,
233
+ currentTheme,
234
+ currentLayout,
235
+ null,
236
+ member.json.optBoolean("selected"),
237
+ checkboxState,
238
+ )
239
+ }
240
+ members.firstOrNull()?.let {
241
+ compactParent?.bindSelection(
242
+ it,
243
+ currentTheme,
244
+ currentLayout,
245
+ null,
246
+ it.json.optBoolean("selected"),
247
+ checkboxState,
248
+ )
249
+ }
250
+ }
251
+
252
+ override fun canStartWalletGroupReorder(localY: Float): Boolean {
253
+ val y = localY - membersColumn.top
254
+ members.forEach { member ->
255
+ val view = memberViews[member.key] ?: return@forEach
256
+ if (y >= view.top && y < view.bottom)
257
+ // Legacy: only `draggable: false` excludes a member from starting a group drag.
258
+ return member.json.optBoolean("draggable", true)
259
+ }
260
+ return true
261
+ }
262
+
263
+ private var expansionCompletion: (() -> Unit)? = null
264
+
265
+ // Legacy group chrome: subdued fill with the separator border drawn beneath the
266
+ // members (not as a foreground); row-level backgroundColor does not apply.
267
+ override fun appearance() {
268
+ super.appearance()
269
+ val theme = currentTheme
270
+ fun parse(value: String, fallback: String) =
271
+ runCatching { parseNativeListColor(value) }.getOrElse { parseNativeListColor(fallback) }
272
+ val fill =
273
+ if (containerStyle.has("backgroundColor"))
274
+ parse(containerStyle.optString("backgroundColor"), "#F9F9F9")
275
+ else parse(theme?.optString("subduedBackground", "#F9F9F9") ?: "#F9F9F9", "#F9F9F9")
276
+ val stroke =
277
+ if (containerStyle.has("borderColor")) parse(containerStyle.optString("borderColor"), "#0000001F")
278
+ else parse(theme?.optString("separator", "#0000001F") ?: "#0000001F", "#0000001F")
279
+ background =
280
+ GradientDrawable().apply {
281
+ setColor(fill)
282
+ cornerRadius =
283
+ if (containerStyle.has("cornerRadius")) stylePx(containerStyle.optDouble("cornerRadius")).toFloat()
284
+ else dp(12).toFloat()
285
+ setStroke(
286
+ if (containerStyle.has("borderWidth")) stylePx(containerStyle.optDouble("borderWidth")) else dp(1),
287
+ stroke,
288
+ )
289
+ }
290
+ foreground = null
291
+ }
292
+
293
+ private fun showExpandedMembers(restAlpha: Float) {
294
+ membersColumn.visibility = VISIBLE
295
+ membersColumn.alpha = 1f
296
+ for (index in 0 until membersColumn.childCount) {
297
+ membersColumn.getChildAt(index).apply {
298
+ visibility = VISIBLE
299
+ alpha = if (index == 0) 1f else restAlpha
300
+ }
301
+ }
302
+ }
303
+
304
+ // Cancelling still settles the expanded state and runs the pending completion,
305
+ // so the container's post-drop relayout is never lost.
306
+ private fun cancelExpansion() {
307
+ val completion = expansionCompletion
308
+ expansionCompletion = null
309
+ val animator = expandAnimator
310
+ expandAnimator = null
311
+ animatedHeight = null
312
+ if (animator != null) {
313
+ animator.removeAllListeners()
314
+ animator.cancel()
315
+ showExpandedMembers(1f)
316
+ requestLayout()
317
+ }
318
+ completion?.invoke()
319
+ }
320
+
321
+ override fun setReorderActive(active: Boolean) {
322
+ if (compact == active && expandAnimator == null) return
323
+ cancelExpansion()
324
+ compact = active
325
+ if (active) {
326
+ membersColumn.visibility = GONE
327
+ } else {
328
+ showExpandedMembers(1f)
329
+ }
330
+ compactContainer.visibility = if (active) VISIBLE else GONE
331
+ compactContainer.alpha = 1f
332
+ compactParent?.setReorderActive(active)
333
+ appearance()
334
+ requestLayout()
335
+ }
336
+
337
+ override fun finishWalletGroupReorder(
338
+ durationMs: Long,
339
+ interpolator: TimeInterpolator,
340
+ completion: (() -> Unit)?,
341
+ ) {
342
+ cancelExpansion()
343
+ val startHeight = height.coerceAtLeast(dp(68))
344
+ compact = false
345
+ compactParent?.setReorderActive(false)
346
+ compactContainer.visibility = GONE
347
+ compactContainer.alpha = 1f
348
+ // Legacy: the first member stays opaque while the remaining members fade in.
349
+ showExpandedMembers(0f)
350
+ appearance()
351
+ if (expandedHeight <= startHeight) {
352
+ showExpandedMembers(1f)
353
+ requestLayout()
354
+ completion?.invoke()
355
+ return
356
+ }
357
+ animatedHeight = startHeight
358
+ requestLayout()
359
+ expansionCompletion = completion
360
+ expandAnimator =
361
+ ValueAnimator.ofInt(startHeight, expandedHeight).apply {
362
+ duration = durationMs
363
+ this.interpolator = interpolator
364
+ addUpdateListener {
365
+ animatedHeight = it.animatedValue as Int
366
+ val fraction = it.animatedFraction
367
+ for (index in 1 until membersColumn.childCount) membersColumn.getChildAt(index).alpha = fraction
368
+ requestLayout()
369
+ }
370
+ addListener(
371
+ object : AnimatorListenerAdapter() {
372
+ override fun onAnimationEnd(animation: Animator) {
373
+ if (expandAnimator !== animation) return
374
+ expandAnimator = null
375
+ animatedHeight = null
376
+ showExpandedMembers(1f)
377
+ requestLayout()
378
+ val pending = expansionCompletion
379
+ expansionCompletion = null
380
+ pending?.invoke()
381
+ }
382
+ }
383
+ )
384
+ start()
385
+ }
386
+ }
387
+
388
+ override fun recycleContent() {
389
+ cancelExpansion()
390
+ memberViews.values.forEach { it.dispose() }
391
+ memberViews.clear()
392
+ membersColumn.removeAllViews()
393
+ membersColumn.alpha = 1f
394
+ membersColumn.visibility = VISIBLE
395
+ members = emptyList()
396
+ compactParent?.dispose()
397
+ compactParent?.let(compactContainer::removeView)
398
+ compactParent = null
399
+ compactContainer.visibility = GONE
400
+ compactContainer.alpha = 1f
401
+ compact = false
402
+ expandedHeight = 0
403
+ currentTheme = null
404
+ }
405
+
406
+ override fun disposeContent() {}
407
+ }
@@ -0,0 +1,34 @@
1
+ package com.margelo.nitro.nativelist
2
+
3
+ import org.junit.Assert.assertEquals
4
+ import org.junit.Assert.assertNull
5
+ import org.junit.Test
6
+
7
+ class NativeListSelectorBackgroundColorTest {
8
+ @Test
9
+ fun walletGroupUsesOnlyTheContainerStyleColor() {
10
+ assertNull(
11
+ resolveNativeListSelectorBackgroundColor(
12
+ rowType = "walletGroup",
13
+ styleBackgroundColor = null,
14
+ rowBackgroundColor = "#112233",
15
+ ),
16
+ )
17
+ assertEquals(
18
+ "#445566",
19
+ resolveNativeListSelectorBackgroundColor(
20
+ rowType = "walletGroup",
21
+ styleBackgroundColor = "#445566",
22
+ rowBackgroundColor = "#112233",
23
+ ),
24
+ )
25
+ assertEquals(
26
+ "#112233",
27
+ resolveNativeListSelectorBackgroundColor(
28
+ rowType = "identity",
29
+ styleBackgroundColor = null,
30
+ rowBackgroundColor = "#112233",
31
+ ),
32
+ )
33
+ }
34
+ }