@onekeyfe/react-native-native-list 3.0.123 → 3.0.124
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 +2 -0
- package/android/src/main/java/com/onekey/nativelist/NativeListView.kt +79 -28
- package/ios/NativeListCell.swift +23 -0
- package/ios/RNCNativeListView.swift +79 -20
- package/lib/module/web/NativeListWebEngine.js +57 -29
- package/lib/typescript/src/web/NativeListWebEngine.d.ts +1 -0
- package/package.json +4 -4
- package/src/web/NativeListWebEngine.ts +87 -33
package/README.md
CHANGED
|
@@ -155,6 +155,8 @@ order, skips headers without `indexTitle`, and scrolls to the header's existing
|
|
|
155
155
|
row `key`. Titles must be unique after Unicode NFC normalization, contain no
|
|
156
156
|
leading/trailing whitespace, and be at most eight Unicode code points. Set
|
|
157
157
|
`hapticsEnabled: false` to follow an application-level reduced-haptics setting.
|
|
158
|
+
Set `centeredInWindow: true` to render the interactive index rail against the
|
|
159
|
+
full window and center it on the window's geometric midpoint.
|
|
158
160
|
The web implementation renders the same explicit index entries as a DOM
|
|
159
161
|
overlay, with click and pointer-drag navigation.
|
|
160
162
|
|
|
@@ -18,14 +18,13 @@ import android.view.Gravity
|
|
|
18
18
|
import android.view.MotionEvent
|
|
19
19
|
import android.view.View
|
|
20
20
|
import android.view.ViewConfiguration
|
|
21
|
+
import android.view.ViewGroup
|
|
21
22
|
import android.view.accessibility.AccessibilityEvent
|
|
22
23
|
import android.view.accessibility.AccessibilityNodeInfo
|
|
23
24
|
import android.widget.FrameLayout
|
|
24
25
|
import android.widget.LinearLayout
|
|
25
26
|
import android.widget.SeekBar
|
|
26
27
|
import android.widget.TextView
|
|
27
|
-
import androidx.core.view.ViewCompat
|
|
28
|
-
import androidx.core.view.WindowInsetsCompat
|
|
29
28
|
import androidx.recyclerview.widget.GridLayoutManager
|
|
30
29
|
import androidx.recyclerview.widget.ItemTouchHelper
|
|
31
30
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
@@ -168,6 +167,8 @@ class NativeListView(
|
|
|
168
167
|
private var sectionIndexScrubbing = false
|
|
169
168
|
private var sectionIndexProgrammaticScroll = false
|
|
170
169
|
private var sectionIndexHapticsEnabled = true
|
|
170
|
+
private val sectionIndexLocationOnScreen = IntArray(2)
|
|
171
|
+
private val sectionIndexHostLocationOnScreen = IntArray(2)
|
|
171
172
|
private var pendingScrollRequest: ScrollRequest? = null
|
|
172
173
|
private val actionAnchorInstanceId = UUID.randomUUID().toString()
|
|
173
174
|
private var actionAnchorCounter = 0L
|
|
@@ -304,6 +305,7 @@ class NativeListView(
|
|
|
304
305
|
lastLayoutWidth = nextWidth
|
|
305
306
|
lastLayoutHeight = nextHeight
|
|
306
307
|
lastLayoutDirection = layoutDirection
|
|
308
|
+
updateSectionIndexAttachment()
|
|
307
309
|
performPendingScrollIfNeeded()
|
|
308
310
|
}
|
|
309
311
|
|
|
@@ -318,6 +320,7 @@ class NativeListView(
|
|
|
318
320
|
|
|
319
321
|
override fun onAttachedToWindow() {
|
|
320
322
|
super.onAttachedToWindow()
|
|
323
|
+
updateSectionIndexAttachment()
|
|
321
324
|
val first = config?.items?.firstOrNull()
|
|
322
325
|
if (recyclerView.isLayoutRequested && (first?.type == "market" || first?.json?.optString("presentation") == "market")) {
|
|
323
326
|
relayoutContents()
|
|
@@ -325,6 +328,7 @@ class NativeListView(
|
|
|
325
328
|
}
|
|
326
329
|
|
|
327
330
|
override fun onDetachedFromWindow() {
|
|
331
|
+
attachSectionIndexToList()
|
|
328
332
|
updateRefreshIndicatorOffset(0)
|
|
329
333
|
super.onDetachedFromWindow()
|
|
330
334
|
}
|
|
@@ -950,6 +954,7 @@ class NativeListView(
|
|
|
950
954
|
|
|
951
955
|
fun dispose() {
|
|
952
956
|
if (disposed) return
|
|
957
|
+
attachSectionIndexToList()
|
|
953
958
|
stopReorderRelayoutLoop()
|
|
954
959
|
invalidateActionAnchor("destroy")
|
|
955
960
|
actionAnchor = null
|
|
@@ -1024,9 +1029,9 @@ class NativeListView(
|
|
|
1024
1029
|
themeColor(next.theme, "disabledText", "#8D8D8D"),
|
|
1025
1030
|
themeColor(next.theme, "positive", "#218358"),
|
|
1026
1031
|
themeColor(next.theme, "inverseText", "#FCFCFC"),
|
|
1027
|
-
next.sectionIndexCenteredInWindow,
|
|
1028
1032
|
)
|
|
1029
1033
|
sectionIndexView.visibility = if (sectionIndexEntries.isEmpty()) GONE else VISIBLE
|
|
1034
|
+
updateSectionIndexAttachment()
|
|
1030
1035
|
sectionIndexPreview.setTextColor(Color.WHITE)
|
|
1031
1036
|
sectionIndexPreview.background = sectionIndexPreviewBackground()
|
|
1032
1037
|
sectionIndexView.setActiveIndex(
|
|
@@ -1078,11 +1083,75 @@ class NativeListView(
|
|
|
1078
1083
|
private fun positionSectionIndexPreview(index: Int) {
|
|
1079
1084
|
val halfHeight = sectionIndexDp(SECTION_INDEX_PREVIEW_HEIGHT_DP) / 2f
|
|
1080
1085
|
val maximumY = (contentContainer.height - halfHeight).coerceAtLeast(halfHeight)
|
|
1081
|
-
|
|
1086
|
+
sectionIndexView.getLocationOnScreen(sectionIndexLocationOnScreen)
|
|
1087
|
+
contentContainer.getLocationOnScreen(sectionIndexHostLocationOnScreen)
|
|
1088
|
+
val targetY = sectionIndexLocationOnScreen[1] - sectionIndexHostLocationOnScreen[1] +
|
|
1089
|
+
sectionIndexView.centerYForIndex(index)
|
|
1082
1090
|
val clampedY = targetY.coerceIn(halfHeight, maximumY)
|
|
1083
1091
|
sectionIndexPreview.translationY = clampedY - contentContainer.height / 2f
|
|
1084
1092
|
}
|
|
1085
1093
|
|
|
1094
|
+
private fun updateSectionIndexAttachment() {
|
|
1095
|
+
val windowHost = rootView as? FrameLayout
|
|
1096
|
+
val useWindowHost = config?.sectionIndexCenteredInWindow == true &&
|
|
1097
|
+
sectionIndexEntries.isNotEmpty() &&
|
|
1098
|
+
isAttachedToWindow &&
|
|
1099
|
+
isShown &&
|
|
1100
|
+
width > 0 &&
|
|
1101
|
+
height > 0 &&
|
|
1102
|
+
windowHost != null &&
|
|
1103
|
+
windowHost.width > 0 &&
|
|
1104
|
+
windowHost.height > 0 &&
|
|
1105
|
+
windowHost !== contentContainer
|
|
1106
|
+
if (useWindowHost) {
|
|
1107
|
+
attachSectionIndexToWindow(windowHost)
|
|
1108
|
+
} else {
|
|
1109
|
+
attachSectionIndexToList()
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
private fun attachSectionIndexToList() {
|
|
1114
|
+
if (sectionIndexView.parent === contentContainer) return
|
|
1115
|
+
(sectionIndexView.parent as? ViewGroup)?.removeView(sectionIndexView)
|
|
1116
|
+
contentContainer.addView(
|
|
1117
|
+
sectionIndexView,
|
|
1118
|
+
FrameLayout.LayoutParams(
|
|
1119
|
+
sectionIndexDp(SECTION_INDEX_RAIL_WIDTH_DP),
|
|
1120
|
+
FrameLayout.LayoutParams.MATCH_PARENT,
|
|
1121
|
+
Gravity.END,
|
|
1122
|
+
),
|
|
1123
|
+
)
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
private fun attachSectionIndexToWindow(windowHost: FrameLayout) {
|
|
1127
|
+
val railWidth = sectionIndexDp(SECTION_INDEX_RAIL_WIDTH_DP)
|
|
1128
|
+
val railHeight = sectionIndexView.preferredHeight(windowHost.height)
|
|
1129
|
+
windowHost.getLocationOnScreen(sectionIndexHostLocationOnScreen)
|
|
1130
|
+
getLocationOnScreen(sectionIndexLocationOnScreen)
|
|
1131
|
+
val listLeft = sectionIndexLocationOnScreen[0] - sectionIndexHostLocationOnScreen[0]
|
|
1132
|
+
val maximumEndMargin = (windowHost.width - railWidth).coerceAtLeast(0)
|
|
1133
|
+
val endMargin = if (layoutDirection == LAYOUT_DIRECTION_RTL) {
|
|
1134
|
+
listLeft
|
|
1135
|
+
} else {
|
|
1136
|
+
windowHost.width - listLeft - width
|
|
1137
|
+
}.coerceIn(0, maximumEndMargin)
|
|
1138
|
+
val layoutParams = FrameLayout.LayoutParams(
|
|
1139
|
+
railWidth,
|
|
1140
|
+
railHeight,
|
|
1141
|
+
Gravity.TOP or Gravity.END,
|
|
1142
|
+
).apply {
|
|
1143
|
+
marginEnd = endMargin
|
|
1144
|
+
topMargin = (windowHost.height - railHeight) / 2
|
|
1145
|
+
}
|
|
1146
|
+
if (sectionIndexView.parent !== windowHost) {
|
|
1147
|
+
(sectionIndexView.parent as? ViewGroup)?.removeView(sectionIndexView)
|
|
1148
|
+
windowHost.addView(sectionIndexView, layoutParams)
|
|
1149
|
+
sectionIndexView.bringToFront()
|
|
1150
|
+
} else {
|
|
1151
|
+
sectionIndexView.layoutParams = layoutParams
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1086
1155
|
private fun sectionIndexPreviewBackground(): ShapeDrawable {
|
|
1087
1156
|
val path = Path().apply {
|
|
1088
1157
|
moveTo(25f, 0f)
|
|
@@ -2108,10 +2177,7 @@ private class NativeListSectionIndexView(
|
|
|
2108
2177
|
private var normalColor = Color.GRAY
|
|
2109
2178
|
private var activeColor = Color.BLACK
|
|
2110
2179
|
private var activeTextColor = Color.WHITE
|
|
2111
|
-
private var centeredInWindow = false
|
|
2112
2180
|
private var lastTouchIndex: Int? = null
|
|
2113
|
-
private val rootLocationOnScreen = IntArray(2)
|
|
2114
|
-
private val locationOnScreen = IntArray(2)
|
|
2115
2181
|
private val activeBackgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG)
|
|
2116
2182
|
private val normalPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
|
2117
2183
|
textAlign = Paint.Align.CENTER
|
|
@@ -2134,13 +2200,11 @@ private class NativeListSectionIndexView(
|
|
|
2134
2200
|
normalColor: Int,
|
|
2135
2201
|
activeColor: Int,
|
|
2136
2202
|
activeTextColor: Int,
|
|
2137
|
-
centeredInWindow: Boolean,
|
|
2138
2203
|
) {
|
|
2139
2204
|
this.titles = titles
|
|
2140
2205
|
this.normalColor = normalColor
|
|
2141
2206
|
this.activeColor = activeColor
|
|
2142
2207
|
this.activeTextColor = activeTextColor
|
|
2143
|
-
this.centeredInWindow = centeredInWindow
|
|
2144
2208
|
activeIndex = null
|
|
2145
2209
|
updateContentDescription()
|
|
2146
2210
|
invalidate()
|
|
@@ -2271,6 +2335,11 @@ private class NativeListSectionIndexView(
|
|
|
2271
2335
|
|
|
2272
2336
|
fun centerYForIndex(index: Int): Float = entryCenterY(index, indexMetrics())
|
|
2273
2337
|
|
|
2338
|
+
fun preferredHeight(maximumHeight: Int): Int = minOf(
|
|
2339
|
+
maximumHeight,
|
|
2340
|
+
(dp(8f) * 2f + dp(16f) * titles.size).roundToInt(),
|
|
2341
|
+
)
|
|
2342
|
+
|
|
2274
2343
|
private data class Metrics(val originY: Float, val trackHeight: Float)
|
|
2275
2344
|
|
|
2276
2345
|
private fun indexMetrics(): Metrics {
|
|
@@ -2279,25 +2348,7 @@ private class NativeListSectionIndexView(
|
|
|
2279
2348
|
val labelSpacing = dp(16f)
|
|
2280
2349
|
val availableHeight = (height - edgePadding * 2f).coerceAtLeast(0f)
|
|
2281
2350
|
val trackHeight = minOf(availableHeight, labelSpacing * titles.size)
|
|
2282
|
-
|
|
2283
|
-
if (!centeredInWindow || !isAttachedToWindow) {
|
|
2284
|
-
return Metrics(centeredOriginY, trackHeight)
|
|
2285
|
-
}
|
|
2286
|
-
val systemBarInsets = ViewCompat.getRootWindowInsets(rootView)
|
|
2287
|
-
?.getInsetsIgnoringVisibility(
|
|
2288
|
-
WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout(),
|
|
2289
|
-
) ?: return Metrics(centeredOriginY, trackHeight)
|
|
2290
|
-
rootView.getLocationOnScreen(rootLocationOnScreen)
|
|
2291
|
-
getLocationOnScreen(locationOnScreen)
|
|
2292
|
-
val safeTop = rootLocationOnScreen[1] + systemBarInsets.top
|
|
2293
|
-
val safeBottom = rootLocationOnScreen[1] + rootView.height - systemBarInsets.bottom
|
|
2294
|
-
if (safeBottom <= safeTop) return Metrics(centeredOriginY, trackHeight)
|
|
2295
|
-
val localCenterY = (safeTop + safeBottom) / 2f - locationOnScreen[1]
|
|
2296
|
-
val maximumOrigin = (height - edgePadding - trackHeight).coerceAtLeast(edgePadding)
|
|
2297
|
-
return Metrics(
|
|
2298
|
-
(localCenterY - trackHeight / 2f).coerceIn(edgePadding, maximumOrigin),
|
|
2299
|
-
trackHeight,
|
|
2300
|
-
)
|
|
2351
|
+
return Metrics((height - trackHeight) / 2f, trackHeight)
|
|
2301
2352
|
}
|
|
2302
2353
|
|
|
2303
2354
|
private fun entryCenterY(index: Int, metrics: Metrics): Float {
|
package/ios/NativeListCell.swift
CHANGED
|
@@ -100,6 +100,15 @@ final class NativeListActionOrigin {
|
|
|
100
100
|
|
|
101
101
|
// OneKey patch: explicit summary actions use the source text's physical-pixel line box.
|
|
102
102
|
private final class NativeListAccessoryButton: UIButton {
|
|
103
|
+
var pressedBackgroundColor: UIColor?
|
|
104
|
+
|
|
105
|
+
override var isHighlighted: Bool {
|
|
106
|
+
didSet {
|
|
107
|
+
guard let pressedBackgroundColor else { return }
|
|
108
|
+
backgroundColor = isHighlighted ? pressedBackgroundColor : .clear
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
103
112
|
var selectorSummaryLineHeight: CGFloat? {
|
|
104
113
|
didSet { invalidateIntrinsicContentSize(); setNeedsLayout() }
|
|
105
114
|
}
|
|
@@ -1373,6 +1382,7 @@ final class NativeListCell: UICollectionViewCell {
|
|
|
1373
1382
|
button.setImage(nil, for: .disabled)
|
|
1374
1383
|
button.isEnabled = true
|
|
1375
1384
|
button.alpha = 1
|
|
1385
|
+
button.pressedBackgroundColor = nil
|
|
1376
1386
|
button.titleLabel?.numberOfLines = 1
|
|
1377
1387
|
button.contentHorizontalAlignment = .center
|
|
1378
1388
|
button.backgroundColor = .clear
|
|
@@ -1924,6 +1934,15 @@ final class NativeListCell: UICollectionViewCell {
|
|
|
1924
1934
|
trailingStack.spacing = 10
|
|
1925
1935
|
}
|
|
1926
1936
|
bindAccessories(item, accessories, theme, checkboxState)
|
|
1937
|
+
if item.data.string("presentation") == "accountSelector",
|
|
1938
|
+
accessories.count == 1,
|
|
1939
|
+
let accessory = accessoryButtons.first(where: { !$0.isHidden }) {
|
|
1940
|
+
// The vertical trailing stack has no stable intrinsic width, so keep its
|
|
1941
|
+
// flexible space in the account title and subtitle column.
|
|
1942
|
+
let width = trailingStack.widthAnchor.constraint(equalTo: accessory.widthAnchor)
|
|
1943
|
+
width.isActive = true
|
|
1944
|
+
selectorConstraints.append(width)
|
|
1945
|
+
}
|
|
1927
1946
|
}
|
|
1928
1947
|
|
|
1929
1948
|
private func bindRail(_ item: NativeListItem, theme: [String: Any]?) {
|
|
@@ -3965,6 +3984,10 @@ final class NativeListCell: UICollectionViewCell {
|
|
|
3965
3984
|
let isAccountIcon = currentItem?.data.string("presentation") == "accountSelector" && !isDrillIn
|
|
3966
3985
|
let size: CGFloat = isDrillIn ? 24 : isAccountIcon && !isAccountCreate ? 38 : 36
|
|
3967
3986
|
if isAccountCreate { button.layer.cornerRadius = 8 }
|
|
3987
|
+
if isAccountIcon && !isAccountCreate && !data.string("actionKey").isEmpty {
|
|
3988
|
+
button.pressedBackgroundColor = nativeListColor(currentTheme, "rowPressedBackground", "#E8E8E8")
|
|
3989
|
+
button.layer.cornerRadius = size / 2
|
|
3990
|
+
}
|
|
3968
3991
|
if isAccountIcon { rootStack.setCustomSpacing(5, after: mainStack) }
|
|
3969
3992
|
button.accessibilityIdentifier = data["testID"] as? String
|
|
3970
3993
|
button.accessibilityLabel = data["accessibilityLabel"] as? String
|
|
@@ -65,6 +65,8 @@ final class NativeListView: UIView {
|
|
|
65
65
|
private let footerCell = NativeListCell(frame: .zero)
|
|
66
66
|
private let sectionIndexView = NativeListSectionIndexView()
|
|
67
67
|
private let sectionIndexPreview = NativeListSectionIndexPreviewView()
|
|
68
|
+
private var sectionIndexLayoutConstraints: [NSLayoutConstraint] = []
|
|
69
|
+
private var sectionIndexWindowHeightConstraint: NSLayoutConstraint?
|
|
68
70
|
private var footerHeightConstraint: NSLayoutConstraint!
|
|
69
71
|
private var dataSource: UICollectionViewDiffableDataSource<Int, String>!
|
|
70
72
|
private var config: NativeListConfig?
|
|
@@ -161,10 +163,6 @@ final class NativeListView: UIView {
|
|
|
161
163
|
footerContainer.trailingAnchor.constraint(equalTo: trailingAnchor),
|
|
162
164
|
footerContainer.bottomAnchor.constraint(equalTo: bottomAnchor),
|
|
163
165
|
footerHeightConstraint,
|
|
164
|
-
sectionIndexView.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor),
|
|
165
|
-
sectionIndexView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor),
|
|
166
|
-
sectionIndexView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor),
|
|
167
|
-
sectionIndexView.widthAnchor.constraint(equalToConstant: Self.sectionIndexRailWidth),
|
|
168
166
|
sectionIndexPreview.trailingAnchor.constraint(
|
|
169
167
|
equalTo: safeAreaLayoutGuide.trailingAnchor,
|
|
170
168
|
constant: -Self.sectionIndexPreviewEndMargin
|
|
@@ -173,6 +171,7 @@ final class NativeListView: UIView {
|
|
|
173
171
|
sectionIndexPreview.widthAnchor.constraint(equalToConstant: Self.sectionIndexPreviewWidth),
|
|
174
172
|
sectionIndexPreview.heightAnchor.constraint(equalToConstant: Self.sectionIndexPreviewHeight),
|
|
175
173
|
])
|
|
174
|
+
attachSectionIndexToList()
|
|
176
175
|
|
|
177
176
|
sectionIndexView.isHidden = true
|
|
178
177
|
sectionIndexView.onSelect = { [weak self] index, interacting in
|
|
@@ -233,8 +232,19 @@ final class NativeListView: UIView {
|
|
|
233
232
|
fatalError("init(coder:) has not been implemented")
|
|
234
233
|
}
|
|
235
234
|
|
|
235
|
+
override func willMove(toWindow newWindow: UIWindow?) {
|
|
236
|
+
if newWindow == nil { attachSectionIndexToList() }
|
|
237
|
+
super.willMove(toWindow: newWindow)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
override func didMoveToWindow() {
|
|
241
|
+
super.didMoveToWindow()
|
|
242
|
+
updateSectionIndexAttachment()
|
|
243
|
+
}
|
|
244
|
+
|
|
236
245
|
override func layoutSubviews() {
|
|
237
246
|
super.layoutSubviews()
|
|
247
|
+
updateSectionIndexAttachment()
|
|
238
248
|
let direction = effectiveUserInterfaceLayoutDirection
|
|
239
249
|
if let lastLayoutSize,
|
|
240
250
|
lastLayoutSize != bounds.size || lastLayoutDirection != nil && lastLayoutDirection != direction {
|
|
@@ -1027,10 +1037,10 @@ final class NativeListView: UIView {
|
|
|
1027
1037
|
titles: sectionIndexEntries.map(\.title),
|
|
1028
1038
|
textColor: nativeListColor(config.theme, "disabledText", "#8D8D8D"),
|
|
1029
1039
|
activeColor: nativeListColor(config.theme, "positive", "#218358"),
|
|
1030
|
-
activeTextColor: nativeListColor(config.theme, "inverseText", "#FCFCFC")
|
|
1031
|
-
centeredInWindow: config.sectionIndexCenteredInWindow
|
|
1040
|
+
activeTextColor: nativeListColor(config.theme, "inverseText", "#FCFCFC")
|
|
1032
1041
|
)
|
|
1033
1042
|
sectionIndexView.isHidden = sectionIndexEntries.isEmpty
|
|
1043
|
+
updateSectionIndexAttachment()
|
|
1034
1044
|
sectionIndexPreview.configure(
|
|
1035
1045
|
fillColor: UIColor(
|
|
1036
1046
|
red: 202.0 / 255.0,
|
|
@@ -1049,6 +1059,63 @@ final class NativeListView: UIView {
|
|
|
1049
1059
|
if sectionIndexEntries.isEmpty { finishSectionIndexInteraction(immediately: true) }
|
|
1050
1060
|
}
|
|
1051
1061
|
|
|
1062
|
+
private func updateSectionIndexAttachment() {
|
|
1063
|
+
guard config?.sectionIndexCenteredInWindow == true,
|
|
1064
|
+
!sectionIndexEntries.isEmpty,
|
|
1065
|
+
bounds.width > 0,
|
|
1066
|
+
bounds.height > 0,
|
|
1067
|
+
isVisibleInHierarchy,
|
|
1068
|
+
let window else {
|
|
1069
|
+
attachSectionIndexToList()
|
|
1070
|
+
return
|
|
1071
|
+
}
|
|
1072
|
+
attachSectionIndex(to: window)
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
private var isVisibleInHierarchy: Bool {
|
|
1076
|
+
var current: UIView? = self
|
|
1077
|
+
while let view = current {
|
|
1078
|
+
if view.isHidden || view.alpha <= 0.01 { return false }
|
|
1079
|
+
current = view.superview
|
|
1080
|
+
}
|
|
1081
|
+
return true
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
private func attachSectionIndexToList() {
|
|
1085
|
+
guard sectionIndexView.superview !== self || sectionIndexLayoutConstraints.isEmpty else { return }
|
|
1086
|
+
NSLayoutConstraint.deactivate(sectionIndexLayoutConstraints)
|
|
1087
|
+
sectionIndexWindowHeightConstraint = nil
|
|
1088
|
+
sectionIndexView.removeFromSuperview()
|
|
1089
|
+
addSubview(sectionIndexView)
|
|
1090
|
+
sectionIndexLayoutConstraints = [
|
|
1091
|
+
sectionIndexView.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor),
|
|
1092
|
+
sectionIndexView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor),
|
|
1093
|
+
sectionIndexView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor),
|
|
1094
|
+
sectionIndexView.widthAnchor.constraint(equalToConstant: Self.sectionIndexRailWidth),
|
|
1095
|
+
]
|
|
1096
|
+
NSLayoutConstraint.activate(sectionIndexLayoutConstraints)
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
private func attachSectionIndex(to window: UIWindow) {
|
|
1100
|
+
let railHeight = sectionIndexView.preferredHeight(constrainedTo: window.bounds.height)
|
|
1101
|
+
if sectionIndexView.superview === window {
|
|
1102
|
+
sectionIndexWindowHeightConstraint?.constant = railHeight
|
|
1103
|
+
return
|
|
1104
|
+
}
|
|
1105
|
+
NSLayoutConstraint.deactivate(sectionIndexLayoutConstraints)
|
|
1106
|
+
sectionIndexView.removeFromSuperview()
|
|
1107
|
+
window.addSubview(sectionIndexView)
|
|
1108
|
+
let heightConstraint = sectionIndexView.heightAnchor.constraint(equalToConstant: railHeight)
|
|
1109
|
+
sectionIndexWindowHeightConstraint = heightConstraint
|
|
1110
|
+
sectionIndexLayoutConstraints = [
|
|
1111
|
+
sectionIndexView.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor),
|
|
1112
|
+
sectionIndexView.centerYAnchor.constraint(equalTo: window.centerYAnchor),
|
|
1113
|
+
sectionIndexView.widthAnchor.constraint(equalToConstant: Self.sectionIndexRailWidth),
|
|
1114
|
+
heightConstraint,
|
|
1115
|
+
]
|
|
1116
|
+
NSLayoutConstraint.activate(sectionIndexLayoutConstraints)
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1052
1119
|
private func selectSectionIndex(_ index: Int, interacting: Bool) {
|
|
1053
1120
|
guard let entry = sectionIndexEntries[safe: index] else { return }
|
|
1054
1121
|
let changed = sectionIndexView.activeIndex != index
|
|
@@ -2156,7 +2223,6 @@ private final class NativeListSectionIndexView: UIControl, UIGestureRecognizerDe
|
|
|
2156
2223
|
private var textColor: UIColor = .secondaryLabel
|
|
2157
2224
|
private var activeColor: UIColor = .tintColor
|
|
2158
2225
|
private var activeTextColor: UIColor = .white
|
|
2159
|
-
private var centeredInWindow = false
|
|
2160
2226
|
private var lastTouchIndex: Int?
|
|
2161
2227
|
private(set) var activeIndex: Int?
|
|
2162
2228
|
|
|
@@ -2197,14 +2263,12 @@ private final class NativeListSectionIndexView: UIControl, UIGestureRecognizerDe
|
|
|
2197
2263
|
titles: [String],
|
|
2198
2264
|
textColor: UIColor,
|
|
2199
2265
|
activeColor: UIColor,
|
|
2200
|
-
activeTextColor: UIColor
|
|
2201
|
-
centeredInWindow: Bool
|
|
2266
|
+
activeTextColor: UIColor
|
|
2202
2267
|
) {
|
|
2203
2268
|
self.titles = titles
|
|
2204
2269
|
self.textColor = textColor
|
|
2205
2270
|
self.activeColor = activeColor
|
|
2206
2271
|
self.activeTextColor = activeTextColor
|
|
2207
|
-
self.centeredInWindow = centeredInWindow
|
|
2208
2272
|
labels.forEach { $0.removeFromSuperview() }
|
|
2209
2273
|
labels = titles.map { title in
|
|
2210
2274
|
let label = UILabel()
|
|
@@ -2313,20 +2377,15 @@ private final class NativeListSectionIndexView: UIControl, UIGestureRecognizerDe
|
|
|
2313
2377
|
entryCenterY(index: index, metrics: indexMetrics(count: titles.count))
|
|
2314
2378
|
}
|
|
2315
2379
|
|
|
2380
|
+
func preferredHeight(constrainedTo maximumHeight: CGFloat) -> CGFloat {
|
|
2381
|
+
min(maximumHeight, Self.edgePadding * 2 + Self.labelSpacing * CGFloat(titles.count))
|
|
2382
|
+
}
|
|
2383
|
+
|
|
2316
2384
|
private func indexMetrics(count: Int) -> (originY: CGFloat, trackHeight: CGFloat) {
|
|
2317
2385
|
guard count > 0 else { return (bounds.midY, 0) }
|
|
2318
2386
|
let availableHeight = max(0, bounds.height - Self.edgePadding * 2)
|
|
2319
2387
|
let trackHeight = min(availableHeight, Self.labelSpacing * CGFloat(count))
|
|
2320
|
-
|
|
2321
|
-
guard centeredInWindow, let window else { return (centeredOriginY, trackHeight) }
|
|
2322
|
-
let windowCenterY = window.safeAreaLayoutGuide.layoutFrame.midY
|
|
2323
|
-
let localCenterY = convert(CGPoint(x: 0, y: windowCenterY), from: window).y
|
|
2324
|
-
return (
|
|
2325
|
-
(localCenterY - trackHeight / 2).clamped(
|
|
2326
|
-
to: Self.edgePadding...max(Self.edgePadding, bounds.height - Self.edgePadding - trackHeight)
|
|
2327
|
-
),
|
|
2328
|
-
trackHeight
|
|
2329
|
-
)
|
|
2388
|
+
return ((bounds.height - trackHeight) / 2, trackHeight)
|
|
2330
2389
|
}
|
|
2331
2390
|
|
|
2332
2391
|
private func entryCenterY(
|
|
@@ -10,6 +10,7 @@ const SECTION_INDEX_RAIL_WIDTH = 32;
|
|
|
10
10
|
const SECTION_INDEX_EDGE_PADDING = 8;
|
|
11
11
|
const SECTION_INDEX_LABEL_SPACING = 16;
|
|
12
12
|
const SECTION_INDEX_MIN_HEIGHT = 120;
|
|
13
|
+
const SECTION_INDEX_WINDOW_Z_INDEX = 100_000;
|
|
13
14
|
const DEFAULT_VIEWPORT_WIDTH = 320;
|
|
14
15
|
const DEFAULT_VIEWPORT_HEIGHT = 640;
|
|
15
16
|
const OVERSCAN_VIEWPORTS = 1;
|
|
@@ -467,12 +468,12 @@ export const WEB_LIST_CSS = `
|
|
|
467
468
|
.ok-native-list-item[data-native-list-reorderable="true"]>.ok-native-list-row{cursor:grab}
|
|
468
469
|
.ok-native-list-root[data-native-list-dragging="true"] .ok-native-list-row{cursor:grabbing}
|
|
469
470
|
.ok-native-list-item[data-native-list-animate-reorder="true"]{transition:transform ${WEB_REORDER_ANIMATION.outOfWayDurationMs}ms ${WEB_REORDER_ANIMATION.outOfWayTimingFunction},height ${WEB_REORDER_ANIMATION.outOfWayDurationMs}ms ${WEB_REORDER_ANIMATION.outOfWayTimingFunction}}
|
|
470
|
-
.ok-native-list-item[data-native-list-dragging="true"]>.ok-native-list-row{overflow:hidden;border-radius:12px;background:var(--nl-
|
|
471
|
+
.ok-native-list-item[data-native-list-dragging="true"]>.ok-native-list-row{overflow:hidden;border-radius:12px;background:var(--nl-pressed)}
|
|
471
472
|
.ok-native-list-item[data-native-list-dragging="true"]>.ok-native-list-row>*{visibility:hidden}
|
|
472
|
-
.ok-native-list-item[data-native-list-dragging="true"]>.ok-native-list-wallet-group{border-color:transparent;background:var(--nl-
|
|
473
|
+
.ok-native-list-item[data-native-list-dragging="true"]>.ok-native-list-wallet-group{border-color:transparent;background:var(--nl-pressed)}
|
|
473
474
|
.ok-native-list-item[data-native-list-dragging="true"]>.ok-native-list-wallet-group>*{visibility:hidden}
|
|
474
475
|
.ok-native-list-reorder-preview{position:fixed;left:0;top:0;z-index:100001;pointer-events:none;overflow:hidden;border-radius:12px;box-shadow:0 4px 24px rgba(0,0,0,.12);transform-origin:center;will-change:transform;font-family:Roobert,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
|
|
475
|
-
.ok-native-list-reorder-preview>.ok-native-list-row{background:var(--nl-
|
|
476
|
+
.ok-native-list-reorder-preview>.ok-native-list-row{background:var(--nl-pressed);cursor:grabbing}
|
|
476
477
|
.ok-native-list-reorder-preview[data-native-list-selected="true"]>.ok-native-list-row{background:var(--nl-selected)}
|
|
477
478
|
.ok-native-list-reorder-count{position:absolute;right:4px;bottom:4px;display:flex;align-items:center;justify-content:center;box-sizing:border-box;min-width:24px;height:24px;padding:0 6px;border:1px solid var(--nl-row);border-radius:12px;background:var(--nl-inverse);color:var(--nl-inverse-text);font-size:12px;line-height:22px;font-weight:600}
|
|
478
479
|
.ok-native-list-item[data-separator="true"]>.ok-native-list-row{border-bottom:1px solid var(--nl-separator)}
|
|
@@ -504,7 +505,7 @@ export const WEB_LIST_CSS = `
|
|
|
504
505
|
.ok-native-list-subtitle-segments{display:flex;align-items:center;min-width:0;max-width:100%;height:20px}.ok-native-list-subtitle-segments>.ok-native-list-secondary{flex:0 1 auto;min-width:0}.ok-native-list-subtitle-dot{flex:0 0 4px;width:4px;height:4px;margin:0 6px;border-radius:50%;background:var(--nl-disabled)}.ok-native-list-wallet-row>.ok-native-list-flex{flex:0 1 auto;width:100%;align-items:center}.ok-native-list-wallet-badges{display:flex;gap:4px;justify-content:center;margin-top:4px;height:20px;max-width:100%}.ok-native-list-wallet-badges>.ok-native-list-badge{background:var(--nl-strong);color:var(--nl-secondary);font-size:12px;line-height:16px;height:20px;box-sizing:border-box;padding:2px 4px}.ok-native-list-visual-overlay{position:absolute;display:flex;align-items:center;justify-content:center;box-sizing:border-box;border-radius:50%;overflow:hidden;line-height:1;font-size:10px}.ok-native-list-visual-overlay img,.ok-native-list-visual-overlay svg{width:100%;height:100%;object-fit:contain}
|
|
505
506
|
.ok-native-list-row.ok-native-list-market-skeleton{padding:12px 20px;gap:0}.ok-native-list-skeleton-left{display:flex;align-items:center;gap:12px;flex:1}.ok-native-list-skeleton-text{display:flex;flex-direction:column;gap:4px}.ok-native-list-skeleton-right{display:flex;align-items:center;gap:8px}.ok-native-list-skeleton-mark{display:block;flex-shrink:0;border-radius:8px;animation:ok-native-list-skeleton 1.5s linear infinite alternate}@keyframes ok-native-list-skeleton{from{background-color:var(--nl-skeleton-base)}to{background-color:var(--nl-skeleton-highlight)}}.ok-native-list-market-spinner{display:block;width:20px;height:20px;flex-shrink:0;color:var(--nl-icon);animation:ok-native-list-spin .75s linear infinite}
|
|
506
507
|
@media (prefers-reduced-motion:reduce){.ok-native-list-index-preview,.ok-native-list-refresh{transition:none}.ok-native-list-spinner,.ok-native-list-market-spinner{animation:none}.ok-native-list-skeleton-mark{animation:none;background:var(--nl-skeleton-base)}}
|
|
507
|
-
.ok-native-list-footer{flex:0 0 auto;min-height:0}.ok-native-list-sticky{position:absolute;z-index:4;left:0;right:0;top:0;pointer-events:auto;box-shadow:0 1px 0 var(--nl-separator)}.ok-native-list-viewport-frame:has(>.ok-native-list-index-rail:not([hidden]))>.ok-native-list-viewport{scrollbar-width:none}.ok-native-list-viewport-frame:has(>.ok-native-list-index-rail:not([hidden]))>.ok-native-list-viewport::-webkit-scrollbar{display:none}.ok-native-list-index-rail{position:absolute;z-index:6;top:0;right:0;bottom:0;width:${SECTION_INDEX_RAIL_WIDTH}px;touch-action:none;cursor:pointer}.ok-native-list-index-rail[hidden]{display:none}.ok-native-list-index-button{appearance:none;position:absolute;left:15px;display:flex;width:14px;height:14px;align-items:center;justify-content:center;padding:0;transform:translateY(-50%);border:0;border-radius:7px;background:transparent;color:var(--nl-disabled);font-family:inherit;font-size:10px;font-weight:400;line-height:1;cursor:pointer}.ok-native-list-index-button[data-active="true"]{background:var(--nl-positive);color:var(--nl-inverse-text);font-weight:500}.ok-native-list-index-button:focus-visible{outline:2px solid var(--nl-positive);outline-offset:1px}
|
|
508
|
+
.ok-native-list-footer{flex:0 0 auto;min-height:0}.ok-native-list-sticky{position:absolute;z-index:4;left:0;right:0;top:0;pointer-events:auto;box-shadow:0 1px 0 var(--nl-separator)}.ok-native-list-viewport-frame:has(>.ok-native-list-index-rail:not([hidden]))>.ok-native-list-viewport,.ok-native-list-viewport-frame[data-section-index-visible="true"]>.ok-native-list-viewport{scrollbar-width:none}.ok-native-list-viewport-frame:has(>.ok-native-list-index-rail:not([hidden]))>.ok-native-list-viewport::-webkit-scrollbar,.ok-native-list-viewport-frame[data-section-index-visible="true"]>.ok-native-list-viewport::-webkit-scrollbar{display:none}.ok-native-list-index-rail{position:absolute;z-index:6;top:0;right:0;bottom:0;width:${SECTION_INDEX_RAIL_WIDTH}px;touch-action:none;cursor:pointer;font-family:Roobert,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.ok-native-list-index-rail[hidden]{display:none}.ok-native-list-index-button{appearance:none;position:absolute;left:15px;display:flex;width:14px;height:14px;align-items:center;justify-content:center;padding:0;transform:translateY(-50%);border:0;border-radius:7px;background:transparent;color:var(--nl-disabled);font-family:inherit;font-size:10px;font-weight:400;line-height:1;cursor:pointer}.ok-native-list-index-button[data-active="true"]{background:var(--nl-positive);color:var(--nl-inverse-text);font-weight:500}.ok-native-list-index-button:focus-visible{outline:2px solid var(--nl-positive);outline-offset:1px}
|
|
508
509
|
.ok-native-list-refresh{position:absolute;z-index:7;left:50%;top:8px;display:flex;align-items:center;gap:6px;padding:6px 10px;border-radius:999px;background:var(--nl-inverse);color:var(--nl-inverse-text);font-size:12px;opacity:0;transform:translate(-50%,-16px);transition:opacity .15s ease,transform .15s ease;pointer-events:none}.ok-native-list-refresh[data-visible="true"]{opacity:1;transform:translate(-50%,0)}
|
|
509
510
|
.ok-native-list-warning{height:auto;display:flex;flex-direction:column;align-items:stretch;gap:4px;padding:14px 12px;border-top:1px solid;border-bottom:1px solid;box-sizing:border-box;cursor:default}.ok-native-list-warning-title,.ok-native-list-warning-message{font-size:14px;line-height:20px;white-space:normal;overflow-wrap:anywhere}.ok-native-list-warning-title{font-weight:500;color:var(--nl-primary)}.ok-native-list-warning-message{font-weight:400;color:var(--nl-secondary)}
|
|
510
511
|
.ok-native-list-subtitle-segments{display:flex;align-items:center;min-width:0;max-width:100%;height:20px}.ok-native-list-subtitle-segments>.ok-native-list-secondary{flex:0 1 auto;min-width:0}.ok-native-list-subtitle-dot{flex:0 0 4px;width:4px;height:4px;margin:0 6px;border-radius:50%;background:var(--nl-disabled)}.ok-native-list-wallet-row>.ok-native-list-flex{flex:0 1 auto;width:100%;align-items:center}.ok-native-list-wallet-badges{display:flex;gap:4px;justify-content:center;margin-top:4px;height:20px;max-width:100%}.ok-native-list-wallet-badges>.ok-native-list-badge{background:var(--nl-strong);color:var(--nl-secondary);font-size:12px;line-height:16px;height:20px;box-sizing:border-box;padding:2px 4px}.ok-native-list-visual-overlay{position:absolute;display:flex;align-items:center;justify-content:center;box-sizing:border-box;border-radius:50%;overflow:hidden;line-height:1;font-size:10px}.ok-native-list-visual-overlay img,.ok-native-list-visual-overlay svg{width:100%;height:100%;object-fit:contain}
|
|
@@ -2257,6 +2258,7 @@ export class NativeListWebEngine {
|
|
|
2257
2258
|
disposeWebImageRetries(this.root);
|
|
2258
2259
|
this.pool.forEach(disposeWebImageRetries);
|
|
2259
2260
|
this.root.remove();
|
|
2261
|
+
this.indexRail.remove();
|
|
2260
2262
|
this.hideReorderPreview();
|
|
2261
2263
|
this.reorderPreview.remove();
|
|
2262
2264
|
if (host) host.style.position = this.previousHostPosition;
|
|
@@ -2308,6 +2310,7 @@ export class NativeListWebEngine {
|
|
|
2308
2310
|
Object.entries(values).forEach(([name, value]) => {
|
|
2309
2311
|
if (!value) return;
|
|
2310
2312
|
this.root.style.setProperty(name, value);
|
|
2313
|
+
this.indexRail.style.setProperty(name, value);
|
|
2311
2314
|
this.reorderPreview.style.setProperty(name, value);
|
|
2312
2315
|
});
|
|
2313
2316
|
}
|
|
@@ -2315,14 +2318,12 @@ export class NativeListWebEngine {
|
|
|
2315
2318
|
if (this.destroyed) return;
|
|
2316
2319
|
const viewportWidth = this.viewport.clientWidth;
|
|
2317
2320
|
const viewportHeight = this.snapshot.layout.orientation === 'horizontal' ? this.viewport.clientHeight : this.verticalScrollMetrics().viewportLength;
|
|
2321
|
+
const stickyInset = this.snapshot.layout.orientation === 'horizontal' ? 0 : this.collapsiblePagerInsets().sticky;
|
|
2318
2322
|
if (this.snapshot.layout.orientation !== 'horizontal') {
|
|
2319
|
-
const stickyInset = this.collapsiblePagerInsets().sticky;
|
|
2320
2323
|
this.sticky.style.top = String(stickyInset) + 'px';
|
|
2321
|
-
this.indexRail.style.top = String(stickyInset) + 'px';
|
|
2322
2324
|
this.refreshIndicator.style.top = String(stickyInset + 8) + 'px';
|
|
2323
2325
|
} else {
|
|
2324
2326
|
this.sticky.style.top = '0px';
|
|
2325
|
-
this.indexRail.style.top = '0px';
|
|
2326
2327
|
this.refreshIndicator.style.top = '8px';
|
|
2327
2328
|
}
|
|
2328
2329
|
if (this.lastViewportWidth >= 0 && (viewportWidth !== this.lastViewportWidth || viewportHeight !== this.lastViewportHeight)) {
|
|
@@ -2342,7 +2343,7 @@ export class NativeListWebEngine {
|
|
|
2342
2343
|
this.layout = computeWebListLayout(measuredSnapshot, viewportWidth, viewportHeight, this.reorderCompactKey);
|
|
2343
2344
|
this.content.style.width = String(this.layout.contentWidth) + 'px';
|
|
2344
2345
|
this.content.style.height = String(this.layout.contentHeight) + 'px';
|
|
2345
|
-
this.renderSectionIndex(this.viewport.clientHeight <= 0 ? DEFAULT_VIEWPORT_HEIGHT : Math.max(1, viewportHeight));
|
|
2346
|
+
this.renderSectionIndex(this.viewport.clientHeight <= 0 ? DEFAULT_VIEWPORT_HEIGHT : Math.max(1, viewportHeight), stickyInset);
|
|
2346
2347
|
if (previousHorizontal !== this.layout.horizontal) {
|
|
2347
2348
|
this.viewport.scrollLeft = 0;
|
|
2348
2349
|
this.viewport.scrollTop = 0;
|
|
@@ -2570,32 +2571,54 @@ export class NativeListWebEngine {
|
|
|
2570
2571
|
sectionIndexMetrics(viewportHeight) {
|
|
2571
2572
|
const availableHeight = Math.max(0, viewportHeight - SECTION_INDEX_EDGE_PADDING * 2);
|
|
2572
2573
|
const trackHeight = Math.min(availableHeight, SECTION_INDEX_LABEL_SPACING * this.sectionIndexEntries.length);
|
|
2573
|
-
const centeredOriginY = (viewportHeight - trackHeight) / 2;
|
|
2574
|
-
if (!this.snapshot.capabilities?.sectionIndex?.centeredInWindow) {
|
|
2575
|
-
return {
|
|
2576
|
-
originY: centeredOriginY,
|
|
2577
|
-
trackHeight
|
|
2578
|
-
};
|
|
2579
|
-
}
|
|
2580
|
-
const view = this.document.defaultView;
|
|
2581
|
-
if (!view) return {
|
|
2582
|
-
originY: centeredOriginY,
|
|
2583
|
-
trackHeight
|
|
2584
|
-
};
|
|
2585
|
-
const visualViewport = view.visualViewport;
|
|
2586
|
-
const windowCenterY = visualViewport ? visualViewport.offsetTop + visualViewport.height / 2 : view.innerHeight / 2;
|
|
2587
|
-
const railTop = this.viewportFrame.getBoundingClientRect().top + (Number.parseFloat(this.indexRail.style.top) || 0);
|
|
2588
|
-
const minOriginY = SECTION_INDEX_EDGE_PADDING;
|
|
2589
|
-
const maxOriginY = Math.max(minOriginY, viewportHeight - SECTION_INDEX_EDGE_PADDING - trackHeight);
|
|
2590
2574
|
return {
|
|
2591
|
-
originY:
|
|
2575
|
+
originY: (viewportHeight - trackHeight) / 2,
|
|
2592
2576
|
trackHeight
|
|
2593
2577
|
};
|
|
2594
2578
|
}
|
|
2595
|
-
|
|
2579
|
+
configureSectionIndexRail(viewportHeight, stickyInset, windowCentered) {
|
|
2580
|
+
const view = this.document.defaultView;
|
|
2581
|
+
const direction = view?.getComputedStyle(this.viewportFrame).direction === 'rtl' ? 'rtl' : 'ltr';
|
|
2582
|
+
this.indexRail.style.direction = direction;
|
|
2583
|
+
if (!windowCentered) {
|
|
2584
|
+
if (this.indexRail.parentElement !== this.viewportFrame) {
|
|
2585
|
+
this.viewportFrame.appendChild(this.indexRail);
|
|
2586
|
+
}
|
|
2587
|
+
this.indexRail.style.removeProperty('position');
|
|
2588
|
+
this.indexRail.style.removeProperty('left');
|
|
2589
|
+
this.indexRail.style.removeProperty('right');
|
|
2590
|
+
this.indexRail.style.removeProperty('height');
|
|
2591
|
+
this.indexRail.style.removeProperty('z-index');
|
|
2592
|
+
this.indexRail.style.insetInlineEnd = '0px';
|
|
2593
|
+
this.indexRail.style.top = String(stickyInset) + 'px';
|
|
2594
|
+
this.indexRail.style.bottom = '0px';
|
|
2595
|
+
return viewportHeight;
|
|
2596
|
+
}
|
|
2597
|
+
if (!view) return viewportHeight;
|
|
2598
|
+
const visualViewport = view.visualViewport;
|
|
2599
|
+
const windowTop = visualViewport?.offsetTop ?? 0;
|
|
2600
|
+
const windowHeight = visualViewport?.height ?? view.innerHeight;
|
|
2601
|
+
const railHeight = Math.min(windowHeight, SECTION_INDEX_EDGE_PADDING * 2 + SECTION_INDEX_LABEL_SPACING * this.sectionIndexEntries.length);
|
|
2602
|
+
const frame = this.viewportFrame.getBoundingClientRect();
|
|
2603
|
+
if (this.indexRail.parentElement !== this.document.body) {
|
|
2604
|
+
this.document.body.appendChild(this.indexRail);
|
|
2605
|
+
}
|
|
2606
|
+
this.indexRail.style.position = 'fixed';
|
|
2607
|
+
this.indexRail.style.removeProperty('left');
|
|
2608
|
+
this.indexRail.style.removeProperty('right');
|
|
2609
|
+
this.indexRail.style.insetInlineEnd = String(Math.max(0, direction === 'rtl' ? frame.left : view.innerWidth - frame.right)) + 'px';
|
|
2610
|
+
this.indexRail.style.top = String(windowTop + (windowHeight - railHeight) / 2) + 'px';
|
|
2611
|
+
this.indexRail.style.bottom = 'auto';
|
|
2612
|
+
this.indexRail.style.height = String(railHeight) + 'px';
|
|
2613
|
+
this.indexRail.style.zIndex = String(SECTION_INDEX_WINDOW_Z_INDEX);
|
|
2614
|
+
return railHeight;
|
|
2615
|
+
}
|
|
2616
|
+
renderSectionIndex(viewportHeight, stickyInset) {
|
|
2596
2617
|
this.indexRail.replaceChildren();
|
|
2597
2618
|
if (!sectionIndexEnabled(this.snapshot)) {
|
|
2598
2619
|
this.sectionIndexEntries = [];
|
|
2620
|
+
this.configureSectionIndexRail(viewportHeight, stickyInset, false);
|
|
2621
|
+
setData(this.viewportFrame, 'sectionIndexVisible', false);
|
|
2599
2622
|
this.indexRail.hidden = true;
|
|
2600
2623
|
return;
|
|
2601
2624
|
}
|
|
@@ -2605,12 +2628,16 @@ export class NativeListWebEngine {
|
|
|
2605
2628
|
position
|
|
2606
2629
|
}] : []);
|
|
2607
2630
|
if (this.sectionIndexEntries.length === 0 || viewportHeight < SECTION_INDEX_MIN_HEIGHT) {
|
|
2631
|
+
this.configureSectionIndexRail(viewportHeight, stickyInset, false);
|
|
2632
|
+
setData(this.viewportFrame, 'sectionIndexVisible', false);
|
|
2608
2633
|
this.indexRail.hidden = true;
|
|
2609
2634
|
return;
|
|
2610
2635
|
}
|
|
2611
|
-
const
|
|
2636
|
+
const windowCentered = this.snapshot.capabilities?.sectionIndex?.centeredInWindow === true && this.viewport.clientHeight > 0;
|
|
2637
|
+
const indexLayoutHeight = this.configureSectionIndexRail(viewportHeight, stickyInset, windowCentered);
|
|
2638
|
+
const visibleEntryIndices = this.sectionIndexVisibleEntryIndices(indexLayoutHeight);
|
|
2612
2639
|
setData(this.indexRail, 'compact', visibleEntryIndices.length < this.sectionIndexEntries.length);
|
|
2613
|
-
const metrics = this.sectionIndexMetrics(
|
|
2640
|
+
const metrics = this.sectionIndexMetrics(indexLayoutHeight);
|
|
2614
2641
|
const visibleTrackHeight = Math.min(metrics.trackHeight, SECTION_INDEX_LABEL_SPACING * visibleEntryIndices.length);
|
|
2615
2642
|
const visibleOriginY = metrics.originY + (metrics.trackHeight - visibleTrackHeight) / 2;
|
|
2616
2643
|
const fragment = this.document.createDocumentFragment();
|
|
@@ -2628,6 +2655,7 @@ export class NativeListWebEngine {
|
|
|
2628
2655
|
});
|
|
2629
2656
|
this.indexRail.appendChild(fragment);
|
|
2630
2657
|
this.indexRail.hidden = this.indexRail.childElementCount === 0;
|
|
2658
|
+
setData(this.viewportFrame, 'sectionIndexVisible', !this.indexRail.hidden);
|
|
2631
2659
|
}
|
|
2632
2660
|
updateVisibleSelection() {
|
|
2633
2661
|
const update = (element, row) => {
|
|
@@ -154,6 +154,7 @@ export declare class NativeListWebEngine {
|
|
|
154
154
|
private renderFooter;
|
|
155
155
|
private sectionIndexVisibleEntryIndices;
|
|
156
156
|
private sectionIndexMetrics;
|
|
157
|
+
private configureSectionIndexRail;
|
|
157
158
|
private renderSectionIndex;
|
|
158
159
|
private updateVisibleSelection;
|
|
159
160
|
private updateVisibleState;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/react-native-native-list",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.124",
|
|
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",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"nitrogen": "nitrogen",
|
|
39
39
|
"typecheck": "tsc -b",
|
|
40
40
|
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
41
|
-
"test": "jest --runInBand",
|
|
41
|
+
"test": "jest --runInBand && node --test src/__tests__/selector-parity.cjs",
|
|
42
42
|
"release": "yarn prepare && npm whoami && npm publish --access public"
|
|
43
43
|
},
|
|
44
44
|
"keywords": [
|
|
@@ -83,8 +83,8 @@
|
|
|
83
83
|
"typescript": "^5.9.2"
|
|
84
84
|
},
|
|
85
85
|
"peerDependencies": {
|
|
86
|
-
"@onekeyfe/react-native-image": "3.0.
|
|
87
|
-
"@onekeyfe/react-native-native-logger": "3.0.
|
|
86
|
+
"@onekeyfe/react-native-image": "3.0.124",
|
|
87
|
+
"@onekeyfe/react-native-native-logger": "3.0.124",
|
|
88
88
|
"react": "*",
|
|
89
89
|
"react-native": "*",
|
|
90
90
|
"react-native-nitro-modules": "0.37.0"
|
|
@@ -51,6 +51,7 @@ const SECTION_INDEX_RAIL_WIDTH = 32;
|
|
|
51
51
|
const SECTION_INDEX_EDGE_PADDING = 8;
|
|
52
52
|
const SECTION_INDEX_LABEL_SPACING = 16;
|
|
53
53
|
const SECTION_INDEX_MIN_HEIGHT = 120;
|
|
54
|
+
const SECTION_INDEX_WINDOW_Z_INDEX = 100_000;
|
|
54
55
|
const DEFAULT_VIEWPORT_WIDTH = 320;
|
|
55
56
|
const DEFAULT_VIEWPORT_HEIGHT = 640;
|
|
56
57
|
const OVERSCAN_VIEWPORTS = 1;
|
|
@@ -885,12 +886,12 @@ export const WEB_LIST_CSS = `
|
|
|
885
886
|
.ok-native-list-item[data-native-list-reorderable="true"]>.ok-native-list-row{cursor:grab}
|
|
886
887
|
.ok-native-list-root[data-native-list-dragging="true"] .ok-native-list-row{cursor:grabbing}
|
|
887
888
|
.ok-native-list-item[data-native-list-animate-reorder="true"]{transition:transform ${WEB_REORDER_ANIMATION.outOfWayDurationMs}ms ${WEB_REORDER_ANIMATION.outOfWayTimingFunction},height ${WEB_REORDER_ANIMATION.outOfWayDurationMs}ms ${WEB_REORDER_ANIMATION.outOfWayTimingFunction}}
|
|
888
|
-
.ok-native-list-item[data-native-list-dragging="true"]>.ok-native-list-row{overflow:hidden;border-radius:12px;background:var(--nl-
|
|
889
|
+
.ok-native-list-item[data-native-list-dragging="true"]>.ok-native-list-row{overflow:hidden;border-radius:12px;background:var(--nl-pressed)}
|
|
889
890
|
.ok-native-list-item[data-native-list-dragging="true"]>.ok-native-list-row>*{visibility:hidden}
|
|
890
|
-
.ok-native-list-item[data-native-list-dragging="true"]>.ok-native-list-wallet-group{border-color:transparent;background:var(--nl-
|
|
891
|
+
.ok-native-list-item[data-native-list-dragging="true"]>.ok-native-list-wallet-group{border-color:transparent;background:var(--nl-pressed)}
|
|
891
892
|
.ok-native-list-item[data-native-list-dragging="true"]>.ok-native-list-wallet-group>*{visibility:hidden}
|
|
892
893
|
.ok-native-list-reorder-preview{position:fixed;left:0;top:0;z-index:100001;pointer-events:none;overflow:hidden;border-radius:12px;box-shadow:0 4px 24px rgba(0,0,0,.12);transform-origin:center;will-change:transform;font-family:Roobert,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
|
|
893
|
-
.ok-native-list-reorder-preview>.ok-native-list-row{background:var(--nl-
|
|
894
|
+
.ok-native-list-reorder-preview>.ok-native-list-row{background:var(--nl-pressed);cursor:grabbing}
|
|
894
895
|
.ok-native-list-reorder-preview[data-native-list-selected="true"]>.ok-native-list-row{background:var(--nl-selected)}
|
|
895
896
|
.ok-native-list-reorder-count{position:absolute;right:4px;bottom:4px;display:flex;align-items:center;justify-content:center;box-sizing:border-box;min-width:24px;height:24px;padding:0 6px;border:1px solid var(--nl-row);border-radius:12px;background:var(--nl-inverse);color:var(--nl-inverse-text);font-size:12px;line-height:22px;font-weight:600}
|
|
896
897
|
.ok-native-list-item[data-separator="true"]>.ok-native-list-row{border-bottom:1px solid var(--nl-separator)}
|
|
@@ -922,7 +923,7 @@ export const WEB_LIST_CSS = `
|
|
|
922
923
|
.ok-native-list-subtitle-segments{display:flex;align-items:center;min-width:0;max-width:100%;height:20px}.ok-native-list-subtitle-segments>.ok-native-list-secondary{flex:0 1 auto;min-width:0}.ok-native-list-subtitle-dot{flex:0 0 4px;width:4px;height:4px;margin:0 6px;border-radius:50%;background:var(--nl-disabled)}.ok-native-list-wallet-row>.ok-native-list-flex{flex:0 1 auto;width:100%;align-items:center}.ok-native-list-wallet-badges{display:flex;gap:4px;justify-content:center;margin-top:4px;height:20px;max-width:100%}.ok-native-list-wallet-badges>.ok-native-list-badge{background:var(--nl-strong);color:var(--nl-secondary);font-size:12px;line-height:16px;height:20px;box-sizing:border-box;padding:2px 4px}.ok-native-list-visual-overlay{position:absolute;display:flex;align-items:center;justify-content:center;box-sizing:border-box;border-radius:50%;overflow:hidden;line-height:1;font-size:10px}.ok-native-list-visual-overlay img,.ok-native-list-visual-overlay svg{width:100%;height:100%;object-fit:contain}
|
|
923
924
|
.ok-native-list-row.ok-native-list-market-skeleton{padding:12px 20px;gap:0}.ok-native-list-skeleton-left{display:flex;align-items:center;gap:12px;flex:1}.ok-native-list-skeleton-text{display:flex;flex-direction:column;gap:4px}.ok-native-list-skeleton-right{display:flex;align-items:center;gap:8px}.ok-native-list-skeleton-mark{display:block;flex-shrink:0;border-radius:8px;animation:ok-native-list-skeleton 1.5s linear infinite alternate}@keyframes ok-native-list-skeleton{from{background-color:var(--nl-skeleton-base)}to{background-color:var(--nl-skeleton-highlight)}}.ok-native-list-market-spinner{display:block;width:20px;height:20px;flex-shrink:0;color:var(--nl-icon);animation:ok-native-list-spin .75s linear infinite}
|
|
924
925
|
@media (prefers-reduced-motion:reduce){.ok-native-list-index-preview,.ok-native-list-refresh{transition:none}.ok-native-list-spinner,.ok-native-list-market-spinner{animation:none}.ok-native-list-skeleton-mark{animation:none;background:var(--nl-skeleton-base)}}
|
|
925
|
-
.ok-native-list-footer{flex:0 0 auto;min-height:0}.ok-native-list-sticky{position:absolute;z-index:4;left:0;right:0;top:0;pointer-events:auto;box-shadow:0 1px 0 var(--nl-separator)}.ok-native-list-viewport-frame:has(>.ok-native-list-index-rail:not([hidden]))>.ok-native-list-viewport{scrollbar-width:none}.ok-native-list-viewport-frame:has(>.ok-native-list-index-rail:not([hidden]))>.ok-native-list-viewport::-webkit-scrollbar{display:none}.ok-native-list-index-rail{position:absolute;z-index:6;top:0;right:0;bottom:0;width:${SECTION_INDEX_RAIL_WIDTH}px;touch-action:none;cursor:pointer}.ok-native-list-index-rail[hidden]{display:none}.ok-native-list-index-button{appearance:none;position:absolute;left:15px;display:flex;width:14px;height:14px;align-items:center;justify-content:center;padding:0;transform:translateY(-50%);border:0;border-radius:7px;background:transparent;color:var(--nl-disabled);font-family:inherit;font-size:10px;font-weight:400;line-height:1;cursor:pointer}.ok-native-list-index-button[data-active="true"]{background:var(--nl-positive);color:var(--nl-inverse-text);font-weight:500}.ok-native-list-index-button:focus-visible{outline:2px solid var(--nl-positive);outline-offset:1px}
|
|
926
|
+
.ok-native-list-footer{flex:0 0 auto;min-height:0}.ok-native-list-sticky{position:absolute;z-index:4;left:0;right:0;top:0;pointer-events:auto;box-shadow:0 1px 0 var(--nl-separator)}.ok-native-list-viewport-frame:has(>.ok-native-list-index-rail:not([hidden]))>.ok-native-list-viewport,.ok-native-list-viewport-frame[data-section-index-visible="true"]>.ok-native-list-viewport{scrollbar-width:none}.ok-native-list-viewport-frame:has(>.ok-native-list-index-rail:not([hidden]))>.ok-native-list-viewport::-webkit-scrollbar,.ok-native-list-viewport-frame[data-section-index-visible="true"]>.ok-native-list-viewport::-webkit-scrollbar{display:none}.ok-native-list-index-rail{position:absolute;z-index:6;top:0;right:0;bottom:0;width:${SECTION_INDEX_RAIL_WIDTH}px;touch-action:none;cursor:pointer;font-family:Roobert,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.ok-native-list-index-rail[hidden]{display:none}.ok-native-list-index-button{appearance:none;position:absolute;left:15px;display:flex;width:14px;height:14px;align-items:center;justify-content:center;padding:0;transform:translateY(-50%);border:0;border-radius:7px;background:transparent;color:var(--nl-disabled);font-family:inherit;font-size:10px;font-weight:400;line-height:1;cursor:pointer}.ok-native-list-index-button[data-active="true"]{background:var(--nl-positive);color:var(--nl-inverse-text);font-weight:500}.ok-native-list-index-button:focus-visible{outline:2px solid var(--nl-positive);outline-offset:1px}
|
|
926
927
|
.ok-native-list-refresh{position:absolute;z-index:7;left:50%;top:8px;display:flex;align-items:center;gap:6px;padding:6px 10px;border-radius:999px;background:var(--nl-inverse);color:var(--nl-inverse-text);font-size:12px;opacity:0;transform:translate(-50%,-16px);transition:opacity .15s ease,transform .15s ease;pointer-events:none}.ok-native-list-refresh[data-visible="true"]{opacity:1;transform:translate(-50%,0)}
|
|
927
928
|
.ok-native-list-warning{height:auto;display:flex;flex-direction:column;align-items:stretch;gap:4px;padding:14px 12px;border-top:1px solid;border-bottom:1px solid;box-sizing:border-box;cursor:default}.ok-native-list-warning-title,.ok-native-list-warning-message{font-size:14px;line-height:20px;white-space:normal;overflow-wrap:anywhere}.ok-native-list-warning-title{font-weight:500;color:var(--nl-primary)}.ok-native-list-warning-message{font-weight:400;color:var(--nl-secondary)}
|
|
928
929
|
.ok-native-list-subtitle-segments{display:flex;align-items:center;min-width:0;max-width:100%;height:20px}.ok-native-list-subtitle-segments>.ok-native-list-secondary{flex:0 1 auto;min-width:0}.ok-native-list-subtitle-dot{flex:0 0 4px;width:4px;height:4px;margin:0 6px;border-radius:50%;background:var(--nl-disabled)}.ok-native-list-wallet-row>.ok-native-list-flex{flex:0 1 auto;width:100%;align-items:center}.ok-native-list-wallet-badges{display:flex;gap:4px;justify-content:center;margin-top:4px;height:20px;max-width:100%}.ok-native-list-wallet-badges>.ok-native-list-badge{background:var(--nl-strong);color:var(--nl-secondary);font-size:12px;line-height:16px;height:20px;box-sizing:border-box;padding:2px 4px}.ok-native-list-visual-overlay{position:absolute;display:flex;align-items:center;justify-content:center;box-sizing:border-box;border-radius:50%;overflow:hidden;line-height:1;font-size:10px}.ok-native-list-visual-overlay img,.ok-native-list-visual-overlay svg{width:100%;height:100%;object-fit:contain}
|
|
@@ -3887,6 +3888,7 @@ export class NativeListWebEngine {
|
|
|
3887
3888
|
disposeWebImageRetries(this.root);
|
|
3888
3889
|
this.pool.forEach(disposeWebImageRetries);
|
|
3889
3890
|
this.root.remove();
|
|
3891
|
+
this.indexRail.remove();
|
|
3890
3892
|
this.hideReorderPreview();
|
|
3891
3893
|
this.reorderPreview.remove();
|
|
3892
3894
|
if (host) host.style.position = this.previousHostPosition;
|
|
@@ -3945,6 +3947,7 @@ export class NativeListWebEngine {
|
|
|
3945
3947
|
Object.entries(values).forEach(([name, value]) => {
|
|
3946
3948
|
if (!value) return;
|
|
3947
3949
|
this.root.style.setProperty(name, value);
|
|
3950
|
+
this.indexRail.style.setProperty(name, value);
|
|
3948
3951
|
this.reorderPreview.style.setProperty(name, value);
|
|
3949
3952
|
});
|
|
3950
3953
|
}
|
|
@@ -3956,14 +3959,15 @@ export class NativeListWebEngine {
|
|
|
3956
3959
|
this.snapshot.layout.orientation === 'horizontal'
|
|
3957
3960
|
? this.viewport.clientHeight
|
|
3958
3961
|
: this.verticalScrollMetrics().viewportLength;
|
|
3962
|
+
const stickyInset =
|
|
3963
|
+
this.snapshot.layout.orientation === 'horizontal'
|
|
3964
|
+
? 0
|
|
3965
|
+
: this.collapsiblePagerInsets().sticky;
|
|
3959
3966
|
if (this.snapshot.layout.orientation !== 'horizontal') {
|
|
3960
|
-
const stickyInset = this.collapsiblePagerInsets().sticky;
|
|
3961
3967
|
this.sticky.style.top = String(stickyInset) + 'px';
|
|
3962
|
-
this.indexRail.style.top = String(stickyInset) + 'px';
|
|
3963
3968
|
this.refreshIndicator.style.top = String(stickyInset + 8) + 'px';
|
|
3964
3969
|
} else {
|
|
3965
3970
|
this.sticky.style.top = '0px';
|
|
3966
|
-
this.indexRail.style.top = '0px';
|
|
3967
3971
|
this.refreshIndicator.style.top = '8px';
|
|
3968
3972
|
}
|
|
3969
3973
|
if (
|
|
@@ -3999,7 +4003,8 @@ export class NativeListWebEngine {
|
|
|
3999
4003
|
this.renderSectionIndex(
|
|
4000
4004
|
this.viewport.clientHeight <= 0
|
|
4001
4005
|
? DEFAULT_VIEWPORT_HEIGHT
|
|
4002
|
-
: Math.max(1, viewportHeight)
|
|
4006
|
+
: Math.max(1, viewportHeight),
|
|
4007
|
+
stickyInset
|
|
4003
4008
|
);
|
|
4004
4009
|
if (previousHorizontal !== this.layout.horizontal) {
|
|
4005
4010
|
this.viewport.scrollLeft = 0;
|
|
@@ -4333,37 +4338,75 @@ export class NativeListWebEngine {
|
|
|
4333
4338
|
availableHeight,
|
|
4334
4339
|
SECTION_INDEX_LABEL_SPACING * this.sectionIndexEntries.length
|
|
4335
4340
|
);
|
|
4336
|
-
const centeredOriginY = (viewportHeight - trackHeight) / 2;
|
|
4337
|
-
if (!this.snapshot.capabilities?.sectionIndex?.centeredInWindow) {
|
|
4338
|
-
return { originY: centeredOriginY, trackHeight };
|
|
4339
|
-
}
|
|
4340
|
-
const view = this.document.defaultView;
|
|
4341
|
-
if (!view) return { originY: centeredOriginY, trackHeight };
|
|
4342
|
-
const visualViewport = view.visualViewport;
|
|
4343
|
-
const windowCenterY = visualViewport
|
|
4344
|
-
? visualViewport.offsetTop + visualViewport.height / 2
|
|
4345
|
-
: view.innerHeight / 2;
|
|
4346
|
-
const railTop =
|
|
4347
|
-
this.viewportFrame.getBoundingClientRect().top +
|
|
4348
|
-
(Number.parseFloat(this.indexRail.style.top) || 0);
|
|
4349
|
-
const minOriginY = SECTION_INDEX_EDGE_PADDING;
|
|
4350
|
-
const maxOriginY = Math.max(
|
|
4351
|
-
minOriginY,
|
|
4352
|
-
viewportHeight - SECTION_INDEX_EDGE_PADDING - trackHeight
|
|
4353
|
-
);
|
|
4354
4341
|
return {
|
|
4355
|
-
originY:
|
|
4356
|
-
maxOriginY,
|
|
4357
|
-
Math.max(minOriginY, windowCenterY - railTop - trackHeight / 2)
|
|
4358
|
-
),
|
|
4342
|
+
originY: (viewportHeight - trackHeight) / 2,
|
|
4359
4343
|
trackHeight,
|
|
4360
4344
|
};
|
|
4361
4345
|
}
|
|
4362
4346
|
|
|
4363
|
-
private
|
|
4347
|
+
private configureSectionIndexRail(
|
|
4348
|
+
viewportHeight: number,
|
|
4349
|
+
stickyInset: number,
|
|
4350
|
+
windowCentered: boolean
|
|
4351
|
+
): number {
|
|
4352
|
+
const view = this.document.defaultView;
|
|
4353
|
+
const direction =
|
|
4354
|
+
view?.getComputedStyle(this.viewportFrame).direction === 'rtl'
|
|
4355
|
+
? 'rtl'
|
|
4356
|
+
: 'ltr';
|
|
4357
|
+
this.indexRail.style.direction = direction;
|
|
4358
|
+
if (!windowCentered) {
|
|
4359
|
+
if (this.indexRail.parentElement !== this.viewportFrame) {
|
|
4360
|
+
this.viewportFrame.appendChild(this.indexRail);
|
|
4361
|
+
}
|
|
4362
|
+
this.indexRail.style.removeProperty('position');
|
|
4363
|
+
this.indexRail.style.removeProperty('left');
|
|
4364
|
+
this.indexRail.style.removeProperty('right');
|
|
4365
|
+
this.indexRail.style.removeProperty('height');
|
|
4366
|
+
this.indexRail.style.removeProperty('z-index');
|
|
4367
|
+
this.indexRail.style.insetInlineEnd = '0px';
|
|
4368
|
+
this.indexRail.style.top = String(stickyInset) + 'px';
|
|
4369
|
+
this.indexRail.style.bottom = '0px';
|
|
4370
|
+
return viewportHeight;
|
|
4371
|
+
}
|
|
4372
|
+
|
|
4373
|
+
if (!view) return viewportHeight;
|
|
4374
|
+
const visualViewport = view.visualViewport;
|
|
4375
|
+
const windowTop = visualViewport?.offsetTop ?? 0;
|
|
4376
|
+
const windowHeight = visualViewport?.height ?? view.innerHeight;
|
|
4377
|
+
const railHeight = Math.min(
|
|
4378
|
+
windowHeight,
|
|
4379
|
+
SECTION_INDEX_EDGE_PADDING * 2 +
|
|
4380
|
+
SECTION_INDEX_LABEL_SPACING * this.sectionIndexEntries.length
|
|
4381
|
+
);
|
|
4382
|
+
const frame = this.viewportFrame.getBoundingClientRect();
|
|
4383
|
+
if (this.indexRail.parentElement !== this.document.body) {
|
|
4384
|
+
this.document.body.appendChild(this.indexRail);
|
|
4385
|
+
}
|
|
4386
|
+
this.indexRail.style.position = 'fixed';
|
|
4387
|
+
this.indexRail.style.removeProperty('left');
|
|
4388
|
+
this.indexRail.style.removeProperty('right');
|
|
4389
|
+
this.indexRail.style.insetInlineEnd =
|
|
4390
|
+
String(
|
|
4391
|
+
Math.max(
|
|
4392
|
+
0,
|
|
4393
|
+
direction === 'rtl' ? frame.left : view.innerWidth - frame.right
|
|
4394
|
+
)
|
|
4395
|
+
) + 'px';
|
|
4396
|
+
this.indexRail.style.top =
|
|
4397
|
+
String(windowTop + (windowHeight - railHeight) / 2) + 'px';
|
|
4398
|
+
this.indexRail.style.bottom = 'auto';
|
|
4399
|
+
this.indexRail.style.height = String(railHeight) + 'px';
|
|
4400
|
+
this.indexRail.style.zIndex = String(SECTION_INDEX_WINDOW_Z_INDEX);
|
|
4401
|
+
return railHeight;
|
|
4402
|
+
}
|
|
4403
|
+
|
|
4404
|
+
private renderSectionIndex(viewportHeight: number, stickyInset: number) {
|
|
4364
4405
|
this.indexRail.replaceChildren();
|
|
4365
4406
|
if (!sectionIndexEnabled(this.snapshot)) {
|
|
4366
4407
|
this.sectionIndexEntries = [];
|
|
4408
|
+
this.configureSectionIndexRail(viewportHeight, stickyInset, false);
|
|
4409
|
+
setData(this.viewportFrame, 'sectionIndexVisible', false);
|
|
4367
4410
|
this.indexRail.hidden = true;
|
|
4368
4411
|
return;
|
|
4369
4412
|
}
|
|
@@ -4376,17 +4419,27 @@ export class NativeListWebEngine {
|
|
|
4376
4419
|
this.sectionIndexEntries.length === 0 ||
|
|
4377
4420
|
viewportHeight < SECTION_INDEX_MIN_HEIGHT
|
|
4378
4421
|
) {
|
|
4422
|
+
this.configureSectionIndexRail(viewportHeight, stickyInset, false);
|
|
4423
|
+
setData(this.viewportFrame, 'sectionIndexVisible', false);
|
|
4379
4424
|
this.indexRail.hidden = true;
|
|
4380
4425
|
return;
|
|
4381
4426
|
}
|
|
4427
|
+
const windowCentered =
|
|
4428
|
+
this.snapshot.capabilities?.sectionIndex?.centeredInWindow === true &&
|
|
4429
|
+
this.viewport.clientHeight > 0;
|
|
4430
|
+
const indexLayoutHeight = this.configureSectionIndexRail(
|
|
4431
|
+
viewportHeight,
|
|
4432
|
+
stickyInset,
|
|
4433
|
+
windowCentered
|
|
4434
|
+
);
|
|
4382
4435
|
const visibleEntryIndices =
|
|
4383
|
-
this.sectionIndexVisibleEntryIndices(
|
|
4436
|
+
this.sectionIndexVisibleEntryIndices(indexLayoutHeight);
|
|
4384
4437
|
setData(
|
|
4385
4438
|
this.indexRail,
|
|
4386
4439
|
'compact',
|
|
4387
4440
|
visibleEntryIndices.length < this.sectionIndexEntries.length
|
|
4388
4441
|
);
|
|
4389
|
-
const metrics = this.sectionIndexMetrics(
|
|
4442
|
+
const metrics = this.sectionIndexMetrics(indexLayoutHeight);
|
|
4390
4443
|
const visibleTrackHeight = Math.min(
|
|
4391
4444
|
metrics.trackHeight,
|
|
4392
4445
|
SECTION_INDEX_LABEL_SPACING * visibleEntryIndices.length
|
|
@@ -4418,6 +4471,7 @@ export class NativeListWebEngine {
|
|
|
4418
4471
|
});
|
|
4419
4472
|
this.indexRail.appendChild(fragment);
|
|
4420
4473
|
this.indexRail.hidden = this.indexRail.childElementCount === 0;
|
|
4474
|
+
setData(this.viewportFrame, 'sectionIndexVisible', !this.indexRail.hidden);
|
|
4421
4475
|
}
|
|
4422
4476
|
|
|
4423
4477
|
private updateVisibleSelection() {
|