@onekeyfe/react-native-native-list 3.0.134 → 3.0.135

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.
@@ -103,4 +103,5 @@ dependencies {
103
103
  implementation project(":react-native-nitro-modules")
104
104
  implementation project(":onekeyfe_react-native-native-logger")
105
105
  implementation project(":onekeyfe_react-native-image")
106
+ testImplementation "junit:junit:4.13.2"
106
107
  }
@@ -3,6 +3,25 @@ package com.margelo.nitro.nativelist
3
3
  import org.json.JSONArray
4
4
  import org.json.JSONObject
5
5
 
6
+ internal fun isNativeListRowPressEnabled(
7
+ type: String,
8
+ variant: String,
9
+ disabled: Boolean,
10
+ pressDisabled: Boolean,
11
+ ): Boolean = !disabled && !pressDisabled && (type != "system" || variant == "retry")
12
+
13
+ internal fun isNativeListWholeRowInteractive(
14
+ type: String,
15
+ variant: String,
16
+ disabled: Boolean,
17
+ pressDisabled: Boolean,
18
+ ): Boolean = type != "walletGroup" && isNativeListRowPressEnabled(
19
+ type = type,
20
+ variant = variant,
21
+ disabled = disabled,
22
+ pressDisabled = pressDisabled,
23
+ )
24
+
6
25
  internal data class NativeListItem(
7
26
  val key: String,
8
27
  val type: String,
@@ -22,9 +41,20 @@ internal data class NativeListItem(
22
41
  get() = !json.optBoolean("disabled", false) && type in SELECTABLE_TYPES
23
42
 
24
43
  val isRowPressEnabled: Boolean
25
- get() = !json.optBoolean("disabled", false) &&
26
- !json.optBoolean("pressDisabled", false) &&
27
- (type != "system" || json.optString("variant") == "retry")
44
+ get() = isNativeListRowPressEnabled(
45
+ type = type,
46
+ variant = json.optString("variant"),
47
+ disabled = json.optBoolean("disabled", false),
48
+ pressDisabled = json.optBoolean("pressDisabled", false),
49
+ )
50
+
51
+ val isWholeRowInteractive: Boolean
52
+ get() = isNativeListWholeRowInteractive(
53
+ type = type,
54
+ variant = json.optString("variant"),
55
+ disabled = json.optBoolean("disabled", false),
56
+ pressDisabled = json.optBoolean("pressDisabled", false),
57
+ )
28
58
 
29
59
  val isReorderable: Boolean
30
60
  get() {
@@ -985,6 +985,11 @@ internal class NativeListRowView(
985
985
  "action" -> bindAction(item, theme, checkboxState)
986
986
  "system" -> bindSystem(item, theme)
987
987
  }
988
+ // Keep passive rows out of accessibility and keyboard focus while allowing
989
+ // their independently bound accessory controls to remain interactive.
990
+ val wholeRowPressEnabled = item.isWholeRowInteractive
991
+ isClickable = wholeRowPressEnabled
992
+ isFocusable = wholeRowPressEnabled
988
993
  applySize(item)
989
994
  if (item.type == "system" && item.json.optString("variant") == "warning") {
990
995
  title.typeface = NativeListFonts.medium(context)
@@ -0,0 +1,43 @@
1
+ package com.margelo.nitro.nativelist
2
+
3
+ import org.junit.Assert.assertEquals
4
+ import org.junit.Test
5
+
6
+ class NativeListPressPolicyTest {
7
+ @Test
8
+ fun wholeRowInteractionMatchesPassiveAndAccessoryOnlyRows() {
9
+ data class Case(
10
+ val type: String,
11
+ val variant: String = "",
12
+ val disabled: Boolean = false,
13
+ val pressDisabled: Boolean = false,
14
+ val expected: Boolean,
15
+ )
16
+
17
+ val cases = listOf(
18
+ Case(type = "identity", expected = true),
19
+ Case(type = "identity", pressDisabled = true, expected = false),
20
+ Case(type = "identity", disabled = true, expected = false),
21
+ Case(type = "system", variant = "retry", expected = true),
22
+ Case(type = "system", variant = "retry", pressDisabled = true, expected = false),
23
+ Case(type = "system", variant = "loading", expected = false),
24
+ Case(type = "system", variant = "noMatch", expected = false),
25
+ Case(type = "system", variant = "warning", expected = false),
26
+ Case(type = "system", variant = "end", expected = false),
27
+ Case(type = "system", variant = "spacer", expected = false),
28
+ Case(type = "walletGroup", expected = false),
29
+ )
30
+
31
+ assertEquals(
32
+ cases.map(Case::expected),
33
+ cases.map { case ->
34
+ isNativeListWholeRowInteractive(
35
+ type = case.type,
36
+ variant = case.variant,
37
+ disabled = case.disabled,
38
+ pressDisabled = case.pressDisabled,
39
+ )
40
+ },
41
+ )
42
+ }
43
+ }
@@ -115,6 +115,7 @@ final class NativeListView: UIView {
115
115
  private let reorderStartFeedback = UIImpactFeedbackGenerator(style: .medium)
116
116
  private let reorderMoveFeedback = UISelectionFeedbackGenerator()
117
117
  private var interactiveReorderFeedbackIndex: Int?
118
+ private var interactiveMovementKeys: [String]?
118
119
  private var actionAnchor: ActionAnchorRecord?
119
120
  private let actionAnchorInstanceID = UUID().uuidString
120
121
  private var actionAnchorCounter = 0
@@ -731,6 +732,9 @@ final class NativeListView: UIView {
731
732
  collectionView.layoutIfNeeded()
732
733
  return
733
734
  }
735
+ if !interactiveReorderUsesAtomicTargeting {
736
+ interactiveMovementKeys = dataSource.snapshot().itemIdentifiers
737
+ }
734
738
  interactiveReorderFeedbackIndex = indexPath.item
735
739
  reorderStartFeedback.impactOccurred(intensity: 0.7)
736
740
  reorderStartFeedback.prepare()
@@ -838,9 +842,11 @@ final class NativeListView: UIView {
838
842
  interactiveReorderAnimator?.stopAnimation(true)
839
843
  interactiveReorderFeedbackIndex = nil
840
844
  if cancelled {
845
+ interactiveMovementKeys = nil
841
846
  collectionView.cancelInteractiveMovement()
842
847
  } else {
843
848
  collectionView.endInteractiveMovement()
849
+ interactiveMovementKeys = nil
844
850
  }
845
851
  let animator = UIViewPropertyAnimator(
846
852
  duration: ReorderAnimation.duration,
@@ -874,6 +880,7 @@ final class NativeListView: UIView {
874
880
  guard interactiveReorderSource != nil else { return }
875
881
  interactiveReorderAnimator?.stopAnimation(true)
876
882
  interactiveReorderAnimator = nil
883
+ interactiveMovementKeys = nil
877
884
  collectionView.cancelInteractiveMovement()
878
885
  interactiveReorderCell?.setPressed(false)
879
886
  if interactiveReorderCompactKey != nil {
@@ -978,6 +985,23 @@ final class NativeListView: UIView {
978
985
  return collectionView.cellForItem(at: IndexPath(item: index, section: 0)) as? NativeListCell
979
986
  }
980
987
 
988
+ // OneKey patch: flow layout sizes an interactive move in UIKit's in-flight order,
989
+ // while the diffable snapshot and cell index paths keep the pre-drag order until
990
+ // the move commits. Resolving sizes from the snapshot swaps row heights.
991
+ private func layoutItem(at indexPath: IndexPath) -> NativeListItem? {
992
+ guard let keys = interactiveMovementKeys else { return item(at: indexPath) }
993
+ return keys[safe: indexPath.item].flatMap { itemsByKey[$0] }
994
+ }
995
+
996
+ private func updateInteractiveMovementKeys(movingFrom source: IndexPath, to target: IndexPath) {
997
+ guard interactiveMovementKeys != nil else { return }
998
+ var keys = dataSource.snapshot().itemIdentifiers
999
+ guard keys.indices.contains(source.item) else { return }
1000
+ let key = keys.remove(at: source.item)
1001
+ keys.insert(key, at: min(max(target.item, 0), keys.count))
1002
+ interactiveMovementKeys = keys
1003
+ }
1004
+
981
1005
  private func item(at indexPath: IndexPath) -> NativeListItem? {
982
1006
  if let key = dataSource.itemIdentifier(for: indexPath), let item = itemsByKey[key] {
983
1007
  return item
@@ -2049,7 +2073,9 @@ extension NativeListView: UICollectionViewDelegateFlowLayout {
2049
2073
  atCurrentIndexPath currentIndexPath: IndexPath,
2050
2074
  toProposedIndexPath proposedIndexPath: IndexPath
2051
2075
  ) -> IndexPath {
2052
- interactiveReorderUsesAtomicTargeting ? currentIndexPath : proposedIndexPath
2076
+ guard !interactiveReorderUsesAtomicTargeting else { return currentIndexPath }
2077
+ updateInteractiveMovementKeys(movingFrom: originalIndexPath, to: proposedIndexPath)
2078
+ return proposedIndexPath
2053
2079
  }
2054
2080
 
2055
2081
  func collectionView(
@@ -2057,7 +2083,9 @@ extension NativeListView: UICollectionViewDelegateFlowLayout {
2057
2083
  targetIndexPathForMoveFromItemAt originalIndexPath: IndexPath,
2058
2084
  toProposedIndexPath proposedIndexPath: IndexPath
2059
2085
  ) -> IndexPath {
2060
- interactiveReorderUsesAtomicTargeting ? originalIndexPath : proposedIndexPath
2086
+ guard !interactiveReorderUsesAtomicTargeting else { return originalIndexPath }
2087
+ updateInteractiveMovementKeys(movingFrom: originalIndexPath, to: proposedIndexPath)
2088
+ return proposedIndexPath
2061
2089
  }
2062
2090
 
2063
2091
  func collectionView(
@@ -2065,7 +2093,7 @@ extension NativeListView: UICollectionViewDelegateFlowLayout {
2065
2093
  layout collectionViewLayout: UICollectionViewLayout,
2066
2094
  sizeForItemAt indexPath: IndexPath
2067
2095
  ) -> CGSize {
2068
- guard let config, let item = item(at: indexPath) else { return .zero }
2096
+ guard let config, let item = layoutItem(at: indexPath) else { return .zero }
2069
2097
  let insets = flowLayout.sectionInset
2070
2098
  if config.orientation == "horizontal" {
2071
2099
  let width: CGFloat = item.type == "rail" ? railWidth(item) : item.type == "mediaTile" ? 200 : 280
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-native-list",
3
- "version": "3.0.134",
3
+ "version": "3.0.135",
4
4
  "description": "Template-driven native RecyclerView and UICollectionView for React Native",
5
5
  "source": "./src/index.ts",
6
6
  "main": "./lib/module/index.js",
@@ -83,8 +83,8 @@
83
83
  "typescript": "^5.9.2"
84
84
  },
85
85
  "peerDependencies": {
86
- "@onekeyfe/react-native-image": "3.0.134",
87
- "@onekeyfe/react-native-native-logger": "3.0.134",
86
+ "@onekeyfe/react-native-image": "3.0.135",
87
+ "@onekeyfe/react-native-native-logger": "3.0.135",
88
88
  "react": "*",
89
89
  "react-native": "*",
90
90
  "react-native-nitro-modules": "0.37.0"