@webority-technologies/mobile 0.0.23 → 0.0.24
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/lib/commonjs/components/Accordion/Accordion.js +5 -5
- package/lib/commonjs/components/AnimatePresence/AnimatePresence.js +69 -0
- package/lib/commonjs/components/AnimatePresence/index.js +13 -0
- package/lib/commonjs/components/AppBar/AppBar.js +9 -6
- package/lib/commonjs/components/Banner/Banner.js +12 -2
- package/lib/commonjs/components/Card/Card.js +3 -3
- package/lib/commonjs/components/Checkbox/Checkbox.js +3 -2
- package/lib/commonjs/components/Chip/Chip.js +4 -2
- package/lib/commonjs/components/DatePicker/DatePicker.js +23 -18
- package/lib/commonjs/components/DateRangePicker/DateRangePicker.js +11 -9
- package/lib/commonjs/components/Dialog/Dialog.js +4 -2
- package/lib/commonjs/components/Drawer/Drawer.js +4 -2
- package/lib/commonjs/components/FloatingActionButton/FloatingActionButton.js +10 -8
- package/lib/commonjs/components/ImageGallery/ImageGallery.js +17 -15
- package/lib/commonjs/components/ListItem/ListItem.js +4 -3
- package/lib/commonjs/components/Modal/Modal.js +4 -3
- package/lib/commonjs/components/NumberInput/NumberInput.js +7 -5
- package/lib/commonjs/components/OTPInput/OTPInput.js +7 -7
- package/lib/commonjs/components/Radio/Radio.js +2 -3
- package/lib/commonjs/components/Rating/Rating.js +4 -3
- package/lib/commonjs/components/SearchBar/SearchBar.js +7 -4
- package/lib/commonjs/components/SegmentedControl/SegmentedControl.js +4 -3
- package/lib/commonjs/components/Select/Select.js +7 -4
- package/lib/commonjs/components/Slider/Slider.js +228 -228
- package/lib/commonjs/components/Stepper/Stepper.js +6 -5
- package/lib/commonjs/components/Swipeable/Swipeable.js +8 -9
- package/lib/commonjs/components/Tabs/Tabs.js +4 -3
- package/lib/commonjs/components/TimePicker/TimePicker.js +14 -9
- package/lib/commonjs/components/index.js +121 -114
- package/lib/commonjs/utils/hapticUtils.js +11 -1
- package/lib/commonjs/utils/index.js +6 -0
- package/lib/module/components/Accordion/Accordion.js +6 -6
- package/lib/module/components/AnimatePresence/AnimatePresence.js +63 -0
- package/lib/module/components/AnimatePresence/index.js +4 -0
- package/lib/module/components/AppBar/AppBar.js +10 -7
- package/lib/module/components/Banner/Banner.js +12 -2
- package/lib/module/components/Card/Card.js +4 -4
- package/lib/module/components/Checkbox/Checkbox.js +4 -3
- package/lib/module/components/Chip/Chip.js +5 -3
- package/lib/module/components/DatePicker/DatePicker.js +24 -19
- package/lib/module/components/DateRangePicker/DateRangePicker.js +12 -10
- package/lib/module/components/Dialog/Dialog.js +5 -3
- package/lib/module/components/Drawer/Drawer.js +5 -3
- package/lib/module/components/FloatingActionButton/FloatingActionButton.js +11 -9
- package/lib/module/components/ImageGallery/ImageGallery.js +18 -16
- package/lib/module/components/ListItem/ListItem.js +5 -4
- package/lib/module/components/Modal/Modal.js +5 -4
- package/lib/module/components/NumberInput/NumberInput.js +8 -6
- package/lib/module/components/OTPInput/OTPInput.js +8 -8
- package/lib/module/components/Radio/Radio.js +3 -4
- package/lib/module/components/Rating/Rating.js +5 -4
- package/lib/module/components/SearchBar/SearchBar.js +8 -5
- package/lib/module/components/SegmentedControl/SegmentedControl.js +5 -4
- package/lib/module/components/Select/Select.js +8 -5
- package/lib/module/components/Slider/Slider.js +231 -231
- package/lib/module/components/Stepper/Stepper.js +7 -6
- package/lib/module/components/Swipeable/Swipeable.js +9 -10
- package/lib/module/components/Tabs/Tabs.js +5 -4
- package/lib/module/components/TimePicker/TimePicker.js +15 -10
- package/lib/module/components/index.js +1 -0
- package/lib/module/utils/hapticUtils.js +9 -0
- package/lib/module/utils/index.js +1 -1
- package/lib/typescript/commonjs/components/Accordion/Accordion.d.ts +3 -0
- package/lib/typescript/commonjs/components/AnimatePresence/AnimatePresence.d.ts +30 -0
- package/lib/typescript/commonjs/components/AnimatePresence/index.d.ts +3 -0
- package/lib/typescript/commonjs/components/AppBar/AppBar.d.ts +6 -0
- package/lib/typescript/commonjs/components/Banner/Banner.d.ts +3 -0
- package/lib/typescript/commonjs/components/Card/Card.d.ts +3 -0
- package/lib/typescript/commonjs/components/Checkbox/Checkbox.d.ts +1 -0
- package/lib/typescript/commonjs/components/Chip/Chip.d.ts +3 -0
- package/lib/typescript/commonjs/components/DatePicker/DatePicker.d.ts +3 -0
- package/lib/typescript/commonjs/components/DateRangePicker/DateRangePicker.d.ts +6 -0
- package/lib/typescript/commonjs/components/Dialog/Dialog.d.ts +3 -0
- package/lib/typescript/commonjs/components/Drawer/Drawer.d.ts +3 -0
- package/lib/typescript/commonjs/components/FloatingActionButton/FloatingActionButton.d.ts +5 -0
- package/lib/typescript/commonjs/components/ImageGallery/ImageGallery.d.ts +6 -0
- package/lib/typescript/commonjs/components/ListItem/ListItem.d.ts +3 -0
- package/lib/typescript/commonjs/components/Modal/Modal.d.ts +6 -0
- package/lib/typescript/commonjs/components/NumberInput/NumberInput.d.ts +3 -0
- package/lib/typescript/commonjs/components/OTPInput/OTPInput.d.ts +6 -0
- package/lib/typescript/commonjs/components/Rating/Rating.d.ts +6 -0
- package/lib/typescript/commonjs/components/SearchBar/SearchBar.d.ts +3 -0
- package/lib/typescript/commonjs/components/SegmentedControl/SegmentedControl.d.ts +3 -0
- package/lib/typescript/commonjs/components/Select/Select.d.ts +6 -0
- package/lib/typescript/commonjs/components/Slider/Slider.d.ts +3 -0
- package/lib/typescript/commonjs/components/Stepper/Stepper.d.ts +6 -0
- package/lib/typescript/commonjs/components/Swipeable/Swipeable.d.ts +3 -0
- package/lib/typescript/commonjs/components/Tabs/Tabs.d.ts +3 -0
- package/lib/typescript/commonjs/components/TimePicker/TimePicker.d.ts +3 -0
- package/lib/typescript/commonjs/components/index.d.ts +2 -0
- package/lib/typescript/commonjs/theme/types.d.ts +2 -67
- package/lib/typescript/commonjs/utils/hapticUtils.d.ts +8 -0
- package/lib/typescript/commonjs/utils/index.d.ts +1 -1
- package/lib/typescript/module/components/Accordion/Accordion.d.ts +3 -0
- package/lib/typescript/module/components/AnimatePresence/AnimatePresence.d.ts +30 -0
- package/lib/typescript/module/components/AnimatePresence/index.d.ts +3 -0
- package/lib/typescript/module/components/AppBar/AppBar.d.ts +6 -0
- package/lib/typescript/module/components/Banner/Banner.d.ts +3 -0
- package/lib/typescript/module/components/Card/Card.d.ts +3 -0
- package/lib/typescript/module/components/Checkbox/Checkbox.d.ts +1 -0
- package/lib/typescript/module/components/Chip/Chip.d.ts +3 -0
- package/lib/typescript/module/components/DatePicker/DatePicker.d.ts +3 -0
- package/lib/typescript/module/components/DateRangePicker/DateRangePicker.d.ts +6 -0
- package/lib/typescript/module/components/Dialog/Dialog.d.ts +3 -0
- package/lib/typescript/module/components/Drawer/Drawer.d.ts +3 -0
- package/lib/typescript/module/components/FloatingActionButton/FloatingActionButton.d.ts +5 -0
- package/lib/typescript/module/components/ImageGallery/ImageGallery.d.ts +6 -0
- package/lib/typescript/module/components/ListItem/ListItem.d.ts +3 -0
- package/lib/typescript/module/components/Modal/Modal.d.ts +6 -0
- package/lib/typescript/module/components/NumberInput/NumberInput.d.ts +3 -0
- package/lib/typescript/module/components/OTPInput/OTPInput.d.ts +6 -0
- package/lib/typescript/module/components/Rating/Rating.d.ts +6 -0
- package/lib/typescript/module/components/SearchBar/SearchBar.d.ts +3 -0
- package/lib/typescript/module/components/SegmentedControl/SegmentedControl.d.ts +3 -0
- package/lib/typescript/module/components/Select/Select.d.ts +6 -0
- package/lib/typescript/module/components/Slider/Slider.d.ts +3 -0
- package/lib/typescript/module/components/Stepper/Stepper.d.ts +6 -0
- package/lib/typescript/module/components/Swipeable/Swipeable.d.ts +3 -0
- package/lib/typescript/module/components/Tabs/Tabs.d.ts +3 -0
- package/lib/typescript/module/components/TimePicker/TimePicker.d.ts +3 -0
- package/lib/typescript/module/components/index.d.ts +2 -0
- package/lib/typescript/module/theme/types.d.ts +2 -67
- package/lib/typescript/module/utils/hapticUtils.d.ts +8 -0
- package/lib/typescript/module/utils/index.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import React, { forwardRef, useCallback, useEffect, useMemo,
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import React, { forwardRef, useCallback, useEffect, useMemo, useState } from 'react';
|
|
4
|
+
import { Platform, Pressable, StyleSheet, Text, View } from 'react-native';
|
|
5
|
+
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
6
|
+
import Animated, { Easing, cancelAnimation, runOnJS, useAnimatedStyle, useSharedValue, withSpring, withTiming } from 'react-native-reanimated';
|
|
7
|
+
import { useTheme } from "../../theme/index.js";
|
|
8
|
+
import { resolveHaptic, triggerHaptic } from "../../utils/index.js";
|
|
7
9
|
import { useControllableState } from "../../hooks/index.js";
|
|
8
10
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
11
|
const SIZE_MAP = {
|
|
@@ -30,7 +32,6 @@ const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
|
30
32
|
const snapToStep = (value, min, max, step) => {
|
|
31
33
|
if (step <= 0) return clamp(value, min, max);
|
|
32
34
|
const stepped = Math.round((value - min) / step) * step + min;
|
|
33
|
-
// Guard against floating-point drift.
|
|
34
35
|
const decimals = String(step).split('.')[1]?.length ?? 0;
|
|
35
36
|
const fixed = decimals > 0 ? Number(stepped.toFixed(decimals)) : stepped;
|
|
36
37
|
return clamp(fixed, min, max);
|
|
@@ -47,6 +48,7 @@ const Slider = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
47
48
|
showLabel = false,
|
|
48
49
|
formatLabel = defaultFormatLabel,
|
|
49
50
|
accessibilityLabel,
|
|
51
|
+
haptic,
|
|
50
52
|
style,
|
|
51
53
|
containerStyle,
|
|
52
54
|
trackStyle,
|
|
@@ -61,17 +63,20 @@ const Slider = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
61
63
|
const theme = useTheme();
|
|
62
64
|
const sliderTheme = theme.components.slider;
|
|
63
65
|
const sliderSizeTheme = sliderTheme?.[size];
|
|
64
|
-
const sizeCfg = {
|
|
66
|
+
const sizeCfg = useMemo(() => ({
|
|
65
67
|
trackHeight: sliderSizeTheme?.trackHeight ?? SIZE_MAP[size].trackHeight,
|
|
66
68
|
thumbDiameter: sliderSizeTheme?.thumbDiameter ?? SIZE_MAP[size].thumbDiameter
|
|
67
|
-
};
|
|
69
|
+
}), [sliderSizeTheme, size]);
|
|
68
70
|
const thumbPressAnimationEnabled = sliderTheme?.thumbPressAnimation ?? false;
|
|
69
71
|
const resolvedPressScale = thumbPressScale ?? sliderTheme?.thumbPressScale ?? 1.1;
|
|
70
72
|
const resolvedLabelDuration = labelShowDuration ?? sliderTheme?.labelShowDuration ?? theme.motion.duration.fast;
|
|
71
73
|
const resolvedLabelOpacity = labelOpacityProp ?? sliderTheme?.labelOpacity ?? 1;
|
|
72
74
|
const styles = useMemo(() => buildStyles(theme, sizeCfg), [theme, sizeCfg]);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
const springCfg = useMemo(() => ({
|
|
76
|
+
damping: theme.motion.spring.snappy.damping,
|
|
77
|
+
stiffness: theme.motion.spring.snappy.stiffness,
|
|
78
|
+
mass: theme.motion.spring.snappy.mass
|
|
79
|
+
}), [theme.motion.spring.snappy]);
|
|
75
80
|
const [trackWidth, setTrackWidth] = useState(0);
|
|
76
81
|
const fillColor = theme.colors[TONE_TO_COLOR_KEY[tone]];
|
|
77
82
|
const isRange = props.range === true;
|
|
@@ -81,217 +86,218 @@ const Slider = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
81
86
|
onChange: props.onChange
|
|
82
87
|
});
|
|
83
88
|
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
// Label opacity per thumb.
|
|
99
|
-
const lowLabelOpacity = useRef(createAnimatedValue(0)).current;
|
|
100
|
-
const highLabelOpacity = useRef(createAnimatedValue(0)).current;
|
|
89
|
+
// ───────── Shared values (UI thread) ─────────
|
|
90
|
+
// lowX/highX: thumb px positions. lowVal/highVal: their snapped numeric values
|
|
91
|
+
// (mirrored on the UI thread so worklets can clamp the range + detect changes).
|
|
92
|
+
const lowX = useSharedValue(0);
|
|
93
|
+
const highX = useSharedValue(0);
|
|
94
|
+
const lowVal = useSharedValue(isRange ? currentValue[0] : currentValue);
|
|
95
|
+
const highVal = useSharedValue(isRange ? currentValue[1] : currentValue);
|
|
96
|
+
const dragStartX = useSharedValue(0);
|
|
97
|
+
const trackW = useSharedValue(0);
|
|
98
|
+
const lowScale = useSharedValue(1);
|
|
99
|
+
const highScale = useSharedValue(1);
|
|
100
|
+
const lowLabelOpacity = useSharedValue(0);
|
|
101
|
+
const highLabelOpacity = useSharedValue(0);
|
|
101
102
|
|
|
102
|
-
//
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
// Float-drift guard precision for step snapping (e.g. step 0.1 -> precision 10).
|
|
104
|
+
const precision = useMemo(() => {
|
|
105
|
+
const decimals = String(step).split('.')[1]?.length ?? 0;
|
|
106
|
+
return Math.pow(10, decimals);
|
|
107
|
+
}, [step]);
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
return () => {
|
|
110
|
+
cancelAnimation(lowX);
|
|
111
|
+
cancelAnimation(highX);
|
|
112
|
+
cancelAnimation(lowScale);
|
|
113
|
+
cancelAnimation(highScale);
|
|
114
|
+
cancelAnimation(lowLabelOpacity);
|
|
115
|
+
cancelAnimation(highLabelOpacity);
|
|
116
|
+
};
|
|
117
|
+
}, [lowX, highX, lowScale, highScale, lowLabelOpacity, highLabelOpacity]);
|
|
106
118
|
const valueToPx = useCallback((value, width) => {
|
|
107
119
|
if (max === min || width <= 0) return 0;
|
|
108
|
-
|
|
109
|
-
return ratio * width;
|
|
120
|
+
return (value - min) / (max - min) * width;
|
|
110
121
|
}, [min, max]);
|
|
111
|
-
|
|
112
|
-
// Derived helper: convert px -> snapped value.
|
|
113
122
|
const pxToValue = useCallback((px, width) => {
|
|
114
123
|
if (width <= 0) return min;
|
|
115
124
|
const ratio = clamp(px / width, 0, 1);
|
|
116
|
-
|
|
117
|
-
return snapToStep(raw, min, max, step);
|
|
125
|
+
return snapToStep(min + ratio * (max - min), min, max, step);
|
|
118
126
|
}, [min, max, step]);
|
|
119
127
|
|
|
120
|
-
// Sync external value ->
|
|
128
|
+
// Sync external value -> shared values (controlled updates, value prop changes).
|
|
121
129
|
useEffect(() => {
|
|
122
130
|
if (trackWidth <= 0) return;
|
|
123
131
|
if (isRange) {
|
|
124
132
|
const [v0, v1] = currentValue;
|
|
125
133
|
const lo = Math.min(v0, v1);
|
|
126
134
|
const hi = Math.max(v0, v1);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
setNativeValue(lowX, valueToPx(lo, trackWidth));
|
|
132
|
-
setNativeValue(highX, valueToPx(hi, trackWidth));
|
|
135
|
+
lowVal.value = lo;
|
|
136
|
+
highVal.value = hi;
|
|
137
|
+
lowX.value = valueToPx(lo, trackWidth);
|
|
138
|
+
highX.value = valueToPx(hi, trackWidth);
|
|
133
139
|
} else {
|
|
134
140
|
const v = currentValue;
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
setNativeValue(lowX, valueToPx(v, trackWidth));
|
|
141
|
+
lowVal.value = v;
|
|
142
|
+
highVal.value = v;
|
|
143
|
+
lowX.value = valueToPx(v, trackWidth);
|
|
139
144
|
}
|
|
140
|
-
// We intentionally listen to the value across both shapes via JSON; ESLint is fine.
|
|
141
145
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
142
146
|
}, [JSON.stringify(currentValue), trackWidth, isRange, valueToPx]);
|
|
143
147
|
const onTrackLayout = useCallback(e => {
|
|
144
148
|
const w = e.nativeEvent.layout.width;
|
|
149
|
+
trackW.value = w;
|
|
145
150
|
setTrackWidth(w);
|
|
146
|
-
|
|
147
|
-
if (isRange) {
|
|
148
|
-
const [v0, v1] = currentValue;
|
|
149
|
-
const lo = Math.min(v0, v1);
|
|
150
|
-
const hi = Math.max(v0, v1);
|
|
151
|
-
setNativeValue(lowX, valueToPx(lo, w));
|
|
152
|
-
setNativeValue(highX, valueToPx(hi, w));
|
|
153
|
-
} else {
|
|
154
|
-
setNativeValue(lowX, valueToPx(currentValue, w));
|
|
155
|
-
}
|
|
156
|
-
},
|
|
157
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
158
|
-
[isRange, valueToPx]);
|
|
151
|
+
}, [trackW]);
|
|
159
152
|
const fireChange = useCallback((lo, hi) => {
|
|
160
153
|
setCurrentValue(isRange ? [lo, hi] : lo);
|
|
161
154
|
}, [isRange, setCurrentValue]);
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
damping: theme.motion.spring.snappy.damping,
|
|
167
|
-
stiffness: theme.motion.spring.snappy.stiffness,
|
|
168
|
-
mass: theme.motion.spring.snappy.mass,
|
|
169
|
-
useNativeDriver: true
|
|
170
|
-
}).start();
|
|
171
|
-
}
|
|
172
|
-
if (showLabel) {
|
|
173
|
-
Animated.timing(opacity, {
|
|
174
|
-
toValue: pressed ? resolvedLabelOpacity : 0,
|
|
175
|
-
duration: resolvedLabelDuration,
|
|
176
|
-
easing: Easing.out(Easing.cubic),
|
|
177
|
-
useNativeDriver: true
|
|
178
|
-
}).start();
|
|
179
|
-
}
|
|
180
|
-
}, [showLabel, theme.motion, thumbPressAnimationEnabled, resolvedPressScale, resolvedLabelDuration, resolvedLabelOpacity]);
|
|
155
|
+
const fireHaptic = useCallback(() => {
|
|
156
|
+
const h = resolveHaptic(haptic, 'selection');
|
|
157
|
+
if (h) triggerHaptic(h);
|
|
158
|
+
}, [haptic]);
|
|
181
159
|
|
|
182
|
-
//
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
160
|
+
// ───────── Pan gesture per thumb (UI thread) ─────────
|
|
161
|
+
const buildPan = useCallback(which => {
|
|
162
|
+
const xSV = which === 'low' ? lowX : highX;
|
|
163
|
+
const valSV = which === 'low' ? lowVal : highVal;
|
|
164
|
+
const otherSV = which === 'low' ? highVal : lowVal;
|
|
165
|
+
const scaleSV = which === 'low' ? lowScale : highScale;
|
|
166
|
+
const labelSV = which === 'low' ? lowLabelOpacity : highLabelOpacity;
|
|
167
|
+
const isLow = which === 'low';
|
|
168
|
+
const _min = min;
|
|
169
|
+
const _max = max;
|
|
170
|
+
const _step = step;
|
|
171
|
+
const _precision = precision;
|
|
172
|
+
const _range = isRange;
|
|
173
|
+
const _pressScale = resolvedPressScale;
|
|
174
|
+
const _pressAnim = thumbPressAnimationEnabled;
|
|
175
|
+
const _showLabel = showLabel;
|
|
176
|
+
const _labelOpacity = resolvedLabelOpacity;
|
|
177
|
+
const _labelDur = resolvedLabelDuration;
|
|
178
|
+
const _spring = springCfg;
|
|
179
|
+
return Gesture.Pan().enabled(!disabled).minDistance(0).onStart(() => {
|
|
180
|
+
'worklet';
|
|
196
181
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (sliderTheme?.stepHaptic ?? false) triggerHaptic('selection');
|
|
205
|
-
lastReportedLow.current = nextValue;
|
|
206
|
-
lowRef.current = nextValue;
|
|
207
|
-
setNativeValue(lowX, valueToPx(nextValue, trackWidth));
|
|
208
|
-
fireChange(nextValue, highRef.current);
|
|
209
|
-
}
|
|
210
|
-
} else {
|
|
211
|
-
if (isRange && nextValue < lowRef.current) nextValue = lowRef.current;
|
|
212
|
-
if (nextValue !== lastReportedHigh.current) {
|
|
213
|
-
if (sliderTheme?.stepHaptic ?? false) triggerHaptic('selection');
|
|
214
|
-
lastReportedHigh.current = nextValue;
|
|
215
|
-
highRef.current = nextValue;
|
|
216
|
-
setNativeValue(highX, valueToPx(nextValue, trackWidth));
|
|
217
|
-
fireChange(lowRef.current, nextValue);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
},
|
|
221
|
-
onPanResponderRelease: () => {
|
|
222
|
-
animateThumbPress(which === 'low' ? lowScale : highScale, which === 'low' ? lowLabelOpacity : highLabelOpacity, false);
|
|
223
|
-
},
|
|
224
|
-
onPanResponderTerminate: () => {
|
|
225
|
-
animateThumbPress(which === 'low' ? lowScale : highScale, which === 'low' ? lowLabelOpacity : highLabelOpacity, false);
|
|
182
|
+
dragStartX.value = xSV.value;
|
|
183
|
+
if (_pressAnim) scaleSV.value = withSpring(_pressScale, _spring);
|
|
184
|
+
if (_showLabel) {
|
|
185
|
+
labelSV.value = withTiming(_labelOpacity, {
|
|
186
|
+
duration: _labelDur,
|
|
187
|
+
easing: Easing.out(Easing.cubic)
|
|
188
|
+
});
|
|
226
189
|
}
|
|
227
|
-
|
|
228
|
-
|
|
190
|
+
runOnJS(fireHaptic)();
|
|
191
|
+
}).onUpdate(event => {
|
|
192
|
+
'worklet';
|
|
193
|
+
|
|
194
|
+
if (trackW.value <= 0) return;
|
|
195
|
+
const px = Math.min(Math.max(dragStartX.value + event.translationX, 0), trackW.value);
|
|
196
|
+
const ratio = px / trackW.value;
|
|
197
|
+
let v = _min + ratio * (_max - _min);
|
|
198
|
+
if (_step > 0) {
|
|
199
|
+
v = Math.round((v - _min) / _step) * _step + _min;
|
|
200
|
+
v = Math.round(v * _precision) / _precision;
|
|
201
|
+
}
|
|
202
|
+
v = Math.min(Math.max(v, _min), _max);
|
|
203
|
+
if (_range) {
|
|
204
|
+
if (isLow && v > otherSV.value) v = otherSV.value;
|
|
205
|
+
if (!isLow && v < otherSV.value) v = otherSV.value;
|
|
206
|
+
}
|
|
207
|
+
// Thumb sits at the snapped value's position.
|
|
208
|
+
xSV.value = _max === _min ? 0 : (v - _min) / (_max - _min) * trackW.value;
|
|
209
|
+
if (v !== valSV.value) {
|
|
210
|
+
valSV.value = v;
|
|
211
|
+
runOnJS(fireHaptic)();
|
|
212
|
+
runOnJS(fireChange)(lowVal.value, highVal.value);
|
|
213
|
+
}
|
|
214
|
+
}).onEnd(() => {
|
|
215
|
+
'worklet';
|
|
229
216
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
217
|
+
if (_pressAnim) scaleSV.value = withSpring(1, _spring);
|
|
218
|
+
if (_showLabel) {
|
|
219
|
+
labelSV.value = withTiming(0, {
|
|
220
|
+
duration: _labelDur,
|
|
221
|
+
easing: Easing.out(Easing.cubic)
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
}, [disabled, min, max, step, precision, isRange, resolvedPressScale, thumbPressAnimationEnabled, showLabel, resolvedLabelOpacity, resolvedLabelDuration, springCfg, fireChange, fireHaptic, lowX, highX, lowVal, highVal, lowScale, highScale, lowLabelOpacity, highLabelOpacity, dragStartX, trackW]);
|
|
226
|
+
const lowPan = useMemo(() => buildPan('low'), [buildPan]);
|
|
227
|
+
const highPan = useMemo(() => buildPan('high'), [buildPan]);
|
|
233
228
|
|
|
234
229
|
// Tap on track jumps to position (single mode only).
|
|
235
230
|
const handleTrackPress = useCallback(e => {
|
|
236
231
|
if (disabled || isRange || trackWidth <= 0) return;
|
|
237
232
|
const px = clamp(e.nativeEvent.locationX, 0, trackWidth);
|
|
238
233
|
const next = pxToValue(px, trackWidth);
|
|
239
|
-
if (next !==
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
Animated.timing(lowX, {
|
|
244
|
-
toValue: valueToPx(next, trackWidth),
|
|
234
|
+
if (next !== lowVal.value) {
|
|
235
|
+
fireHaptic();
|
|
236
|
+
lowVal.value = next;
|
|
237
|
+
lowX.value = withTiming(valueToPx(next, trackWidth), {
|
|
245
238
|
duration: theme.motion.duration.fast,
|
|
246
|
-
easing: Easing.out(Easing.cubic)
|
|
247
|
-
|
|
248
|
-
}).start();
|
|
239
|
+
easing: Easing.out(Easing.cubic)
|
|
240
|
+
});
|
|
249
241
|
fireChange(next, next);
|
|
250
242
|
}
|
|
251
|
-
}, [disabled, isRange, trackWidth, pxToValue, valueToPx, fireChange, lowX, theme.motion.duration.fast]);
|
|
243
|
+
}, [disabled, isRange, trackWidth, pxToValue, valueToPx, fireChange, fireHaptic, lowX, lowVal, theme.motion.duration.fast]);
|
|
252
244
|
|
|
253
|
-
// Accessibility actions: increment / decrement
|
|
245
|
+
// Accessibility actions: increment / decrement.
|
|
254
246
|
const handleAccessibilityAction = useCallback(event => {
|
|
255
247
|
if (disabled) return;
|
|
256
248
|
const delta = event.nativeEvent.actionName === 'increment' ? step : -step;
|
|
257
249
|
if (isRange) {
|
|
258
|
-
const nextLow = clamp(
|
|
259
|
-
if (nextLow !==
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
fireChange(nextLow, highRef.current);
|
|
250
|
+
const nextLow = clamp(lowVal.value + delta, min, highVal.value);
|
|
251
|
+
if (nextLow !== lowVal.value) {
|
|
252
|
+
lowVal.value = nextLow;
|
|
253
|
+
lowX.value = valueToPx(nextLow, trackWidth);
|
|
254
|
+
fireHaptic();
|
|
255
|
+
fireChange(nextLow, highVal.value);
|
|
265
256
|
}
|
|
266
257
|
} else {
|
|
267
|
-
const next = clamp(
|
|
268
|
-
if (next !==
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
if (sliderTheme?.a11yHaptic ?? false) triggerHaptic('selection');
|
|
258
|
+
const next = clamp(lowVal.value + delta, min, max);
|
|
259
|
+
if (next !== lowVal.value) {
|
|
260
|
+
lowVal.value = next;
|
|
261
|
+
lowX.value = valueToPx(next, trackWidth);
|
|
262
|
+
fireHaptic();
|
|
273
263
|
fireChange(next, next);
|
|
274
264
|
}
|
|
275
265
|
}
|
|
276
|
-
}, [disabled, step, isRange, min, max, valueToPx, trackWidth, lowX, fireChange,
|
|
266
|
+
}, [disabled, step, isRange, min, max, valueToPx, trackWidth, lowX, lowVal, highVal, fireChange, fireHaptic]);
|
|
277
267
|
const thumbRadius = sizeCfg.thumbDiameter / 2;
|
|
278
268
|
|
|
279
|
-
//
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
269
|
+
// ───────── Animated styles ─────────
|
|
270
|
+
const fillAnimStyle = useAnimatedStyle(() => isRange ? {
|
|
271
|
+
left: lowX.value,
|
|
272
|
+
width: Math.max(0, highX.value - lowX.value)
|
|
273
|
+
} : {
|
|
274
|
+
left: 0,
|
|
275
|
+
width: lowX.value
|
|
276
|
+
});
|
|
277
|
+
const lowThumbStyle = useAnimatedStyle(() => ({
|
|
278
|
+
transform: [{
|
|
279
|
+
translateX: lowX.value - thumbRadius
|
|
280
|
+
}, {
|
|
281
|
+
scale: lowScale.value
|
|
282
|
+
}]
|
|
283
|
+
}));
|
|
284
|
+
const highThumbStyle = useAnimatedStyle(() => ({
|
|
285
|
+
transform: [{
|
|
286
|
+
translateX: highX.value - thumbRadius
|
|
287
|
+
}, {
|
|
288
|
+
scale: highScale.value
|
|
289
|
+
}]
|
|
290
|
+
}));
|
|
291
|
+
const lowLabelStyle = useAnimatedStyle(() => ({
|
|
292
|
+
opacity: lowLabelOpacity.value
|
|
293
|
+
}));
|
|
294
|
+
const highLabelStyle = useAnimatedStyle(() => ({
|
|
295
|
+
opacity: highLabelOpacity.value
|
|
296
|
+
}));
|
|
291
297
|
|
|
292
|
-
// Display values for labels.
|
|
293
|
-
const lowDisplayValue = isRange ?
|
|
294
|
-
const highDisplayValue =
|
|
298
|
+
// Display values (from committed state) for labels + a11y.
|
|
299
|
+
const lowDisplayValue = isRange ? Math.min(...currentValue) : currentValue;
|
|
300
|
+
const highDisplayValue = isRange ? Math.max(...currentValue) : currentValue;
|
|
295
301
|
return /*#__PURE__*/_jsx(View, {
|
|
296
302
|
ref: ref,
|
|
297
303
|
style: [styles.container, containerStyle, style],
|
|
@@ -301,10 +307,15 @@ const Slider = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
301
307
|
accessibilityState: {
|
|
302
308
|
disabled
|
|
303
309
|
},
|
|
304
|
-
accessibilityValue: {
|
|
310
|
+
accessibilityValue: isRange ? {
|
|
305
311
|
min,
|
|
306
312
|
max,
|
|
307
|
-
now:
|
|
313
|
+
now: lowDisplayValue,
|
|
314
|
+
text: `${lowDisplayValue} to ${highDisplayValue}`
|
|
315
|
+
} : {
|
|
316
|
+
min,
|
|
317
|
+
max,
|
|
318
|
+
now: lowDisplayValue
|
|
308
319
|
},
|
|
309
320
|
accessibilityActions: [{
|
|
310
321
|
name: 'increment'
|
|
@@ -327,67 +338,57 @@ const Slider = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
327
338
|
}, trackStyle]
|
|
328
339
|
}), /*#__PURE__*/_jsx(Animated.View, {
|
|
329
340
|
style: [styles.fill, {
|
|
330
|
-
left: fillLeft,
|
|
331
|
-
width: fillWidth,
|
|
332
341
|
backgroundColor: disabled ? theme.colors.surface.disabled : fillColor,
|
|
333
|
-
opacity: disabled ? 0.
|
|
334
|
-
}, fillStyle]
|
|
335
|
-
}), /*#__PURE__*/_jsx(
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
},
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
backgroundColor: fillColor
|
|
384
|
-
}],
|
|
385
|
-
pointerEvents: "none",
|
|
386
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
387
|
-
style: [styles.bubbleText, labelStyle],
|
|
388
|
-
children: formatLabel(highDisplayValue)
|
|
389
|
-
})
|
|
390
|
-
}) : null
|
|
342
|
+
opacity: disabled ? 0.55 : 1
|
|
343
|
+
}, fillStyle, fillAnimStyle]
|
|
344
|
+
}), /*#__PURE__*/_jsx(GestureDetector, {
|
|
345
|
+
gesture: lowPan,
|
|
346
|
+
children: /*#__PURE__*/_jsx(Animated.View, {
|
|
347
|
+
style: [styles.thumb, {
|
|
348
|
+
width: sizeCfg.thumbDiameter,
|
|
349
|
+
height: sizeCfg.thumbDiameter,
|
|
350
|
+
borderRadius: thumbRadius,
|
|
351
|
+
backgroundColor: theme.colors.background.elevated,
|
|
352
|
+
borderColor: disabled ? theme.colors.surface.disabled : fillColor,
|
|
353
|
+
opacity: disabled ? 0.55 : 1
|
|
354
|
+
}, thumbStyle, lowThumbStyle],
|
|
355
|
+
accessibilityElementsHidden: true,
|
|
356
|
+
importantForAccessibility: "no-hide-descendants",
|
|
357
|
+
children: showLabel ? /*#__PURE__*/_jsx(Animated.View, {
|
|
358
|
+
style: [styles.bubble, {
|
|
359
|
+
backgroundColor: fillColor
|
|
360
|
+
}, lowLabelStyle],
|
|
361
|
+
pointerEvents: "none",
|
|
362
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
363
|
+
style: [styles.bubbleText, labelStyle],
|
|
364
|
+
children: formatLabel(lowDisplayValue)
|
|
365
|
+
})
|
|
366
|
+
}) : null
|
|
367
|
+
})
|
|
368
|
+
}), isRange ? /*#__PURE__*/_jsx(GestureDetector, {
|
|
369
|
+
gesture: highPan,
|
|
370
|
+
children: /*#__PURE__*/_jsx(Animated.View, {
|
|
371
|
+
style: [styles.thumb, {
|
|
372
|
+
width: sizeCfg.thumbDiameter,
|
|
373
|
+
height: sizeCfg.thumbDiameter,
|
|
374
|
+
borderRadius: thumbRadius,
|
|
375
|
+
backgroundColor: theme.colors.background.elevated,
|
|
376
|
+
borderColor: disabled ? theme.colors.surface.disabled : fillColor,
|
|
377
|
+
opacity: disabled ? 0.55 : 1
|
|
378
|
+
}, thumbStyle, highThumbStyle],
|
|
379
|
+
accessibilityElementsHidden: true,
|
|
380
|
+
importantForAccessibility: "no-hide-descendants",
|
|
381
|
+
children: showLabel ? /*#__PURE__*/_jsx(Animated.View, {
|
|
382
|
+
style: [styles.bubble, {
|
|
383
|
+
backgroundColor: fillColor
|
|
384
|
+
}, highLabelStyle],
|
|
385
|
+
pointerEvents: "none",
|
|
386
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
387
|
+
style: [styles.bubbleText, labelStyle],
|
|
388
|
+
children: formatLabel(highDisplayValue)
|
|
389
|
+
})
|
|
390
|
+
}) : null
|
|
391
|
+
})
|
|
391
392
|
}) : null]
|
|
392
393
|
})
|
|
393
394
|
})
|
|
@@ -448,7 +449,6 @@ const buildStyles = (theme, sizeCfg) => {
|
|
|
448
449
|
minWidth: 36,
|
|
449
450
|
alignItems: 'center',
|
|
450
451
|
justifyContent: 'center',
|
|
451
|
-
// A subtle shadow for elevation.
|
|
452
452
|
...Platform.select({
|
|
453
453
|
ios: {
|
|
454
454
|
shadowColor: theme.shadows.sm.shadowColor,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import React, { forwardRef, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { Animated, Easing, Pressable, StyleSheet, Text, View } from 'react-native';
|
|
5
5
|
import { useTheme, createAnimatedValue } from "../../theme/index.js";
|
|
6
|
-
import { triggerHaptic } from "../../utils/hapticUtils.js";
|
|
6
|
+
import { triggerHaptic, resolveHaptic } from "../../utils/hapticUtils.js";
|
|
7
7
|
import { Skeleton } from "../Skeleton/index.js";
|
|
8
8
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
9
|
const CIRCLE_SIZE = 24;
|
|
@@ -34,7 +34,7 @@ const StepCircle = ({
|
|
|
34
34
|
pulseSize,
|
|
35
35
|
pulseEnabled,
|
|
36
36
|
pulseDuration,
|
|
37
|
-
|
|
37
|
+
pressHaptic,
|
|
38
38
|
circleSlotStyle
|
|
39
39
|
}) => {
|
|
40
40
|
const isActive = status === 'active';
|
|
@@ -81,7 +81,7 @@ const StepCircle = ({
|
|
|
81
81
|
const canPress = interactive && status === 'complete';
|
|
82
82
|
const handlePress = () => {
|
|
83
83
|
if (!canPress) return;
|
|
84
|
-
if (
|
|
84
|
+
if (pressHaptic) triggerHaptic(pressHaptic);
|
|
85
85
|
onPress?.(index);
|
|
86
86
|
};
|
|
87
87
|
const inner = /*#__PURE__*/_jsxs(View, {
|
|
@@ -176,6 +176,7 @@ const Stepper = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
176
176
|
circleStyle,
|
|
177
177
|
labelStyle,
|
|
178
178
|
connectorStyle,
|
|
179
|
+
haptic,
|
|
179
180
|
testID
|
|
180
181
|
} = props;
|
|
181
182
|
const theme = useTheme();
|
|
@@ -184,7 +185,7 @@ const Stepper = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
184
185
|
const pulseSize = stepperTheme?.pulseSize ?? PULSE_SIZE;
|
|
185
186
|
const pulseEnabled = stepperTheme?.pulseAnimation ?? false;
|
|
186
187
|
const pulseDuration = stepperTheme?.pulseDuration ?? PULSE_DURATION;
|
|
187
|
-
const
|
|
188
|
+
const pressHaptic = resolveHaptic(haptic, 'selection');
|
|
188
189
|
const styles = useMemo(() => buildStyles(theme), [theme]);
|
|
189
190
|
const toneColor = toneColorFor(theme, tone);
|
|
190
191
|
const upcomingColor = theme.colors.border.primary;
|
|
@@ -265,7 +266,7 @@ const Stepper = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
265
266
|
pulseSize: pulseSize,
|
|
266
267
|
pulseEnabled: pulseEnabled,
|
|
267
268
|
pulseDuration: pulseDuration,
|
|
268
|
-
|
|
269
|
+
pressHaptic: pressHaptic,
|
|
269
270
|
circleSlotStyle: circleStyle
|
|
270
271
|
}), /*#__PURE__*/_jsx(Text, {
|
|
271
272
|
style: [styles.hLabel, {
|
|
@@ -304,7 +305,7 @@ const Stepper = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
304
305
|
pulseSize: pulseSize,
|
|
305
306
|
pulseEnabled: pulseEnabled,
|
|
306
307
|
pulseDuration: pulseDuration,
|
|
307
|
-
|
|
308
|
+
pressHaptic: pressHaptic,
|
|
308
309
|
circleSlotStyle: circleStyle
|
|
309
310
|
}), !isLast ? /*#__PURE__*/_jsxs(View, {
|
|
310
311
|
style: [styles.vConnectorContainer, connectorStyle],
|