@onekeyfe/react-native-segment-slider 3.0.40
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/LICENSE +21 -0
- package/README.md +31 -0
- package/SegmentSlider.podspec +29 -0
- package/android/CMakeLists.txt +26 -0
- package/android/build.gradle +128 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/cpp-adapter.cpp +6 -0
- package/android/src/main/java/com/margelo/nitro/segmentslider/HybridSegmentSlider.kt +364 -0
- package/android/src/main/java/com/margelo/nitro/segmentslider/SegmentSliderColorParser.kt +56 -0
- package/android/src/main/java/com/margelo/nitro/segmentslider/SegmentSliderPackage.kt +29 -0
- package/ios/HybridSegmentSlider.swift +412 -0
- package/ios/SegmentSliderColorParser.swift +152 -0
- package/lib/module/SegmentSlider.nitro.js +4 -0
- package/lib/module/SegmentSlider.nitro.js.map +1 -0
- package/lib/module/index.js +43 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
- package/lib/nitrogen/generated/android/c++/JFunc_void_double.hpp +75 -0
- package/lib/nitrogen/generated/android/c++/JHybridSegmentSliderSpec.cpp +265 -0
- package/lib/nitrogen/generated/android/c++/JHybridSegmentSliderSpec.hpp +106 -0
- package/lib/nitrogen/generated/android/c++/views/JHybridSegmentSliderStateUpdater.cpp +136 -0
- package/lib/nitrogen/generated/android/c++/views/JHybridSegmentSliderStateUpdater.hpp +49 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/Func_void.kt +80 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/Func_void_double.kt +80 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/HybridSegmentSliderSpec.kt +203 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/segmentsliderOnLoad.kt +35 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/views/HybridSegmentSliderManager.kt +50 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/views/HybridSegmentSliderStateUpdater.kt +23 -0
- package/lib/nitrogen/generated/android/segmentslider+autolinking.cmake +83 -0
- package/lib/nitrogen/generated/android/segmentslider+autolinking.gradle +27 -0
- package/lib/nitrogen/generated/android/segmentsliderOnLoad.cpp +50 -0
- package/lib/nitrogen/generated/android/segmentsliderOnLoad.hpp +25 -0
- package/lib/nitrogen/generated/ios/SegmentSlider+autolinking.rb +60 -0
- package/lib/nitrogen/generated/ios/SegmentSlider-Swift-Cxx-Bridge.cpp +49 -0
- package/lib/nitrogen/generated/ios/SegmentSlider-Swift-Cxx-Bridge.hpp +116 -0
- package/lib/nitrogen/generated/ios/SegmentSlider-Swift-Cxx-Umbrella.hpp +44 -0
- package/lib/nitrogen/generated/ios/SegmentSliderAutolinking.mm +33 -0
- package/lib/nitrogen/generated/ios/SegmentSliderAutolinking.swift +25 -0
- package/lib/nitrogen/generated/ios/c++/HybridSegmentSliderSpecSwift.cpp +11 -0
- package/lib/nitrogen/generated/ios/c++/HybridSegmentSliderSpecSwift.hpp +208 -0
- package/lib/nitrogen/generated/ios/c++/views/HybridSegmentSliderComponent.mm +196 -0
- package/lib/nitrogen/generated/ios/swift/Func_void.swift +47 -0
- package/lib/nitrogen/generated/ios/swift/Func_void_double.swift +47 -0
- package/lib/nitrogen/generated/ios/swift/HybridSegmentSliderSpec.swift +76 -0
- package/lib/nitrogen/generated/ios/swift/HybridSegmentSliderSpec_cxx.swift +423 -0
- package/lib/nitrogen/generated/shared/c++/HybridSegmentSliderSpec.cpp +62 -0
- package/lib/nitrogen/generated/shared/c++/HybridSegmentSliderSpec.hpp +105 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridSegmentSliderComponent.cpp +327 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridSegmentSliderComponent.hpp +128 -0
- package/lib/nitrogen/generated/shared/json/SegmentSliderConfig.json +30 -0
- package/lib/nitrogen/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
- package/lib/nitrogen/nitrogen/generated/android/c++/JFunc_void_double.hpp +75 -0
- package/lib/nitrogen/nitrogen/generated/android/c++/JHybridSegmentSliderSpec.cpp +265 -0
- package/lib/nitrogen/nitrogen/generated/android/c++/JHybridSegmentSliderSpec.hpp +106 -0
- package/lib/nitrogen/nitrogen/generated/android/c++/views/JHybridSegmentSliderStateUpdater.cpp +136 -0
- package/lib/nitrogen/nitrogen/generated/android/c++/views/JHybridSegmentSliderStateUpdater.hpp +49 -0
- package/lib/nitrogen/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/Func_void.kt +80 -0
- package/lib/nitrogen/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/Func_void_double.kt +80 -0
- package/lib/nitrogen/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/HybridSegmentSliderSpec.kt +203 -0
- package/lib/nitrogen/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/segmentsliderOnLoad.kt +35 -0
- package/lib/nitrogen/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/views/HybridSegmentSliderManager.kt +50 -0
- package/lib/nitrogen/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/views/HybridSegmentSliderStateUpdater.kt +23 -0
- package/lib/nitrogen/nitrogen/generated/android/segmentslider+autolinking.cmake +83 -0
- package/lib/nitrogen/nitrogen/generated/android/segmentslider+autolinking.gradle +27 -0
- package/lib/nitrogen/nitrogen/generated/android/segmentsliderOnLoad.cpp +50 -0
- package/lib/nitrogen/nitrogen/generated/android/segmentsliderOnLoad.hpp +25 -0
- package/lib/nitrogen/nitrogen/generated/ios/SegmentSlider+autolinking.rb +60 -0
- package/lib/nitrogen/nitrogen/generated/ios/SegmentSlider-Swift-Cxx-Bridge.cpp +49 -0
- package/lib/nitrogen/nitrogen/generated/ios/SegmentSlider-Swift-Cxx-Bridge.hpp +116 -0
- package/lib/nitrogen/nitrogen/generated/ios/SegmentSlider-Swift-Cxx-Umbrella.hpp +44 -0
- package/lib/nitrogen/nitrogen/generated/ios/SegmentSliderAutolinking.mm +33 -0
- package/lib/nitrogen/nitrogen/generated/ios/SegmentSliderAutolinking.swift +25 -0
- package/lib/nitrogen/nitrogen/generated/ios/c++/HybridSegmentSliderSpecSwift.cpp +11 -0
- package/lib/nitrogen/nitrogen/generated/ios/c++/HybridSegmentSliderSpecSwift.hpp +208 -0
- package/lib/nitrogen/nitrogen/generated/ios/c++/views/HybridSegmentSliderComponent.mm +196 -0
- package/lib/nitrogen/nitrogen/generated/ios/swift/Func_void.swift +47 -0
- package/lib/nitrogen/nitrogen/generated/ios/swift/Func_void_double.swift +47 -0
- package/lib/nitrogen/nitrogen/generated/ios/swift/HybridSegmentSliderSpec.swift +76 -0
- package/lib/nitrogen/nitrogen/generated/ios/swift/HybridSegmentSliderSpec_cxx.swift +423 -0
- package/lib/nitrogen/nitrogen/generated/shared/c++/HybridSegmentSliderSpec.cpp +62 -0
- package/lib/nitrogen/nitrogen/generated/shared/c++/HybridSegmentSliderSpec.hpp +105 -0
- package/lib/nitrogen/nitrogen/generated/shared/c++/views/HybridSegmentSliderComponent.cpp +327 -0
- package/lib/nitrogen/nitrogen/generated/shared/c++/views/HybridSegmentSliderComponent.hpp +128 -0
- package/lib/nitrogen/nitrogen/generated/shared/json/SegmentSliderConfig.json +30 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/SegmentSlider.nitro.d.ts +72 -0
- package/lib/typescript/src/SegmentSlider.nitro.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +11 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/nitro.json +17 -0
- package/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
- package/nitrogen/generated/android/c++/JFunc_void_double.hpp +75 -0
- package/nitrogen/generated/android/c++/JHybridSegmentSliderSpec.cpp +265 -0
- package/nitrogen/generated/android/c++/JHybridSegmentSliderSpec.hpp +106 -0
- package/nitrogen/generated/android/c++/views/JHybridSegmentSliderStateUpdater.cpp +136 -0
- package/nitrogen/generated/android/c++/views/JHybridSegmentSliderStateUpdater.hpp +49 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/Func_void.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/Func_void_double.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/HybridSegmentSliderSpec.kt +203 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/segmentsliderOnLoad.kt +35 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/views/HybridSegmentSliderManager.kt +50 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/segmentslider/views/HybridSegmentSliderStateUpdater.kt +23 -0
- package/nitrogen/generated/android/segmentslider+autolinking.cmake +83 -0
- package/nitrogen/generated/android/segmentslider+autolinking.gradle +27 -0
- package/nitrogen/generated/android/segmentsliderOnLoad.cpp +50 -0
- package/nitrogen/generated/android/segmentsliderOnLoad.hpp +25 -0
- package/nitrogen/generated/ios/SegmentSlider+autolinking.rb +60 -0
- package/nitrogen/generated/ios/SegmentSlider-Swift-Cxx-Bridge.cpp +49 -0
- package/nitrogen/generated/ios/SegmentSlider-Swift-Cxx-Bridge.hpp +116 -0
- package/nitrogen/generated/ios/SegmentSlider-Swift-Cxx-Umbrella.hpp +44 -0
- package/nitrogen/generated/ios/SegmentSliderAutolinking.mm +33 -0
- package/nitrogen/generated/ios/SegmentSliderAutolinking.swift +25 -0
- package/nitrogen/generated/ios/c++/HybridSegmentSliderSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridSegmentSliderSpecSwift.hpp +208 -0
- package/nitrogen/generated/ios/c++/views/HybridSegmentSliderComponent.mm +196 -0
- package/nitrogen/generated/ios/swift/Func_void.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_double.swift +47 -0
- package/nitrogen/generated/ios/swift/HybridSegmentSliderSpec.swift +76 -0
- package/nitrogen/generated/ios/swift/HybridSegmentSliderSpec_cxx.swift +423 -0
- package/nitrogen/generated/shared/c++/HybridSegmentSliderSpec.cpp +62 -0
- package/nitrogen/generated/shared/c++/HybridSegmentSliderSpec.hpp +105 -0
- package/nitrogen/generated/shared/c++/views/HybridSegmentSliderComponent.cpp +327 -0
- package/nitrogen/generated/shared/c++/views/HybridSegmentSliderComponent.hpp +128 -0
- package/nitrogen/generated/shared/json/SegmentSliderConfig.json +30 -0
- package/package.json +170 -0
- package/src/SegmentSlider.nitro.ts +80 -0
- package/src/index.tsx +59 -0
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
/// Native segmented slider. Renders the track, fill bar, segment marks, thumb
|
|
5
|
+
/// and value bubble as CALayers and handles the drag gesture itself, so a drag
|
|
6
|
+
/// never crosses the JS bridge per frame. Mirrors the web variant
|
|
7
|
+
/// (`composite/SegmentSlider/index.tsx`) pixel-for-pixel.
|
|
8
|
+
final class HybridSegmentSlider: HybridSegmentSliderSpec {
|
|
9
|
+
|
|
10
|
+
// MARK: - Geometry (points), identical to the web constants
|
|
11
|
+
private let thumbSize: CGFloat = 16
|
|
12
|
+
private let markSize: CGFloat = 10
|
|
13
|
+
private let haloRing: CGFloat = 4 // translucent halo width beyond each mark
|
|
14
|
+
private var inset: CGFloat { thumbSize / 2 }
|
|
15
|
+
|
|
16
|
+
// MARK: - HybridView
|
|
17
|
+
var view: UIView = SegmentSliderUIView()
|
|
18
|
+
|
|
19
|
+
// MARK: - Layers
|
|
20
|
+
private let trackLayer = CALayer()
|
|
21
|
+
private let fillLayer = CALayer()
|
|
22
|
+
private let thumbLayer = CALayer()
|
|
23
|
+
private var markLayers: [CALayer] = []
|
|
24
|
+
private let bubbleLayer = CALayer()
|
|
25
|
+
private let bubbleTextLayer = CATextLayer()
|
|
26
|
+
// Translucent highlight shown around the segment node the thumb is over while
|
|
27
|
+
// dragging (mobile analogue of the web mark hover halo). One shared layer,
|
|
28
|
+
// repositioned to the active node; below the marks so the node dot stays crisp.
|
|
29
|
+
private let haloLayer = CALayer()
|
|
30
|
+
|
|
31
|
+
// MARK: - Cached colors
|
|
32
|
+
private var cFill = UIColor.clear.cgColor
|
|
33
|
+
private var cTrack = UIColor.clear.cgColor
|
|
34
|
+
private var cThumb = UIColor.white.cgColor
|
|
35
|
+
private var cThumbBorder = UIColor.gray.cgColor
|
|
36
|
+
private var cMarkActive = UIColor.clear.cgColor
|
|
37
|
+
private var cMarkInactive = UIColor.white.cgColor
|
|
38
|
+
private var cMarkBorder = UIColor.gray.cgColor
|
|
39
|
+
private var cBubble = UIColor.black.cgColor
|
|
40
|
+
private var cBubbleText = UIColor.white.cgColor
|
|
41
|
+
|
|
42
|
+
// MARK: - Interaction state
|
|
43
|
+
private var currentValue: Double = 0
|
|
44
|
+
private var lastEmitted: Double = .nan
|
|
45
|
+
private var isDragging = false
|
|
46
|
+
private var hasLaidOut = false
|
|
47
|
+
// Segment node the thumb currently sits on (-1 = none). Drives the halo +
|
|
48
|
+
// a light haptic tick fired once each time the thumb enters a new node.
|
|
49
|
+
private var hoverMarkIndex = -1
|
|
50
|
+
private lazy var haptic = UIImpactFeedbackGenerator(style: .light)
|
|
51
|
+
|
|
52
|
+
// MARK: - Props (required props are non-optional in the generated spec)
|
|
53
|
+
var value: Double = 0 {
|
|
54
|
+
didSet {
|
|
55
|
+
// While dragging, the finger owns the value — ignore controlled updates
|
|
56
|
+
// (web skips its useLayoutEffect when draggingRef.current is true).
|
|
57
|
+
if !isDragging {
|
|
58
|
+
currentValue = value
|
|
59
|
+
applyVisual(value)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
var min: Double = 0 { didSet { scheduleLayout() } }
|
|
64
|
+
var max: Double = 100 { didSet { scheduleLayout() } }
|
|
65
|
+
var segments: Double = 0 { didSet { scheduleLayout() } }
|
|
66
|
+
var sliderHeight: Double = 4 { didSet { scheduleLayout() } }
|
|
67
|
+
var disabled: Bool = false {
|
|
68
|
+
didSet {
|
|
69
|
+
view.alpha = disabled ? 0.5 : 1
|
|
70
|
+
view.isUserInteractionEnabled = !disabled
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
var showBubble: Bool = true { didSet { scheduleLayout() } }
|
|
74
|
+
var centerOrigin: Bool = false { didSet { scheduleLayout() } }
|
|
75
|
+
var epoch: Double = 0 { didSet { scheduleLayout() } }
|
|
76
|
+
|
|
77
|
+
var fillColor: String = "" {
|
|
78
|
+
didSet { cFill = parse(fillColor); fillLayer.backgroundColor = cFill; scheduleLayout() }
|
|
79
|
+
}
|
|
80
|
+
var trackColor: String = "" {
|
|
81
|
+
didSet { cTrack = parse(trackColor); trackLayer.backgroundColor = cTrack }
|
|
82
|
+
}
|
|
83
|
+
var thumbColor: String = "" {
|
|
84
|
+
didSet { cThumb = parse(thumbColor); thumbLayer.backgroundColor = cThumb }
|
|
85
|
+
}
|
|
86
|
+
var thumbBorderColor: String = "" {
|
|
87
|
+
didSet { cThumbBorder = parse(thumbBorderColor); thumbLayer.borderColor = cThumbBorder }
|
|
88
|
+
}
|
|
89
|
+
var markActiveColor: String = "" {
|
|
90
|
+
didSet { cMarkActive = parse(markActiveColor); scheduleLayout() }
|
|
91
|
+
}
|
|
92
|
+
var markInactiveColor: String = "" {
|
|
93
|
+
didSet { cMarkInactive = parse(markInactiveColor); scheduleLayout() }
|
|
94
|
+
}
|
|
95
|
+
var markBorderColor: String = "" {
|
|
96
|
+
didSet { cMarkBorder = parse(markBorderColor); scheduleLayout() }
|
|
97
|
+
}
|
|
98
|
+
var bubbleColor: String = "" {
|
|
99
|
+
didSet { cBubble = parse(bubbleColor); bubbleLayer.backgroundColor = cBubble }
|
|
100
|
+
}
|
|
101
|
+
var bubbleTextColor: String = "" {
|
|
102
|
+
didSet { cBubbleText = parse(bubbleTextColor); bubbleTextLayer.foregroundColor = cBubbleText }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// MARK: - Callbacks
|
|
106
|
+
var onChange: ((Double) -> Void)?
|
|
107
|
+
var onSlideStart: (() -> Void)?
|
|
108
|
+
var onSlideComplete: (() -> Void)?
|
|
109
|
+
|
|
110
|
+
// MARK: - Init
|
|
111
|
+
override init() {
|
|
112
|
+
super.init()
|
|
113
|
+
view.backgroundColor = .clear
|
|
114
|
+
view.isUserInteractionEnabled = true
|
|
115
|
+
|
|
116
|
+
trackLayer.cornerRadius = CGFloat(sliderHeight) / 2
|
|
117
|
+
fillLayer.cornerRadius = CGFloat(sliderHeight) / 2
|
|
118
|
+
|
|
119
|
+
thumbLayer.cornerRadius = thumbSize / 2
|
|
120
|
+
thumbLayer.borderWidth = 1
|
|
121
|
+
thumbLayer.shadowColor = UIColor.black.cgColor
|
|
122
|
+
thumbLayer.shadowOpacity = 0.08
|
|
123
|
+
thumbLayer.shadowRadius = 1
|
|
124
|
+
thumbLayer.shadowOffset = CGSize(width: 0, height: 1)
|
|
125
|
+
|
|
126
|
+
bubbleLayer.cornerRadius = 4
|
|
127
|
+
bubbleLayer.opacity = 0
|
|
128
|
+
bubbleTextLayer.contentsScale = UIScreen.main.scale
|
|
129
|
+
bubbleTextLayer.alignmentMode = .center
|
|
130
|
+
bubbleTextLayer.fontSize = 12
|
|
131
|
+
bubbleTextLayer.font = UIFont.systemFont(ofSize: 12, weight: .semibold)
|
|
132
|
+
bubbleLayer.addSublayer(bubbleTextLayer)
|
|
133
|
+
|
|
134
|
+
let haloSize = markSize + haloRing * 2
|
|
135
|
+
haloLayer.bounds = CGRect(x: 0, y: 0, width: haloSize, height: haloSize)
|
|
136
|
+
haloLayer.cornerRadius = haloSize / 2
|
|
137
|
+
haloLayer.opacity = 0
|
|
138
|
+
|
|
139
|
+
view.layer.addSublayer(trackLayer)
|
|
140
|
+
view.layer.addSublayer(fillLayer)
|
|
141
|
+
view.layer.addSublayer(thumbLayer)
|
|
142
|
+
view.layer.addSublayer(haloLayer)
|
|
143
|
+
view.layer.addSublayer(bubbleLayer)
|
|
144
|
+
|
|
145
|
+
if let v = view as? SegmentSliderUIView {
|
|
146
|
+
v.onLayout = { [weak self] in self?.performLayout() }
|
|
147
|
+
v.onTouchBegan = { [weak self] x in self?.handleTouchBegan(x) }
|
|
148
|
+
v.onTouchMoved = { [weak self] x in self?.handleTouchMoved(x) }
|
|
149
|
+
v.onTouchEnded = { [weak self] in self?.handleTouchEnded() }
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// MARK: - Color helper
|
|
154
|
+
private func parse(_ s: String) -> CGColor {
|
|
155
|
+
SegmentSliderColorParser.parse(s).cgColor
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private func scheduleLayout() {
|
|
159
|
+
view.setNeedsLayout()
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// MARK: - Geometry helpers
|
|
163
|
+
private var trackWidth: CGFloat { Swift.max(view.bounds.width - thumbSize, 0) }
|
|
164
|
+
private var trackLeft: CGFloat { inset }
|
|
165
|
+
private var range: Double { let r = max - min; return r == 0 ? 1 : r }
|
|
166
|
+
private var markCount: Int { segments >= 1 ? Int(segments) + 1 : 0 }
|
|
167
|
+
|
|
168
|
+
/// value -> fraction 0..1
|
|
169
|
+
private func valueToFrac(_ v: Double) -> CGFloat {
|
|
170
|
+
let clamped = Swift.max(min, Swift.min(max, v))
|
|
171
|
+
return CGFloat((clamped - min) / range)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private func valueFromX(_ x: CGFloat) -> Double {
|
|
175
|
+
let w = trackWidth
|
|
176
|
+
if w <= 0 { return currentValue }
|
|
177
|
+
let clampedX = Swift.max(trackLeft, Swift.min(trackLeft + w, x))
|
|
178
|
+
let frac = Double((clampedX - trackLeft) / w)
|
|
179
|
+
return min + frac * (max - min)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// MARK: - Layout
|
|
183
|
+
private func performLayout() {
|
|
184
|
+
let bounds = view.bounds
|
|
185
|
+
guard bounds.width > 0 else { return }
|
|
186
|
+
|
|
187
|
+
syncMarkLayers(markCount)
|
|
188
|
+
|
|
189
|
+
let h = CGFloat(sliderHeight)
|
|
190
|
+
let trackTop = bounds.midY - h / 2
|
|
191
|
+
|
|
192
|
+
CATransaction.begin()
|
|
193
|
+
CATransaction.setDisableActions(true)
|
|
194
|
+
|
|
195
|
+
trackLayer.frame = CGRect(x: trackLeft, y: trackTop, width: trackWidth, height: h)
|
|
196
|
+
trackLayer.cornerRadius = h / 2
|
|
197
|
+
trackLayer.backgroundColor = cTrack
|
|
198
|
+
fillLayer.cornerRadius = h / 2
|
|
199
|
+
|
|
200
|
+
// Marks sit on the track line, centred vertically on it.
|
|
201
|
+
for i in 0..<markLayers.count {
|
|
202
|
+
let layer = markLayers[i]
|
|
203
|
+
let frac = markCount > 1 ? CGFloat(i) / CGFloat(markCount - 1) : 0
|
|
204
|
+
let cx = trackLeft + frac * trackWidth
|
|
205
|
+
layer.bounds = CGRect(x: 0, y: 0, width: markSize, height: markSize)
|
|
206
|
+
layer.position = CGPoint(x: cx, y: bounds.midY)
|
|
207
|
+
layer.cornerRadius = markSize / 2
|
|
208
|
+
layer.borderWidth = 1
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Thumb sizing (position set in applyVisual).
|
|
212
|
+
thumbLayer.bounds = CGRect(x: 0, y: 0, width: thumbSize, height: thumbSize)
|
|
213
|
+
thumbLayer.cornerRadius = thumbSize / 2
|
|
214
|
+
thumbLayer.backgroundColor = cThumb
|
|
215
|
+
thumbLayer.borderColor = cThumbBorder
|
|
216
|
+
|
|
217
|
+
CATransaction.commit()
|
|
218
|
+
|
|
219
|
+
hasLaidOut = true
|
|
220
|
+
applyVisual(currentValue)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
private func syncMarkLayers(_ count: Int) {
|
|
224
|
+
if markLayers.count < count {
|
|
225
|
+
for _ in markLayers.count..<count {
|
|
226
|
+
let l = CALayer()
|
|
227
|
+
l.borderWidth = 1
|
|
228
|
+
// Marks render ABOVE the thumb + halo (web parity: mark zIndex 3 >
|
|
229
|
+
// thumb 2). So when the thumb sits on a segment node the node's dot
|
|
230
|
+
// shows through the thumb's centre — the "white ring + dark centre"
|
|
231
|
+
// bullseye look. Inserting above the halo keeps order
|
|
232
|
+
// track < fill < thumb < halo < marks < bubble.
|
|
233
|
+
view.layer.insertSublayer(l, above: haloLayer)
|
|
234
|
+
markLayers.append(l)
|
|
235
|
+
}
|
|
236
|
+
} else if markLayers.count > count {
|
|
237
|
+
for l in markLayers[count...] { l.removeFromSuperlayer() }
|
|
238
|
+
markLayers.removeLast(markLayers.count - count)
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// MARK: - Visual application (no implicit animations)
|
|
243
|
+
private func applyVisual(_ v: Double) {
|
|
244
|
+
guard hasLaidOut, view.bounds.width > 0 else { return }
|
|
245
|
+
let bounds = view.bounds
|
|
246
|
+
let h = CGFloat(sliderHeight)
|
|
247
|
+
let trackTop = bounds.midY - h / 2
|
|
248
|
+
let frac = valueToFrac(v)
|
|
249
|
+
let thumbX = trackLeft + frac * trackWidth
|
|
250
|
+
|
|
251
|
+
CATransaction.begin()
|
|
252
|
+
CATransaction.setDisableActions(true)
|
|
253
|
+
|
|
254
|
+
// Fill bar.
|
|
255
|
+
if centerOrigin {
|
|
256
|
+
if v == 0 {
|
|
257
|
+
fillLayer.frame = CGRect(x: trackLeft, y: trackTop, width: 0, height: h)
|
|
258
|
+
} else {
|
|
259
|
+
let centerFrac = valueToFrac(0)
|
|
260
|
+
let leftFrac = Swift.min(frac, centerFrac)
|
|
261
|
+
let widthFrac = abs(frac - centerFrac)
|
|
262
|
+
fillLayer.frame = CGRect(
|
|
263
|
+
x: trackLeft + leftFrac * trackWidth, y: trackTop,
|
|
264
|
+
width: widthFrac * trackWidth, height: h)
|
|
265
|
+
}
|
|
266
|
+
} else {
|
|
267
|
+
fillLayer.frame = CGRect(
|
|
268
|
+
x: trackLeft, y: trackTop, width: frac * trackWidth, height: h)
|
|
269
|
+
}
|
|
270
|
+
fillLayer.backgroundColor = cFill
|
|
271
|
+
|
|
272
|
+
// Marks active states.
|
|
273
|
+
let centerFrac = valueToFrac(0)
|
|
274
|
+
for i in 0..<markLayers.count {
|
|
275
|
+
let markFrac = markLayers.count > 1 ? CGFloat(i) / CGFloat(markLayers.count - 1) : 0
|
|
276
|
+
var active: Bool
|
|
277
|
+
if centerOrigin {
|
|
278
|
+
if v == 0 {
|
|
279
|
+
active = false
|
|
280
|
+
} else if v > 0 {
|
|
281
|
+
active = markFrac > centerFrac && markFrac <= frac
|
|
282
|
+
} else {
|
|
283
|
+
active = markFrac >= frac && markFrac < centerFrac
|
|
284
|
+
}
|
|
285
|
+
} else {
|
|
286
|
+
active = markFrac <= frac
|
|
287
|
+
}
|
|
288
|
+
let layer = markLayers[i]
|
|
289
|
+
layer.backgroundColor = active ? cMarkActive : cMarkInactive
|
|
290
|
+
layer.borderColor = active ? cMarkActive : cMarkBorder
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Thumb position (keep current scale transform).
|
|
294
|
+
thumbLayer.position = CGPoint(x: thumbX, y: bounds.midY)
|
|
295
|
+
|
|
296
|
+
// Bubble.
|
|
297
|
+
if showBubble {
|
|
298
|
+
let text = "\(Int(v.rounded()))%"
|
|
299
|
+
bubbleTextLayer.string = text
|
|
300
|
+
let font = UIFont.systemFont(ofSize: 12, weight: .semibold)
|
|
301
|
+
let textW = (text as NSString)
|
|
302
|
+
.size(withAttributes: [.font: font]).width
|
|
303
|
+
let padH: CGFloat = 8
|
|
304
|
+
let bw = ceil(textW) + padH * 2
|
|
305
|
+
let bh: CGFloat = 20
|
|
306
|
+
let by = trackTop - 8 - bh
|
|
307
|
+
bubbleLayer.frame = CGRect(x: thumbX - bw / 2, y: by, width: bw, height: bh)
|
|
308
|
+
bubbleTextLayer.frame = CGRect(x: 0, y: 2, width: bw, height: 16)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
CATransaction.commit()
|
|
312
|
+
|
|
313
|
+
updateHover(thumbX: thumbX)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// MARK: - Node halo + haptic (mobile analogue of the web mark hover)
|
|
317
|
+
/// Highlights the node the thumb is over and fires a light haptic tick once
|
|
318
|
+
/// per node entered. Only while dragging — controlled value syncs just keep
|
|
319
|
+
/// the halo hidden. Does NOT snap the value (parity with web/perp).
|
|
320
|
+
private func updateHover(thumbX: CGFloat) {
|
|
321
|
+
guard isDragging, markCount > 0 else {
|
|
322
|
+
if hoverMarkIndex != -1 { hoverMarkIndex = -1; setHalo(visible: false, index: 0) }
|
|
323
|
+
return
|
|
324
|
+
}
|
|
325
|
+
let threshold: CGFloat = 12 // half of the 24pt mark hit area
|
|
326
|
+
var best = -1
|
|
327
|
+
var bestDist = threshold
|
|
328
|
+
for i in 0..<markCount {
|
|
329
|
+
let frac = markCount > 1 ? CGFloat(i) / CGFloat(markCount - 1) : 0
|
|
330
|
+
let mx = trackLeft + frac * trackWidth
|
|
331
|
+
let d = abs(thumbX - mx)
|
|
332
|
+
if d <= bestDist { bestDist = d; best = i }
|
|
333
|
+
}
|
|
334
|
+
guard best != hoverMarkIndex else { return }
|
|
335
|
+
hoverMarkIndex = best
|
|
336
|
+
if best == -1 {
|
|
337
|
+
setHalo(visible: false, index: 0)
|
|
338
|
+
} else {
|
|
339
|
+
setHalo(visible: true, index: best)
|
|
340
|
+
haptic.impactOccurred(intensity: 0.6)
|
|
341
|
+
haptic.prepare()
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
private func setHalo(visible: Bool, index: Int) {
|
|
346
|
+
CATransaction.begin()
|
|
347
|
+
CATransaction.setDisableActions(true)
|
|
348
|
+
if visible {
|
|
349
|
+
let frac = markCount > 1 ? CGFloat(index) / CGFloat(markCount - 1) : 0
|
|
350
|
+
let mx = trackLeft + frac * trackWidth
|
|
351
|
+
haloLayer.position = CGPoint(x: mx, y: view.bounds.midY)
|
|
352
|
+
haloLayer.backgroundColor = UIColor(cgColor: cMarkActive)
|
|
353
|
+
.withAlphaComponent(0.25).cgColor
|
|
354
|
+
haloLayer.opacity = 1
|
|
355
|
+
} else {
|
|
356
|
+
haloLayer.opacity = 0
|
|
357
|
+
}
|
|
358
|
+
CATransaction.commit()
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// MARK: - Touch handling
|
|
362
|
+
private func handleTouchBegan(_ x: CGFloat) {
|
|
363
|
+
guard !disabled else { return }
|
|
364
|
+
isDragging = true
|
|
365
|
+
haptic.prepare()
|
|
366
|
+
onSlideStart?()
|
|
367
|
+
animateThumbScale(1.15)
|
|
368
|
+
setBubbleVisible(true)
|
|
369
|
+
setValue(valueFromX(x))
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
private func handleTouchMoved(_ x: CGFloat) {
|
|
373
|
+
guard isDragging else { return }
|
|
374
|
+
setValue(valueFromX(x))
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
private func handleTouchEnded() {
|
|
378
|
+
guard isDragging else { return }
|
|
379
|
+
isDragging = false
|
|
380
|
+
hoverMarkIndex = -1
|
|
381
|
+
setHalo(visible: false, index: 0)
|
|
382
|
+
animateThumbScale(1.0)
|
|
383
|
+
setBubbleVisible(false)
|
|
384
|
+
onSlideComplete?()
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
private func setValue(_ raw: Double) {
|
|
388
|
+
let clamped = Swift.max(min, Swift.min(max, raw))
|
|
389
|
+
let rounded = clamped.rounded()
|
|
390
|
+
currentValue = rounded
|
|
391
|
+
applyVisual(rounded)
|
|
392
|
+
if rounded != lastEmitted {
|
|
393
|
+
lastEmitted = rounded
|
|
394
|
+
onChange?(rounded)
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
private func animateThumbScale(_ s: CGFloat) {
|
|
399
|
+
CATransaction.begin()
|
|
400
|
+
CATransaction.setAnimationDuration(0.12)
|
|
401
|
+
thumbLayer.transform = CATransform3DMakeScale(s, s, 1)
|
|
402
|
+
CATransaction.commit()
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
private func setBubbleVisible(_ visible: Bool) {
|
|
406
|
+
guard showBubble else { return }
|
|
407
|
+
CATransaction.begin()
|
|
408
|
+
CATransaction.setAnimationDuration(0.15)
|
|
409
|
+
bubbleLayer.opacity = visible ? 1 : 0
|
|
410
|
+
CATransaction.commit()
|
|
411
|
+
}
|
|
412
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
/// Parses a color string coming from JS. Supports:
|
|
5
|
+
/// - `#rgb`, `#rrggbb`, `#rrggbbaa`
|
|
6
|
+
/// - `rgb(r, g, b)` / `rgba(r, g, b, a)` where a is 0..1
|
|
7
|
+
/// Falls back to `fallback` on parse failure.
|
|
8
|
+
enum SegmentSliderColorParser {
|
|
9
|
+
static func parse(_ value: String?, fallback: UIColor = .clear) -> UIColor {
|
|
10
|
+
guard let raw = value?.trimmingCharacters(in: .whitespacesAndNewlines),
|
|
11
|
+
!raw.isEmpty else {
|
|
12
|
+
return fallback
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if raw.hasPrefix("#") {
|
|
16
|
+
return parseHex(raw) ?? fallback
|
|
17
|
+
}
|
|
18
|
+
if raw.lowercased().hasPrefix("rgb") {
|
|
19
|
+
return parseRGB(raw) ?? fallback
|
|
20
|
+
}
|
|
21
|
+
return fallback
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private static func parseHex(_ hex: String) -> UIColor? {
|
|
25
|
+
var s = hex
|
|
26
|
+
s.removeFirst() // '#'
|
|
27
|
+
|
|
28
|
+
// Expand shorthand #rgb -> #rrggbb
|
|
29
|
+
if s.count == 3 {
|
|
30
|
+
s = s.map { "\($0)\($0)" }.joined()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
guard s.count == 6 || s.count == 8 else { return nil }
|
|
34
|
+
var int: UInt64 = 0
|
|
35
|
+
guard Scanner(string: s).scanHexInt64(&int) else { return nil }
|
|
36
|
+
|
|
37
|
+
let r, g, b, a: CGFloat
|
|
38
|
+
if s.count == 8 {
|
|
39
|
+
r = CGFloat((int >> 24) & 0xFF) / 255.0
|
|
40
|
+
g = CGFloat((int >> 16) & 0xFF) / 255.0
|
|
41
|
+
b = CGFloat((int >> 8) & 0xFF) / 255.0
|
|
42
|
+
a = CGFloat(int & 0xFF) / 255.0
|
|
43
|
+
} else {
|
|
44
|
+
r = CGFloat((int >> 16) & 0xFF) / 255.0
|
|
45
|
+
g = CGFloat((int >> 8) & 0xFF) / 255.0
|
|
46
|
+
b = CGFloat(int & 0xFF) / 255.0
|
|
47
|
+
a = 1.0
|
|
48
|
+
}
|
|
49
|
+
return UIColor(red: r, green: g, blue: b, alpha: a)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private static func parseRGB(_ value: String) -> UIColor? {
|
|
53
|
+
guard let open = value.firstIndex(of: "("),
|
|
54
|
+
let close = value.firstIndex(of: ")") else { return nil }
|
|
55
|
+
let inner = value[value.index(after: open)..<close]
|
|
56
|
+
let parts = inner.split(separator: ",").map {
|
|
57
|
+
$0.trimmingCharacters(in: .whitespaces)
|
|
58
|
+
}
|
|
59
|
+
guard parts.count >= 3,
|
|
60
|
+
let r = Double(parts[0]),
|
|
61
|
+
let g = Double(parts[1]),
|
|
62
|
+
let b = Double(parts[2]) else { return nil }
|
|
63
|
+
let a = parts.count >= 4 ? (Double(parts[3]) ?? 1.0) : 1.0
|
|
64
|
+
return UIColor(
|
|
65
|
+
red: CGFloat(r) / 255.0,
|
|
66
|
+
green: CGFloat(g) / 255.0,
|
|
67
|
+
blue: CGFloat(b) / 255.0,
|
|
68
|
+
alpha: CGFloat(a)
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/// UIControl subclass that forwards `layoutSubviews` and raw touch positions to
|
|
74
|
+
/// closures so the Nitro HybridView can re-lay-out its CALayers and drive the
|
|
75
|
+
/// drag entirely on the native UI thread.
|
|
76
|
+
///
|
|
77
|
+
/// It subclasses `UIControl` (not `UIView`) on purpose: an enclosing
|
|
78
|
+
/// `UIScrollView` returns `false` from `touchesShouldCancel(in:)` for
|
|
79
|
+
/// `UIControl` subviews, so a horizontal drag on the slider is NOT cancelled
|
|
80
|
+
/// and stolen by the scroll view's pan gesture. This is the same mechanism
|
|
81
|
+
/// `UISlider` relies on to stay draggable inside a scroll view.
|
|
82
|
+
final class SegmentSliderUIView: UIControl {
|
|
83
|
+
var onLayout: (() -> Void)?
|
|
84
|
+
/// Touch began at x (points, view coordinates).
|
|
85
|
+
var onTouchBegan: ((CGFloat) -> Void)?
|
|
86
|
+
/// Touch moved to x (points, view coordinates).
|
|
87
|
+
var onTouchMoved: ((CGFloat) -> Void)?
|
|
88
|
+
/// Touch ended or was cancelled.
|
|
89
|
+
var onTouchEnded: (() -> Void)?
|
|
90
|
+
|
|
91
|
+
override func layoutSubviews() {
|
|
92
|
+
super.layoutSubviews()
|
|
93
|
+
onLayout?()
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// While the user is dragging the slider, NO ancestor pan gesture may act —
|
|
97
|
+
// not the enclosing scroll view's scroll, not the navigation stack's
|
|
98
|
+
// swipe-back, not a parent pager. UIKit's UIControl exemption
|
|
99
|
+
// (`touchesShouldCancelInContentView:`) does NOT help, because the scroll
|
|
100
|
+
// view passes its *direct* content subview to that method, not this
|
|
101
|
+
// deeply-nested view. So on touch-down we walk the ancestor chain and
|
|
102
|
+
// temporarily disable every pan-type recognizer (UIScrollView pan,
|
|
103
|
+
// UIScreenEdgePan / RNSPanGestureRecognizer back-swipe, pager pans — all are
|
|
104
|
+
// UIPanGestureRecognizer subclasses), restoring them on touch-up. This is the
|
|
105
|
+
// iOS analogue of Android's `requestDisallowInterceptTouchEvent(true)`.
|
|
106
|
+
private var disabledRecognizers: [UIGestureRecognizer] = []
|
|
107
|
+
|
|
108
|
+
private func lockAncestorGestures() {
|
|
109
|
+
var v: UIView? = superview
|
|
110
|
+
while let cur = v {
|
|
111
|
+
if let grs = cur.gestureRecognizers {
|
|
112
|
+
for gr in grs where gr is UIPanGestureRecognizer && gr.isEnabled {
|
|
113
|
+
gr.isEnabled = false // cancels it if mid-recognition, blocks it otherwise
|
|
114
|
+
disabledRecognizers.append(gr)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
v = cur.superview
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private func unlockAncestorGestures() {
|
|
122
|
+
for gr in disabledRecognizers {
|
|
123
|
+
gr.isEnabled = true
|
|
124
|
+
}
|
|
125
|
+
disabledRecognizers.removeAll()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
|
|
129
|
+
super.touchesBegan(touches, with: event)
|
|
130
|
+
lockAncestorGestures()
|
|
131
|
+
guard let t = touches.first else { return }
|
|
132
|
+
onTouchBegan?(t.location(in: self).x)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
|
|
136
|
+
super.touchesMoved(touches, with: event)
|
|
137
|
+
guard let t = touches.first else { return }
|
|
138
|
+
onTouchMoved?(t.location(in: self).x)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
|
|
142
|
+
super.touchesEnded(touches, with: event)
|
|
143
|
+
unlockAncestorGestures()
|
|
144
|
+
onTouchEnded?()
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
|
|
148
|
+
super.touchesCancelled(touches, with: event)
|
|
149
|
+
unlockAncestorGestures()
|
|
150
|
+
onTouchEnded?()
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"../../src","sources":["SegmentSlider.nitro.ts"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useRef } from 'react';
|
|
4
|
+
import { callback, getHostComponent } from 'react-native-nitro-modules';
|
|
5
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
6
|
+
const SegmentSliderConfig = require('../nitrogen/generated/shared/json/SegmentSliderConfig.json');
|
|
7
|
+
const SegmentSliderHost = getHostComponent('SegmentSlider', () => SegmentSliderConfig);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Thin wrapper so consumers pass plain functions. Nitro view callbacks must be
|
|
11
|
+
* wrapped with `callback(...)` to cross the JSI boundary; we encapsulate that
|
|
12
|
+
* here behind refs so the underlying functions stay stable across re-renders
|
|
13
|
+
* (otherwise every parent render would re-create the native callback).
|
|
14
|
+
*/
|
|
15
|
+
export function SegmentSliderView({
|
|
16
|
+
onChange,
|
|
17
|
+
onSlideStart,
|
|
18
|
+
onSlideComplete,
|
|
19
|
+
...props
|
|
20
|
+
}) {
|
|
21
|
+
const onChangeRef = useRef(onChange);
|
|
22
|
+
onChangeRef.current = onChange;
|
|
23
|
+
const onSlideStartRef = useRef(onSlideStart);
|
|
24
|
+
onSlideStartRef.current = onSlideStart;
|
|
25
|
+
const onSlideCompleteRef = useRef(onSlideComplete);
|
|
26
|
+
onSlideCompleteRef.current = onSlideComplete;
|
|
27
|
+
const stableOnChange = useCallback(value => {
|
|
28
|
+
onChangeRef.current?.(value);
|
|
29
|
+
}, []);
|
|
30
|
+
const stableOnSlideStart = useCallback(() => {
|
|
31
|
+
onSlideStartRef.current?.();
|
|
32
|
+
}, []);
|
|
33
|
+
const stableOnSlideComplete = useCallback(() => {
|
|
34
|
+
onSlideCompleteRef.current?.();
|
|
35
|
+
}, []);
|
|
36
|
+
return /*#__PURE__*/_jsx(SegmentSliderHost, {
|
|
37
|
+
...props,
|
|
38
|
+
onChange: onChange ? callback(stableOnChange) : undefined,
|
|
39
|
+
onSlideStart: onSlideStart ? callback(stableOnSlideStart) : undefined,
|
|
40
|
+
onSlideComplete: onSlideComplete ? callback(stableOnSlideComplete) : undefined
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useCallback","useRef","callback","getHostComponent","jsx","_jsx","SegmentSliderConfig","require","SegmentSliderHost","SegmentSliderView","onChange","onSlideStart","onSlideComplete","props","onChangeRef","current","onSlideStartRef","onSlideCompleteRef","stableOnChange","value","stableOnSlideStart","stableOnSlideComplete","undefined"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,WAAW,EAAEC,MAAM,QAAQ,OAAO;AAG3C,SAASC,QAAQ,EAAEC,gBAAgB,QAAQ,4BAA4B;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAOxE,MAAMC,mBAAmB,GAAGC,OAAO,CAAC,4DAA4D,CAAC;AAIjG,MAAMC,iBAAiB,GAAGL,gBAAgB,CAGxC,eAAe,EAAE,MAAMG,mBAAmB,CAAC;;AAE7C;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASG,iBAAiBA,CAAC;EAChCC,QAAQ;EACRC,YAAY;EACZC,eAAe;EACf,GAAGC;AAC2B,CAAC,EAAE;EACjC,MAAMC,WAAW,GAAGb,MAAM,CAACS,QAAQ,CAAC;EACpCI,WAAW,CAACC,OAAO,GAAGL,QAAQ;EAC9B,MAAMM,eAAe,GAAGf,MAAM,CAACU,YAAY,CAAC;EAC5CK,eAAe,CAACD,OAAO,GAAGJ,YAAY;EACtC,MAAMM,kBAAkB,GAAGhB,MAAM,CAACW,eAAe,CAAC;EAClDK,kBAAkB,CAACF,OAAO,GAAGH,eAAe;EAE5C,MAAMM,cAAc,GAAGlB,WAAW,CAAEmB,KAAa,IAAK;IACpDL,WAAW,CAACC,OAAO,GAAGI,KAAK,CAAC;EAC9B,CAAC,EAAE,EAAE,CAAC;EACN,MAAMC,kBAAkB,GAAGpB,WAAW,CAAC,MAAM;IAC3CgB,eAAe,CAACD,OAAO,GAAG,CAAC;EAC7B,CAAC,EAAE,EAAE,CAAC;EACN,MAAMM,qBAAqB,GAAGrB,WAAW,CAAC,MAAM;IAC9CiB,kBAAkB,CAACF,OAAO,GAAG,CAAC;EAChC,CAAC,EAAE,EAAE,CAAC;EAEN,oBACEV,IAAA,CAACG,iBAAiB;IAAA,GACZK,KAAK;IACTH,QAAQ,EAAEA,QAAQ,GAAGR,QAAQ,CAACgB,cAAc,CAAC,GAAGI,SAAU;IAC1DX,YAAY,EAAEA,YAAY,GAAGT,QAAQ,CAACkB,kBAAkB,CAAC,GAAGE,SAAU;IACtEV,eAAe,EACbA,eAAe,GAAGV,QAAQ,CAACmB,qBAAqB,CAAC,GAAGC;EACrD,CACF,CAAC;AAEN","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
///
|
|
2
|
+
/// JFunc_void.hpp
|
|
3
|
+
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
|
+
/// https://github.com/mrousavy/nitro
|
|
5
|
+
/// Copyright © 2026 Marc Rousavy @ Margelo
|
|
6
|
+
///
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <fbjni/fbjni.h>
|
|
11
|
+
#include <functional>
|
|
12
|
+
|
|
13
|
+
#include <functional>
|
|
14
|
+
#include <NitroModules/JNICallable.hpp>
|
|
15
|
+
|
|
16
|
+
namespace margelo::nitro::segmentslider {
|
|
17
|
+
|
|
18
|
+
using namespace facebook;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Represents the Java/Kotlin callback `() -> Unit`.
|
|
22
|
+
* This can be passed around between C++ and Java/Kotlin.
|
|
23
|
+
*/
|
|
24
|
+
struct JFunc_void: public jni::JavaClass<JFunc_void> {
|
|
25
|
+
public:
|
|
26
|
+
static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/segmentslider/Func_void;";
|
|
27
|
+
|
|
28
|
+
public:
|
|
29
|
+
/**
|
|
30
|
+
* Invokes the function this `JFunc_void` instance holds through JNI.
|
|
31
|
+
*/
|
|
32
|
+
void invoke() const {
|
|
33
|
+
static const auto method = javaClassStatic()->getMethod<void()>("invoke");
|
|
34
|
+
method(self());
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* An implementation of Func_void that is backed by a C++ implementation (using `std::function<...>`)
|
|
40
|
+
*/
|
|
41
|
+
class JFunc_void_cxx final: public jni::HybridClass<JFunc_void_cxx, JFunc_void> {
|
|
42
|
+
public:
|
|
43
|
+
static jni::local_ref<JFunc_void::javaobject> fromCpp(const std::function<void()>& func) {
|
|
44
|
+
return JFunc_void_cxx::newObjectCxxArgs(func);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
public:
|
|
48
|
+
/**
|
|
49
|
+
* Invokes the C++ `std::function<...>` this `JFunc_void_cxx` instance holds.
|
|
50
|
+
*/
|
|
51
|
+
void invoke_cxx() {
|
|
52
|
+
_func();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public:
|
|
56
|
+
[[nodiscard]]
|
|
57
|
+
inline const std::function<void()>& getFunction() const {
|
|
58
|
+
return _func;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public:
|
|
62
|
+
static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/segmentslider/Func_void_cxx;";
|
|
63
|
+
static void registerNatives() {
|
|
64
|
+
registerHybrid({makeNativeMethod("invoke_cxx", JFunc_void_cxx::invoke_cxx)});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private:
|
|
68
|
+
explicit JFunc_void_cxx(const std::function<void()>& func): _func(func) { }
|
|
69
|
+
|
|
70
|
+
private:
|
|
71
|
+
friend HybridBase;
|
|
72
|
+
std::function<void()> _func;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
} // namespace margelo::nitro::segmentslider
|