@onekeyfe/react-native-native-list 3.0.108 → 3.0.110

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.
@@ -79,6 +79,7 @@ internal data class NativeListConfig(
79
79
  val endReachedThreshold: Double,
80
80
  val sectionIndexEnabled: Boolean,
81
81
  val sectionIndexHapticsEnabled: Boolean,
82
+ val sectionIndexCenteredInWindow: Boolean,
82
83
  val theme: JSONObject?,
83
84
  val fixedFooter: NativeListItem?,
84
85
  val items: List<NativeListItem>,
@@ -127,6 +128,7 @@ internal data class NativeListConfig(
127
128
  endReachedThreshold = capabilities?.optDouble("endReachedThreshold", 0.2) ?: 0.2,
128
129
  sectionIndexEnabled = sectionIndex?.optBoolean("enabled", false) ?: false,
129
130
  sectionIndexHapticsEnabled = sectionIndex?.optBoolean("hapticsEnabled", true) ?: true,
131
+ sectionIndexCenteredInWindow = sectionIndex?.optBoolean("centeredInWindow", false) ?: false,
130
132
  theme = root.optJSONObject("theme"),
131
133
  fixedFooter = fixedFooter,
132
134
  items = items,
@@ -939,6 +939,7 @@ internal class NativeListRowView(
939
939
  }
940
940
 
941
941
  private fun resetViews() {
942
+ clipChildren = true
942
943
  clipToPadding = true
943
944
  selectorOriginalFontFeatures.forEach { (view, original) -> view.fontFeatureSettings = original }
944
945
  selectorOriginalFontFeatures.clear()
@@ -2494,6 +2495,8 @@ internal class NativeListRowView(
2494
2495
  val visibleSources = sources.take(leadingImages.size)
2495
2496
  val tokenPair = kind == "token" && visibleSources.size > 1
2496
2497
  if (tokenPair) {
2498
+ // The network badge intentionally extends past the avatar frame.
2499
+ clipChildren = false
2497
2500
  leadingOverlayBackground.visibility = VISIBLE
2498
2501
  leadingOverlayBackground.background = roundedFill(visualBackdropColor, 10f)
2499
2502
  leadingOverlayBackground.layoutParams = FrameLayout.LayoutParams(
@@ -21,6 +21,8 @@ import android.widget.FrameLayout
21
21
  import android.widget.LinearLayout
22
22
  import android.widget.SeekBar
23
23
  import android.widget.TextView
24
+ import androidx.core.view.ViewCompat
25
+ import androidx.core.view.WindowInsetsCompat
24
26
  import androidx.recyclerview.widget.GridLayoutManager
25
27
  import androidx.recyclerview.widget.ItemTouchHelper
26
28
  import androidx.recyclerview.widget.LinearLayoutManager
@@ -371,6 +373,7 @@ class NativeListView(
371
373
  previous.endReachedThreshold != next.endReachedThreshold ||
372
374
  previous.sectionIndexEnabled != next.sectionIndexEnabled ||
373
375
  previous.sectionIndexHapticsEnabled != next.sectionIndexHapticsEnabled ||
376
+ previous.sectionIndexCenteredInWindow != next.sectionIndexCenteredInWindow ||
374
377
  previous.theme?.toString() != next.theme?.toString() ||
375
378
  previous.fixedFooter?.content != next.fixedFooter?.content ||
376
379
  previous.items.size != next.items.size
@@ -907,6 +910,7 @@ class NativeListView(
907
910
  themeColor(next.theme, "secondaryText", "#646464"),
908
911
  themeColor(next.theme, "accent", "#108303"),
909
912
  themeColor(next.theme, "inverseText", "#FCFCFC"),
913
+ next.sectionIndexCenteredInWindow,
910
914
  )
911
915
  sectionIndexView.visibility = if (sectionIndexEntries.isEmpty()) GONE else VISIBLE
912
916
  sectionIndexPreview.setTextColor(themeColor(next.theme, "inverseText", "#FCFCFC"))
@@ -1704,7 +1708,10 @@ private class NativeListSectionIndexView(
1704
1708
  private var normalColor = Color.GRAY
1705
1709
  private var activeColor = Color.BLACK
1706
1710
  private var activeTextColor = Color.WHITE
1711
+ private var centeredInWindow = false
1707
1712
  private var lastTouchIndex: Int? = null
1713
+ private val rootLocationOnScreen = IntArray(2)
1714
+ private val locationOnScreen = IntArray(2)
1708
1715
  private val activeBackgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG)
1709
1716
  private val normalPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
1710
1717
  textAlign = Paint.Align.CENTER
@@ -1727,11 +1734,13 @@ private class NativeListSectionIndexView(
1727
1734
  normalColor: Int,
1728
1735
  activeColor: Int,
1729
1736
  activeTextColor: Int,
1737
+ centeredInWindow: Boolean,
1730
1738
  ) {
1731
1739
  this.titles = titles
1732
1740
  this.normalColor = normalColor
1733
1741
  this.activeColor = activeColor
1734
1742
  this.activeTextColor = activeTextColor
1743
+ this.centeredInWindow = centeredInWindow
1735
1744
  activeIndex = null
1736
1745
  updateContentDescription()
1737
1746
  invalidate()
@@ -1748,7 +1757,7 @@ private class NativeListSectionIndexView(
1748
1757
  super.onDraw(canvas)
1749
1758
  if (titles.isEmpty()) return
1750
1759
  val cellHeight = cellHeight()
1751
- val originY = (height - cellHeight * titles.size) / 2f
1760
+ val originY = indexOriginY(cellHeight, titles.size)
1752
1761
  val textSize = NativeListScale.font(resources, 10f) * resources.displayMetrics.scaledDensity
1753
1762
  normalPaint.color = normalColor
1754
1763
  normalPaint.textSize = textSize
@@ -1856,7 +1865,7 @@ private class NativeListSectionIndexView(
1856
1865
 
1857
1866
  private fun selectAt(y: Float, interacting: Boolean) {
1858
1867
  val cellHeight = cellHeight()
1859
- val originY = (height - cellHeight * titles.size) / 2f
1868
+ val originY = indexOriginY(cellHeight, titles.size)
1860
1869
  val index = ((y - originY) / cellHeight).toInt().coerceIn(0, titles.lastIndex)
1861
1870
  if (interacting && lastTouchIndex == index) return
1862
1871
  lastTouchIndex = index.takeIf { interacting }
@@ -1873,6 +1882,24 @@ private class NativeListSectionIndexView(
1873
1882
  .coerceAtMost(NativeListScale.dp(resources, 16f))
1874
1883
  .coerceAtLeast(1f)
1875
1884
 
1885
+ private fun indexOriginY(cellHeight: Float, count: Int): Float {
1886
+ val totalHeight = cellHeight * count
1887
+ val centeredOriginY = (height - totalHeight) / 2f
1888
+ if (!centeredInWindow || !isAttachedToWindow) return centeredOriginY
1889
+ val systemBarInsets = ViewCompat.getRootWindowInsets(rootView)
1890
+ ?.getInsetsIgnoringVisibility(
1891
+ WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout(),
1892
+ ) ?: return centeredOriginY
1893
+ rootView.getLocationOnScreen(rootLocationOnScreen)
1894
+ getLocationOnScreen(locationOnScreen)
1895
+ val safeTop = rootLocationOnScreen[1] + systemBarInsets.top
1896
+ val safeBottom = rootLocationOnScreen[1] + rootView.height - systemBarInsets.bottom
1897
+ if (safeBottom <= safeTop) return centeredOriginY
1898
+ val localCenterY = (safeTop + safeBottom) / 2f - locationOnScreen[1]
1899
+ return (localCenterY - totalHeight / 2f)
1900
+ .coerceIn(0f, (height - totalHeight).coerceAtLeast(0f))
1901
+ }
1902
+
1876
1903
  private fun updateContentDescription() {
1877
1904
  contentDescription = activeIndex?.let { titles.getOrNull(it) }
1878
1905
  ?.let { "$ACCESSIBILITY_LABEL, $it" }
@@ -62,6 +62,7 @@ struct NativeListConfig {
62
62
  let endReachedThreshold: Double
63
63
  let sectionIndexEnabled: Bool
64
64
  let sectionIndexHapticsEnabled: Bool
65
+ let sectionIndexCenteredInWindow: Bool
65
66
  let theme: [String: Any]?
66
67
  let fixedFooter: NativeListItem?
67
68
  var items: [NativeListItem]
@@ -123,6 +124,7 @@ struct NativeListConfig {
123
124
  endReachedThreshold: capabilities?["endReachedThreshold"] as? Double ?? 0.2,
124
125
  sectionIndexEnabled: sectionIndex?["enabled"] as? Bool ?? false,
125
126
  sectionIndexHapticsEnabled: sectionIndex?["hapticsEnabled"] as? Bool ?? true,
127
+ sectionIndexCenteredInWindow: sectionIndex?["centeredInWindow"] as? Bool ?? false,
126
128
  theme: root["theme"] as? [String: Any],
127
129
  fixedFooter: footer,
128
130
  items: items
@@ -873,7 +873,8 @@ final class NativeListView: UIView {
873
873
  titles: sectionIndexEntries.map(\.title),
874
874
  textColor: nativeListColor(config.theme, "secondaryText", "#646464"),
875
875
  activeColor: nativeListColor(config.theme, "accent", "#108303"),
876
- activeTextColor: nativeListColor(config.theme, "inverseText", "#FCFCFC")
876
+ activeTextColor: nativeListColor(config.theme, "inverseText", "#FCFCFC"),
877
+ centeredInWindow: config.sectionIndexCenteredInWindow
877
878
  )
878
879
  sectionIndexView.isHidden = sectionIndexEntries.isEmpty
879
880
  sectionIndexPreview.backgroundColor = nativeListColor(
@@ -1113,6 +1114,7 @@ final class NativeListView: UIView {
1113
1114
  current.endReachedThreshold == next.endReachedThreshold,
1114
1115
  current.sectionIndexEnabled == next.sectionIndexEnabled,
1115
1116
  current.sectionIndexHapticsEnabled == next.sectionIndexHapticsEnabled,
1117
+ current.sectionIndexCenteredInWindow == next.sectionIndexCenteredInWindow,
1116
1118
  dictionariesEqual(current.theme, next.theme),
1117
1119
  current.fixedFooter?.content == next.fixedFooter?.content,
1118
1120
  current.items.count == next.items.count else { return false }
@@ -1756,6 +1758,7 @@ private final class NativeListSectionIndexView: UIControl, UIGestureRecognizerDe
1756
1758
  private var textColor: UIColor = .secondaryLabel
1757
1759
  private var activeColor: UIColor = .tintColor
1758
1760
  private var activeTextColor: UIColor = .white
1761
+ private var centeredInWindow = false
1759
1762
  private var lastTouchIndex: Int?
1760
1763
  private(set) var activeIndex: Int?
1761
1764
 
@@ -1796,12 +1799,14 @@ private final class NativeListSectionIndexView: UIControl, UIGestureRecognizerDe
1796
1799
  titles: [String],
1797
1800
  textColor: UIColor,
1798
1801
  activeColor: UIColor,
1799
- activeTextColor: UIColor
1802
+ activeTextColor: UIColor,
1803
+ centeredInWindow: Bool
1800
1804
  ) {
1801
1805
  self.titles = titles
1802
1806
  self.textColor = textColor
1803
1807
  self.activeColor = activeColor
1804
1808
  self.activeTextColor = activeTextColor
1809
+ self.centeredInWindow = centeredInWindow
1805
1810
  labels.forEach { $0.removeFromSuperview() }
1806
1811
  labels = titles.map { title in
1807
1812
  let label = UILabel()
@@ -1829,7 +1834,7 @@ private final class NativeListSectionIndexView: UIControl, UIGestureRecognizerDe
1829
1834
  super.layoutSubviews()
1830
1835
  guard !labels.isEmpty else { return }
1831
1836
  let height = min(16, max(8, bounds.height / CGFloat(labels.count)))
1832
- let originY = (bounds.height - height * CGFloat(labels.count)) / 2
1837
+ let originY = indexOriginY(cellHeight: height, count: labels.count)
1833
1838
  for (index, label) in labels.enumerated() {
1834
1839
  label.frame = CGRect(
1835
1840
  x: 6,
@@ -1875,13 +1880,24 @@ private final class NativeListSectionIndexView: UIControl, UIGestureRecognizerDe
1875
1880
 
1876
1881
  private func select(at y: CGFloat, interacting: Bool) {
1877
1882
  let height = min(16, max(8, bounds.height / CGFloat(titles.count)))
1878
- let originY = (bounds.height - height * CGFloat(titles.count)) / 2
1883
+ let originY = indexOriginY(cellHeight: height, count: titles.count)
1879
1884
  let index = Int(floor((y - originY) / height)).clamped(to: 0...(titles.count - 1))
1880
1885
  if interacting && lastTouchIndex == index { return }
1881
1886
  lastTouchIndex = interacting ? index : nil
1882
1887
  select(index: index, interacting: interacting)
1883
1888
  }
1884
1889
 
1890
+ private func indexOriginY(cellHeight: CGFloat, count: Int) -> CGFloat {
1891
+ let totalHeight = cellHeight * CGFloat(count)
1892
+ let centeredOriginY = (bounds.height - totalHeight) / 2
1893
+ guard centeredInWindow, let window else { return centeredOriginY }
1894
+ let windowCenterY = window.safeAreaLayoutGuide.layoutFrame.midY
1895
+ let localCenterY = convert(CGPoint(x: 0, y: windowCenterY), from: window).y
1896
+ return (localCenterY - totalHeight / 2).clamped(
1897
+ to: 0...max(0, bounds.height - totalHeight)
1898
+ )
1899
+ }
1900
+
1885
1901
  private func select(index: Int, interacting: Bool) {
1886
1902
  onSelect?(index, interacting)
1887
1903
  setActiveIndex(index)
@@ -392,6 +392,9 @@ export function validateSnapshot(snapshot) {
392
392
  if (sectionIndex.hapticsEnabled !== undefined && typeof sectionIndex.hapticsEnabled !== 'boolean') {
393
393
  fail('snapshot.capabilities.sectionIndex.hapticsEnabled', 'must be a boolean');
394
394
  }
395
+ if (sectionIndex.centeredInWindow !== undefined && typeof sectionIndex.centeredInWindow !== 'boolean') {
396
+ fail('snapshot.capabilities.sectionIndex.centeredInWindow', 'must be a boolean');
397
+ }
395
398
  if (sectionIndex.enabled && (snapshot.layout.kind !== 'sectioned' || snapshot.layout.orientation === 'horizontal')) {
396
399
  fail('snapshot.capabilities.sectionIndex', 'requires a vertical sectioned layout');
397
400
  }
@@ -390,6 +390,7 @@ export type NativeListTheme = Readonly<{
390
390
  export type SectionIndexConfig = Readonly<{
391
391
  enabled: boolean;
392
392
  hapticsEnabled?: boolean;
393
+ centeredInWindow?: boolean;
393
394
  }>;
394
395
  export type NativeListSnapshot = Readonly<{
395
396
  schemaVersion: 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-native-list",
3
- "version": "3.0.108",
3
+ "version": "3.0.110",
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",
@@ -82,7 +82,7 @@
82
82
  "typescript": "^5.9.2"
83
83
  },
84
84
  "peerDependencies": {
85
- "@onekeyfe/react-native-image": "3.0.108",
85
+ "@onekeyfe/react-native-image": "3.0.110",
86
86
  "react": "*",
87
87
  "react-native": "*",
88
88
  "react-native-nitro-modules": "0.37.0"
package/src/models.ts CHANGED
@@ -409,6 +409,7 @@ export type NativeListTheme = Readonly<{
409
409
  export type SectionIndexConfig = Readonly<{
410
410
  enabled: boolean;
411
411
  hapticsEnabled?: boolean;
412
+ centeredInWindow?: boolean;
412
413
  }>;
413
414
 
414
415
  export type NativeListSnapshot = Readonly<{
package/src/validation.ts CHANGED
@@ -698,6 +698,15 @@ export function validateSnapshot(
698
698
  'must be a boolean'
699
699
  );
700
700
  }
701
+ if (
702
+ sectionIndex.centeredInWindow !== undefined &&
703
+ typeof sectionIndex.centeredInWindow !== 'boolean'
704
+ ) {
705
+ fail(
706
+ 'snapshot.capabilities.sectionIndex.centeredInWindow',
707
+ 'must be a boolean'
708
+ );
709
+ }
701
710
  if (
702
711
  sectionIndex.enabled &&
703
712
  (snapshot.layout.kind !== 'sectioned' ||