@webority-technologies/mobile 0.0.15 → 0.0.21
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 +60 -19
- package/lib/commonjs/components/AppBar/AppBar.js +29 -20
- package/lib/commonjs/components/Avatar/Avatar.js +38 -8
- package/lib/commonjs/components/Badge/Badge.js +66 -4
- package/lib/commonjs/components/Banner/Banner.js +146 -66
- package/lib/commonjs/components/BottomNavigation/BottomNavigation.js +37 -15
- package/lib/commonjs/components/BottomSheet/BottomSheet.js +78 -53
- package/lib/commonjs/components/Button/Button.js +12 -5
- package/lib/commonjs/components/Card/Card.js +106 -16
- package/lib/commonjs/components/Carousel/Carousel.js +66 -12
- package/lib/commonjs/components/Checkbox/Checkbox.js +11 -7
- package/lib/commonjs/components/Chip/Chip.js +44 -12
- package/lib/commonjs/components/DatePicker/DatePicker.js +185 -76
- package/lib/commonjs/components/DateRangePicker/DateRangePicker.js +133 -59
- package/lib/commonjs/components/Dialog/Dialog.js +16 -10
- package/lib/commonjs/components/Drawer/Drawer.js +13 -10
- package/lib/commonjs/components/FieldBase/FieldBase.js +494 -0
- package/lib/commonjs/components/FieldBase/index.js +32 -0
- package/lib/commonjs/components/FloatingActionButton/FloatingActionButton.js +69 -44
- package/lib/commonjs/components/ForceUpdateDialog/ForceUpdateDialog.js +8 -2
- package/lib/commonjs/components/FormField/FormField.js +3 -2
- package/lib/commonjs/components/ImageGallery/ImageGallery.js +132 -44
- package/lib/commonjs/components/Input/Input.js +144 -181
- package/lib/commonjs/components/ListItem/ListItem.js +90 -11
- package/lib/commonjs/components/Modal/Modal.js +55 -27
- package/lib/commonjs/components/NumberInput/NumberInput.js +60 -106
- package/lib/commonjs/components/OTPInput/OTPInput.js +65 -58
- package/lib/commonjs/components/PickerTrigger/PickerTrigger.js +185 -0
- package/lib/commonjs/components/{AppIcon → PickerTrigger}/index.js +4 -4
- package/lib/commonjs/components/ProgressBar/ProgressBar.js +19 -11
- package/lib/commonjs/components/Radio/Radio.js +11 -6
- package/lib/commonjs/components/Rating/Rating.js +85 -19
- package/lib/commonjs/components/SearchBar/SearchBar.js +90 -107
- package/lib/commonjs/components/SegmentedControl/SegmentedControl.js +22 -11
- package/lib/commonjs/components/Select/Select.js +62 -91
- package/lib/commonjs/components/Skeleton/Skeleton.js +131 -174
- package/lib/commonjs/components/Skeleton/SkeletonClock.js +117 -0
- package/lib/commonjs/components/Skeleton/SkeletonContent.js +164 -81
- package/lib/commonjs/components/Skeleton/SkeletonProvider.js +72 -10
- package/lib/commonjs/components/Skeleton/index.js +17 -16
- package/lib/commonjs/components/Slider/Slider.js +44 -25
- package/lib/commonjs/components/Stepper/Stepper.js +199 -29
- package/lib/commonjs/components/Swipeable/Swipeable.js +36 -19
- package/lib/commonjs/components/Switch/Switch.js +9 -2
- package/lib/commonjs/components/Tabs/Tabs.js +84 -21
- package/lib/commonjs/components/TimePicker/TimePicker.js +123 -45
- package/lib/commonjs/components/Toast/Toast.js +27 -16
- package/lib/commonjs/components/Tooltip/Tooltip.js +56 -32
- package/lib/commonjs/components/index.js +37 -37
- package/lib/commonjs/theme/tokens.js +55 -7
- package/lib/module/components/Accordion/Accordion.js +61 -20
- package/lib/module/components/AppBar/AppBar.js +29 -20
- package/lib/module/components/Avatar/Avatar.js +39 -9
- package/lib/module/components/Badge/Badge.js +67 -5
- package/lib/module/components/Banner/Banner.js +147 -67
- package/lib/module/components/BottomNavigation/BottomNavigation.js +37 -15
- package/lib/module/components/BottomSheet/BottomSheet.js +80 -55
- package/lib/module/components/Button/Button.js +12 -5
- package/lib/module/components/Card/Card.js +107 -17
- package/lib/module/components/Carousel/Carousel.js +67 -13
- package/lib/module/components/Checkbox/Checkbox.js +11 -7
- package/lib/module/components/Chip/Chip.js +45 -13
- package/lib/module/components/DatePicker/DatePicker.js +185 -76
- package/lib/module/components/DateRangePicker/DateRangePicker.js +134 -60
- package/lib/module/components/Dialog/Dialog.js +16 -10
- package/lib/module/components/Drawer/Drawer.js +13 -10
- package/lib/module/components/FieldBase/FieldBase.js +485 -0
- package/lib/module/components/FieldBase/index.js +4 -0
- package/lib/module/components/FloatingActionButton/FloatingActionButton.js +69 -44
- package/lib/module/components/ForceUpdateDialog/ForceUpdateDialog.js +8 -2
- package/lib/module/components/FormField/FormField.js +3 -2
- package/lib/module/components/ImageGallery/ImageGallery.js +128 -40
- package/lib/module/components/Input/Input.js +144 -179
- package/lib/module/components/ListItem/ListItem.js +91 -12
- package/lib/module/components/Modal/Modal.js +55 -27
- package/lib/module/components/NumberInput/NumberInput.js +60 -106
- package/lib/module/components/OTPInput/OTPInput.js +65 -58
- package/lib/module/components/PickerTrigger/PickerTrigger.js +181 -0
- package/lib/module/components/PickerTrigger/index.js +4 -0
- package/lib/module/components/ProgressBar/ProgressBar.js +19 -11
- package/lib/module/components/Radio/Radio.js +11 -6
- package/lib/module/components/Rating/Rating.js +86 -20
- package/lib/module/components/SearchBar/SearchBar.js +90 -107
- package/lib/module/components/SegmentedControl/SegmentedControl.js +22 -11
- package/lib/module/components/Select/Select.js +62 -91
- package/lib/module/components/Skeleton/Skeleton.js +135 -175
- package/lib/module/components/Skeleton/SkeletonClock.js +110 -0
- package/lib/module/components/Skeleton/SkeletonContent.js +167 -84
- package/lib/module/components/Skeleton/SkeletonProvider.js +71 -10
- package/lib/module/components/Skeleton/index.js +3 -2
- package/lib/module/components/Slider/Slider.js +44 -25
- package/lib/module/components/Stepper/Stepper.js +201 -31
- package/lib/module/components/Swipeable/Swipeable.js +36 -19
- package/lib/module/components/Switch/Switch.js +9 -2
- package/lib/module/components/Tabs/Tabs.js +84 -21
- package/lib/module/components/TimePicker/TimePicker.js +123 -45
- package/lib/module/components/Toast/Toast.js +27 -16
- package/lib/module/components/Tooltip/Tooltip.js +56 -32
- package/lib/module/components/index.js +2 -2
- package/lib/module/theme/tokens.js +55 -7
- package/lib/typescript/commonjs/components/Accordion/Accordion.d.ts +10 -5
- package/lib/typescript/commonjs/components/AppBar/AppBar.d.ts +8 -0
- package/lib/typescript/commonjs/components/Avatar/Avatar.d.ts +12 -6
- package/lib/typescript/commonjs/components/Badge/Badge.d.ts +7 -6
- package/lib/typescript/commonjs/components/Banner/Banner.d.ts +17 -6
- package/lib/typescript/commonjs/components/BottomSheet/BottomSheet.d.ts +7 -0
- package/lib/typescript/commonjs/components/Card/Card.d.ts +17 -6
- package/lib/typescript/commonjs/components/Carousel/Carousel.d.ts +7 -6
- package/lib/typescript/commonjs/components/Checkbox/Checkbox.d.ts +9 -1
- package/lib/typescript/commonjs/components/Chip/Chip.d.ts +13 -6
- package/lib/typescript/commonjs/components/DatePicker/DatePicker.d.ts +38 -3
- package/lib/typescript/commonjs/components/DateRangePicker/DateRangePicker.d.ts +36 -3
- package/lib/typescript/commonjs/components/Dialog/Dialog.d.ts +13 -1
- package/lib/typescript/commonjs/components/FieldBase/FieldBase.d.ts +172 -0
- package/lib/typescript/commonjs/components/FieldBase/index.d.ts +3 -0
- package/lib/typescript/commonjs/components/FloatingActionButton/FloatingActionButton.d.ts +8 -6
- package/lib/typescript/commonjs/components/FloatingActionButton/index.d.ts +1 -1
- package/lib/typescript/commonjs/components/ForceUpdateDialog/ForceUpdateDialog.d.ts +7 -0
- package/lib/typescript/commonjs/components/FormField/FormField.d.ts +7 -0
- package/lib/typescript/commonjs/components/ImageGallery/ImageGallery.d.ts +6 -4
- package/lib/typescript/commonjs/components/Input/Input.d.ts +7 -1
- package/lib/typescript/commonjs/components/ListItem/ListItem.d.ts +13 -6
- package/lib/typescript/commonjs/components/NumberInput/NumberInput.d.ts +3 -0
- package/lib/typescript/commonjs/components/PickerTrigger/PickerTrigger.d.ts +57 -0
- package/lib/typescript/commonjs/components/PickerTrigger/index.d.ts +3 -0
- package/lib/typescript/commonjs/components/ProgressBar/ProgressBar.d.ts +2 -0
- package/lib/typescript/commonjs/components/Radio/Radio.d.ts +3 -0
- package/lib/typescript/commonjs/components/Rating/Rating.d.ts +9 -6
- package/lib/typescript/commonjs/components/SegmentedControl/SegmentedControl.d.ts +3 -0
- package/lib/typescript/commonjs/components/Skeleton/Skeleton.d.ts +49 -20
- package/lib/typescript/commonjs/components/Skeleton/SkeletonClock.d.ts +60 -0
- package/lib/typescript/commonjs/components/Skeleton/SkeletonContent.d.ts +80 -19
- package/lib/typescript/commonjs/components/Skeleton/SkeletonProvider.d.ts +39 -5
- package/lib/typescript/commonjs/components/Skeleton/index.d.ts +6 -4
- package/lib/typescript/commonjs/components/Slider/Slider.d.ts +12 -1
- package/lib/typescript/commonjs/components/Stepper/Stepper.d.ts +18 -6
- package/lib/typescript/commonjs/components/Swipeable/Swipeable.d.ts +2 -0
- package/lib/typescript/commonjs/components/Switch/Switch.d.ts +1 -0
- package/lib/typescript/commonjs/components/Tabs/Tabs.d.ts +26 -2
- package/lib/typescript/commonjs/components/TimePicker/TimePicker.d.ts +36 -3
- package/lib/typescript/commonjs/components/Toast/Toast.d.ts +8 -0
- package/lib/typescript/commonjs/components/Tooltip/Tooltip.d.ts +7 -1
- package/lib/typescript/commonjs/components/index.d.ts +5 -5
- package/lib/typescript/commonjs/index.d.ts +1 -1
- package/lib/typescript/commonjs/theme/index.d.ts +1 -1
- package/lib/typescript/commonjs/theme/types.d.ts +578 -12
- package/lib/typescript/module/components/Accordion/Accordion.d.ts +10 -5
- package/lib/typescript/module/components/AppBar/AppBar.d.ts +8 -0
- package/lib/typescript/module/components/Avatar/Avatar.d.ts +12 -6
- package/lib/typescript/module/components/Badge/Badge.d.ts +7 -6
- package/lib/typescript/module/components/Banner/Banner.d.ts +17 -6
- package/lib/typescript/module/components/BottomSheet/BottomSheet.d.ts +7 -0
- package/lib/typescript/module/components/Card/Card.d.ts +17 -6
- package/lib/typescript/module/components/Carousel/Carousel.d.ts +7 -6
- package/lib/typescript/module/components/Checkbox/Checkbox.d.ts +9 -1
- package/lib/typescript/module/components/Chip/Chip.d.ts +13 -6
- package/lib/typescript/module/components/DatePicker/DatePicker.d.ts +38 -3
- package/lib/typescript/module/components/DateRangePicker/DateRangePicker.d.ts +36 -3
- package/lib/typescript/module/components/Dialog/Dialog.d.ts +13 -1
- package/lib/typescript/module/components/FieldBase/FieldBase.d.ts +172 -0
- package/lib/typescript/module/components/FieldBase/index.d.ts +3 -0
- package/lib/typescript/module/components/FloatingActionButton/FloatingActionButton.d.ts +8 -6
- package/lib/typescript/module/components/FloatingActionButton/index.d.ts +1 -1
- package/lib/typescript/module/components/ForceUpdateDialog/ForceUpdateDialog.d.ts +7 -0
- package/lib/typescript/module/components/FormField/FormField.d.ts +7 -0
- package/lib/typescript/module/components/ImageGallery/ImageGallery.d.ts +6 -4
- package/lib/typescript/module/components/Input/Input.d.ts +7 -1
- package/lib/typescript/module/components/ListItem/ListItem.d.ts +13 -6
- package/lib/typescript/module/components/NumberInput/NumberInput.d.ts +3 -0
- package/lib/typescript/module/components/PickerTrigger/PickerTrigger.d.ts +57 -0
- package/lib/typescript/module/components/PickerTrigger/index.d.ts +3 -0
- package/lib/typescript/module/components/ProgressBar/ProgressBar.d.ts +2 -0
- package/lib/typescript/module/components/Radio/Radio.d.ts +3 -0
- package/lib/typescript/module/components/Rating/Rating.d.ts +9 -6
- package/lib/typescript/module/components/SegmentedControl/SegmentedControl.d.ts +3 -0
- package/lib/typescript/module/components/Skeleton/Skeleton.d.ts +49 -20
- package/lib/typescript/module/components/Skeleton/SkeletonClock.d.ts +60 -0
- package/lib/typescript/module/components/Skeleton/SkeletonContent.d.ts +80 -19
- package/lib/typescript/module/components/Skeleton/SkeletonProvider.d.ts +39 -5
- package/lib/typescript/module/components/Skeleton/index.d.ts +6 -4
- package/lib/typescript/module/components/Slider/Slider.d.ts +12 -1
- package/lib/typescript/module/components/Stepper/Stepper.d.ts +18 -6
- package/lib/typescript/module/components/Swipeable/Swipeable.d.ts +2 -0
- package/lib/typescript/module/components/Switch/Switch.d.ts +1 -0
- package/lib/typescript/module/components/Tabs/Tabs.d.ts +26 -2
- package/lib/typescript/module/components/TimePicker/TimePicker.d.ts +36 -3
- package/lib/typescript/module/components/Toast/Toast.d.ts +8 -0
- package/lib/typescript/module/components/Tooltip/Tooltip.d.ts +7 -1
- package/lib/typescript/module/components/index.d.ts +5 -5
- package/lib/typescript/module/index.d.ts +1 -1
- package/lib/typescript/module/theme/index.d.ts +1 -1
- package/lib/typescript/module/theme/types.d.ts +578 -12
- package/package.json +2 -6
- package/lib/commonjs/components/AppIcon/AppIcon.js +0 -120
- package/lib/commonjs/types/vector-icons.d.js +0 -2
- package/lib/module/components/AppIcon/AppIcon.js +0 -111
- package/lib/module/components/AppIcon/index.js +0 -4
- package/lib/module/types/vector-icons.d.js +0 -2
- package/lib/typescript/commonjs/components/AppIcon/AppIcon.d.ts +0 -20
- package/lib/typescript/commonjs/components/AppIcon/index.d.ts +0 -3
- package/lib/typescript/module/components/AppIcon/AppIcon.d.ts +0 -20
- package/lib/typescript/module/components/AppIcon/index.d.ts +0 -3
|
@@ -36,6 +36,7 @@ const SPRING_CONFIG = {
|
|
|
36
36
|
};
|
|
37
37
|
const DRAG_ACTIVATION_OFFSET = 10;
|
|
38
38
|
const RUBBER_BAND_FACTOR = 0.35;
|
|
39
|
+
const FAIL_OFFSET_Y = 15;
|
|
39
40
|
const toneToBg = (theme, tone) => {
|
|
40
41
|
switch (tone) {
|
|
41
42
|
case 'primary':
|
|
@@ -56,16 +57,32 @@ const Swipeable = props => {
|
|
|
56
57
|
children,
|
|
57
58
|
leftActions,
|
|
58
59
|
rightActions,
|
|
59
|
-
threshold
|
|
60
|
-
fullSwipeThreshold
|
|
60
|
+
threshold: thresholdProp,
|
|
61
|
+
fullSwipeThreshold: fullSwipeThresholdProp,
|
|
61
62
|
onSwipeOpen,
|
|
62
63
|
onSwipeClose,
|
|
63
64
|
disabled = false,
|
|
64
65
|
containerStyle,
|
|
66
|
+
contentStyle: contentSlotStyle,
|
|
67
|
+
actionStyle,
|
|
65
68
|
accessibilityLabel,
|
|
66
69
|
testID
|
|
67
70
|
} = props;
|
|
68
71
|
const theme = (0, _index.useTheme)();
|
|
72
|
+
const swipeTheme = theme.components.swipeable;
|
|
73
|
+
const threshold = thresholdProp ?? swipeTheme?.threshold ?? 80;
|
|
74
|
+
const fullSwipeThreshold = fullSwipeThresholdProp ?? swipeTheme?.fullSwipeThreshold ?? 200;
|
|
75
|
+
const dragActivationOffset = swipeTheme?.dragActivationOffset ?? DRAG_ACTIVATION_OFFSET;
|
|
76
|
+
const rubberBandFactor = swipeTheme?.rubberBandFactor ?? RUBBER_BAND_FACTOR;
|
|
77
|
+
const failOffsetY = swipeTheme?.failOffsetY ?? FAIL_OFFSET_Y;
|
|
78
|
+
const springConfig = {
|
|
79
|
+
damping: swipeTheme?.springDamping ?? SPRING_CONFIG.damping,
|
|
80
|
+
stiffness: swipeTheme?.springStiffness ?? SPRING_CONFIG.stiffness,
|
|
81
|
+
mass: swipeTheme?.springMass ?? SPRING_CONFIG.mass
|
|
82
|
+
};
|
|
83
|
+
const fullSwipeHapticEnabled = swipeTheme?.fullSwipeHaptic ?? true;
|
|
84
|
+
const actionPressHapticEnabled = swipeTheme?.actionPressHaptic ?? true;
|
|
85
|
+
const a11yActionHapticEnabled = swipeTheme?.a11yActionHaptic ?? true;
|
|
69
86
|
const styles = (0, _react.useMemo)(() => buildStyles(theme), [theme]);
|
|
70
87
|
const hasLeft = !!leftActions && leftActions.length > 0;
|
|
71
88
|
const hasRight = !!rightActions && rightActions.length > 0;
|
|
@@ -105,22 +122,22 @@ const Swipeable = props => {
|
|
|
105
122
|
const list = side === 'left' ? leftActions : rightActions;
|
|
106
123
|
const first = list?.[0];
|
|
107
124
|
if (!first) return;
|
|
108
|
-
(0, _index2.triggerHaptic)('notificationSuccess');
|
|
125
|
+
if (fullSwipeHapticEnabled) (0, _index2.triggerHaptic)('notificationSuccess');
|
|
109
126
|
first.onPress();
|
|
110
127
|
// After the off-screen slide, snap back to 0 silently.
|
|
111
|
-
translateX.value = (0, _reactNativeReanimated.withSpring)(0,
|
|
128
|
+
translateX.value = (0, _reactNativeReanimated.withSpring)(0, springConfig);
|
|
112
129
|
openSideRef.current = null;
|
|
113
130
|
onSwipeClose?.();
|
|
114
|
-
}, [leftActions, rightActions, translateX, onSwipeClose]);
|
|
131
|
+
}, [leftActions, rightActions, translateX, onSwipeClose, springConfig, fullSwipeHapticEnabled]);
|
|
115
132
|
const close = (0, _react.useCallback)(() => {
|
|
116
|
-
translateX.value = (0, _reactNativeReanimated.withSpring)(0,
|
|
133
|
+
translateX.value = (0, _reactNativeReanimated.withSpring)(0, springConfig);
|
|
117
134
|
notifyClose();
|
|
118
|
-
}, [translateX, notifyClose]);
|
|
135
|
+
}, [translateX, notifyClose, springConfig]);
|
|
119
136
|
const handleActionPress = (0, _react.useCallback)(action => {
|
|
120
|
-
(0, _index2.triggerHaptic)('selection');
|
|
137
|
+
if (actionPressHapticEnabled) (0, _index2.triggerHaptic)('selection');
|
|
121
138
|
action.onPress();
|
|
122
139
|
close();
|
|
123
|
-
}, [close]);
|
|
140
|
+
}, [close, actionPressHapticEnabled]);
|
|
124
141
|
|
|
125
142
|
// ───────── Pan gesture (UI thread) ─────────
|
|
126
143
|
const panGesture = (0, _react.useMemo)(() => {
|
|
@@ -133,7 +150,7 @@ const Swipeable = props => {
|
|
|
133
150
|
const openThreshold = threshold;
|
|
134
151
|
const allowLeft = hasLeft;
|
|
135
152
|
const allowRight = hasRight;
|
|
136
|
-
return _reactNativeGestureHandler.Gesture.Pan().enabled(!disabled).activeOffsetX([-
|
|
153
|
+
return _reactNativeGestureHandler.Gesture.Pan().enabled(!disabled).activeOffsetX([-dragActivationOffset, dragActivationOffset]).failOffsetY([-failOffsetY, failOffsetY]).onStart(() => {
|
|
137
154
|
'worklet';
|
|
138
155
|
|
|
139
156
|
dragStartX.value = translateX.value;
|
|
@@ -152,9 +169,9 @@ const Swipeable = props => {
|
|
|
152
169
|
}
|
|
153
170
|
// Rubber-band past the open position so it still feels physical past the cap.
|
|
154
171
|
if (next > leftCap && allowLeft) {
|
|
155
|
-
translateX.value = leftCap + (next - leftCap) *
|
|
172
|
+
translateX.value = leftCap + (next - leftCap) * rubberBandFactor;
|
|
156
173
|
} else if (next < -rightCap && allowRight) {
|
|
157
|
-
translateX.value = -rightCap + (next + rightCap) *
|
|
174
|
+
translateX.value = -rightCap + (next + rightCap) * rubberBandFactor;
|
|
158
175
|
} else {
|
|
159
176
|
translateX.value = next;
|
|
160
177
|
}
|
|
@@ -188,10 +205,10 @@ const Swipeable = props => {
|
|
|
188
205
|
}
|
|
189
206
|
|
|
190
207
|
// Otherwise → spring back closed.
|
|
191
|
-
translateX.value = (0, _reactNativeReanimated.withSpring)(0,
|
|
208
|
+
translateX.value = (0, _reactNativeReanimated.withSpring)(0, springConfig);
|
|
192
209
|
(0, _reactNativeReanimated.runOnJS)(notifyClose)();
|
|
193
210
|
});
|
|
194
|
-
}, [disabled, dragStartX, fireFirstAction, fullSwipeThreshold, hasLeft, hasRight, leftRevealWidth, notifyClose, notifyOpen, rightRevealWidth, screenWidth, threshold, translateX]);
|
|
211
|
+
}, [disabled, dragStartX, fireFirstAction, fullSwipeThreshold, hasLeft, hasRight, leftRevealWidth, notifyClose, notifyOpen, rightRevealWidth, screenWidth, threshold, translateX, dragActivationOffset, failOffsetY, rubberBandFactor, springConfig]);
|
|
195
212
|
|
|
196
213
|
// ───────── Animated styles ─────────
|
|
197
214
|
const contentStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => ({
|
|
@@ -226,10 +243,10 @@ const Swipeable = props => {
|
|
|
226
243
|
const all = [...(leftActions ?? []), ...(rightActions ?? [])];
|
|
227
244
|
const match = all.find(a => a.key === name);
|
|
228
245
|
if (match) {
|
|
229
|
-
(0, _index2.triggerHaptic)('selection');
|
|
246
|
+
if (a11yActionHapticEnabled) (0, _index2.triggerHaptic)('selection');
|
|
230
247
|
match.onPress();
|
|
231
248
|
}
|
|
232
|
-
}, [leftActions, rightActions]);
|
|
249
|
+
}, [leftActions, rightActions, a11yActionHapticEnabled]);
|
|
233
250
|
|
|
234
251
|
// ───────── Render ─────────
|
|
235
252
|
// No actions configured → render children directly; zero gesture overhead.
|
|
@@ -248,7 +265,7 @@ const Swipeable = props => {
|
|
|
248
265
|
children: [hasLeft ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
|
|
249
266
|
style: [styles.actionsRow, styles.actionsLeft, {
|
|
250
267
|
width: leftRevealWidth
|
|
251
|
-
}, leftBgStyle],
|
|
268
|
+
}, actionStyle, leftBgStyle],
|
|
252
269
|
pointerEvents: "box-none",
|
|
253
270
|
children: leftActions?.map(action => /*#__PURE__*/(0, _jsxRuntime.jsx)(ActionButton, {
|
|
254
271
|
action: action,
|
|
@@ -259,7 +276,7 @@ const Swipeable = props => {
|
|
|
259
276
|
}) : null, hasRight ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
|
|
260
277
|
style: [styles.actionsRow, styles.actionsRight, {
|
|
261
278
|
width: rightRevealWidth
|
|
262
|
-
}, rightBgStyle],
|
|
279
|
+
}, actionStyle, rightBgStyle],
|
|
263
280
|
pointerEvents: "box-none",
|
|
264
281
|
children: rightActions?.map(action => /*#__PURE__*/(0, _jsxRuntime.jsx)(ActionButton, {
|
|
265
282
|
action: action,
|
|
@@ -270,7 +287,7 @@ const Swipeable = props => {
|
|
|
270
287
|
}) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeGestureHandler.GestureDetector, {
|
|
271
288
|
gesture: panGesture,
|
|
272
289
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
|
|
273
|
-
style: [styles.content, contentStyle],
|
|
290
|
+
style: [styles.content, contentSlotStyle, contentStyle],
|
|
274
291
|
children: children
|
|
275
292
|
})
|
|
276
293
|
})]
|
|
@@ -55,6 +55,7 @@ const Switch = exports.Switch = /*#__PURE__*/(0, _react.forwardRef)((props, ref)
|
|
|
55
55
|
haptic = 'selection',
|
|
56
56
|
bounce = false,
|
|
57
57
|
style,
|
|
58
|
+
containerStyle,
|
|
58
59
|
trackStyle,
|
|
59
60
|
thumbStyle,
|
|
60
61
|
testID,
|
|
@@ -167,9 +168,15 @@ const Switch = exports.Switch = /*#__PURE__*/(0, _react.forwardRef)((props, ref)
|
|
|
167
168
|
})
|
|
168
169
|
})
|
|
169
170
|
});
|
|
170
|
-
if (!label)
|
|
171
|
+
if (!label) {
|
|
172
|
+
if (!containerStyle) return switchEl;
|
|
173
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
174
|
+
style: containerStyle,
|
|
175
|
+
children: switchEl
|
|
176
|
+
});
|
|
177
|
+
}
|
|
171
178
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
172
|
-
style: [styles.row, style],
|
|
179
|
+
style: [styles.row, containerStyle, style],
|
|
173
180
|
children: [switchEl, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
174
181
|
style: [styles.label, {
|
|
175
182
|
color: disabled ? theme.colors.text.disabled : theme.colors.text.primary,
|
|
@@ -30,12 +30,28 @@ const Tabs = exports.Tabs = /*#__PURE__*/(0, _react.forwardRef)((props, ref) =>
|
|
|
30
30
|
scrollable = false,
|
|
31
31
|
align = 'left',
|
|
32
32
|
style,
|
|
33
|
+
containerStyle,
|
|
33
34
|
tabStyle,
|
|
35
|
+
tabLabelStyle,
|
|
36
|
+
tabIconStyle,
|
|
37
|
+
tabBadgeStyle,
|
|
38
|
+
dividerStyle,
|
|
39
|
+
indicatorStyle,
|
|
40
|
+
progress,
|
|
34
41
|
accessibilityLabel,
|
|
35
42
|
testID
|
|
36
43
|
} = props;
|
|
37
44
|
const theme = (0, _index.useTheme)();
|
|
38
45
|
const styles = (0, _react.useMemo)(() => buildStyles(theme), [theme]);
|
|
46
|
+
const tabsTokens = theme.components.tabs;
|
|
47
|
+
const tabPaddingHorizontal = tabsTokens?.tabPaddingHorizontal ?? theme.spacing.lg;
|
|
48
|
+
const tabPaddingVertical = tabsTokens?.tabPaddingVertical ?? theme.spacing.md;
|
|
49
|
+
const tabIconSpacing = tabsTokens?.tabIconSpacing ?? 6;
|
|
50
|
+
const underlineHeight = tabsTokens?.underlineHeight ?? 2;
|
|
51
|
+
const pillInset = tabsTokens?.pillInset ?? 4;
|
|
52
|
+
const disabledOpacity = tabsTokens?.disabledOpacity ?? 0.45;
|
|
53
|
+
const badgeGap = tabsTokens?.badgeGap ?? 6;
|
|
54
|
+
const hapticOnPress = tabsTokens?.hapticOnPress ?? false;
|
|
39
55
|
|
|
40
56
|
// Per-tab measured layouts (key → {x, width}).
|
|
41
57
|
const [layouts, setLayouts] = (0, _react.useState)({});
|
|
@@ -43,9 +59,33 @@ const Tabs = exports.Tabs = /*#__PURE__*/(0, _react.forwardRef)((props, ref) =>
|
|
|
43
59
|
const indicatorWidth = (0, _react.useRef)((0, _index.createAnimatedValue)(0)).current;
|
|
44
60
|
const activeLayout = layouts[activeKey];
|
|
45
61
|
|
|
62
|
+
// Progress-driven mode: when the caller supplies a fractional-index signal
|
|
63
|
+
// and every tab has reported its layout, the indicator's translateX/width
|
|
64
|
+
// are interpolated from that signal — the press-spring is suppressed so the
|
|
65
|
+
// two drivers don't fight for the same view.
|
|
66
|
+
const allMeasured = tabs.every(t => layouts[t.key] !== undefined);
|
|
67
|
+
const useProgress = progress !== undefined && allMeasured && tabs.length > 1;
|
|
68
|
+
const progressTranslateX = (0, _react.useMemo)(() => {
|
|
69
|
+
if (!useProgress || !progress) return null;
|
|
70
|
+
return progress.interpolate({
|
|
71
|
+
inputRange: tabs.map((_, i) => i),
|
|
72
|
+
outputRange: tabs.map(t => layouts[t.key].x),
|
|
73
|
+
extrapolate: 'clamp'
|
|
74
|
+
});
|
|
75
|
+
}, [useProgress, progress, tabs, layouts]);
|
|
76
|
+
const progressWidth = (0, _react.useMemo)(() => {
|
|
77
|
+
if (!useProgress || !progress) return null;
|
|
78
|
+
return progress.interpolate({
|
|
79
|
+
inputRange: tabs.map((_, i) => i),
|
|
80
|
+
outputRange: tabs.map(t => layouts[t.key].width),
|
|
81
|
+
extrapolate: 'clamp'
|
|
82
|
+
});
|
|
83
|
+
}, [useProgress, progress, tabs, layouts]);
|
|
84
|
+
|
|
46
85
|
// Animate indicator whenever activeKey or its layout changes.
|
|
47
86
|
(0, _react.useEffect)(() => {
|
|
48
87
|
if (!activeLayout) return;
|
|
88
|
+
if (useProgress) return;
|
|
49
89
|
const spring = theme.motion.spring.snappy;
|
|
50
90
|
_reactNative.Animated.parallel([
|
|
51
91
|
// Both must use the JS driver: width can't run on native, and mixing
|
|
@@ -64,7 +104,7 @@ const Tabs = exports.Tabs = /*#__PURE__*/(0, _react.forwardRef)((props, ref) =>
|
|
|
64
104
|
mass: spring.mass,
|
|
65
105
|
useNativeDriver: false
|
|
66
106
|
})]).start();
|
|
67
|
-
}, [activeLayout, activeKey, indicatorTranslateX, indicatorWidth, theme.motion.spring.snappy]);
|
|
107
|
+
}, [activeLayout, activeKey, indicatorTranslateX, indicatorWidth, theme.motion.spring.snappy, useProgress]);
|
|
68
108
|
const handleLayout = (0, _react.useCallback)(key => e => {
|
|
69
109
|
const {
|
|
70
110
|
x,
|
|
@@ -85,22 +125,27 @@ const Tabs = exports.Tabs = /*#__PURE__*/(0, _react.forwardRef)((props, ref) =>
|
|
|
85
125
|
const handlePress = (0, _react.useCallback)(tab => {
|
|
86
126
|
if (tab.disabled) return;
|
|
87
127
|
if (tab.key === activeKey) return;
|
|
88
|
-
(0, _index2.triggerHaptic)('selection');
|
|
128
|
+
if (hapticOnPress) (0, _index2.triggerHaptic)('selection');
|
|
89
129
|
onChange(tab.key);
|
|
90
|
-
}, [activeKey, onChange]);
|
|
130
|
+
}, [activeKey, onChange, hapticOnPress]);
|
|
91
131
|
const indicatorIsPill = variant === 'pills';
|
|
92
132
|
|
|
93
133
|
// Indicator visual.
|
|
94
134
|
const indicator = /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
|
|
95
135
|
pointerEvents: "none",
|
|
96
|
-
style: [indicatorIsPill ? styles.indicatorPill : styles.indicatorUnderline, {
|
|
97
|
-
|
|
136
|
+
style: [indicatorIsPill ? styles.indicatorPill : styles.indicatorUnderline, indicatorIsPill ? {
|
|
137
|
+
top: pillInset,
|
|
138
|
+
bottom: pillInset
|
|
139
|
+
} : {
|
|
140
|
+
height: underlineHeight
|
|
141
|
+
}, {
|
|
142
|
+
width: progressWidth ?? indicatorWidth,
|
|
98
143
|
transform: [{
|
|
99
|
-
translateX: indicatorTranslateX
|
|
144
|
+
translateX: progressTranslateX ?? indicatorTranslateX
|
|
100
145
|
}],
|
|
101
146
|
backgroundColor: indicatorIsPill ? theme.colors.primaryMuted : theme.colors.primary,
|
|
102
147
|
borderRadius: indicatorIsPill ? theme.radius.full : 0
|
|
103
|
-
}]
|
|
148
|
+
}, indicatorStyle]
|
|
104
149
|
});
|
|
105
150
|
const renderTabs = () => tabs.map(tab => {
|
|
106
151
|
const isActive = tab.key === activeKey;
|
|
@@ -122,23 +167,41 @@ const Tabs = exports.Tabs = /*#__PURE__*/(0, _react.forwardRef)((props, ref) =>
|
|
|
122
167
|
style: ({
|
|
123
168
|
pressed
|
|
124
169
|
}) => [styles.tab, scrollable ? null : styles.tabFlex, {
|
|
125
|
-
paddingHorizontal:
|
|
126
|
-
paddingVertical:
|
|
127
|
-
opacity: isDisabled ?
|
|
170
|
+
paddingHorizontal: tabPaddingHorizontal,
|
|
171
|
+
paddingVertical: tabPaddingVertical,
|
|
172
|
+
opacity: isDisabled ? disabledOpacity : 1,
|
|
128
173
|
backgroundColor: pressed ? theme.colors.surface.pressed : 'transparent'
|
|
129
174
|
}, tabStyle],
|
|
130
175
|
children: [tab.icon ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
131
|
-
style: styles.tabIcon,
|
|
176
|
+
style: [styles.tabIcon, {
|
|
177
|
+
marginRight: tabIconSpacing
|
|
178
|
+
}, tabIconStyle],
|
|
132
179
|
children: tab.icon
|
|
133
180
|
}) : null, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
134
181
|
style: [styles.tabLabel, {
|
|
135
182
|
color: isActive ? theme.colors.primary : theme.colors.text.tertiary,
|
|
136
183
|
fontSize: theme.typography.fontSize.base,
|
|
137
184
|
fontWeight: isActive ? theme.typography.fontWeight.semibold : theme.typography.fontWeight.medium
|
|
138
|
-
}],
|
|
185
|
+
}, tabLabelStyle],
|
|
139
186
|
numberOfLines: 1,
|
|
140
187
|
children: tab.label
|
|
141
|
-
})
|
|
188
|
+
}), tab.badge !== undefined && tab.badge !== null ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
189
|
+
style: [styles.tabBadge, {
|
|
190
|
+
marginLeft: badgeGap,
|
|
191
|
+
backgroundColor: theme.colors.primaryMuted,
|
|
192
|
+
borderRadius: theme.radius.full,
|
|
193
|
+
paddingHorizontal: theme.spacing.xs
|
|
194
|
+
}, tabBadgeStyle],
|
|
195
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
196
|
+
style: {
|
|
197
|
+
color: theme.colors.primary,
|
|
198
|
+
fontSize: theme.typography.fontSize.xs,
|
|
199
|
+
fontWeight: theme.typography.fontWeight.semibold
|
|
200
|
+
},
|
|
201
|
+
numberOfLines: 1,
|
|
202
|
+
children: String(tab.badge)
|
|
203
|
+
})
|
|
204
|
+
}) : null]
|
|
142
205
|
}, tab.key);
|
|
143
206
|
});
|
|
144
207
|
const baseRowStyle = {
|
|
@@ -152,7 +215,7 @@ const Tabs = exports.Tabs = /*#__PURE__*/(0, _react.forwardRef)((props, ref) =>
|
|
|
152
215
|
style: [styles.container, {
|
|
153
216
|
borderBottomColor: theme.colors.border.secondary,
|
|
154
217
|
borderBottomWidth: variant === 'underline' ? _reactNative.StyleSheet.hairlineWidth : 0
|
|
155
|
-
}, style],
|
|
218
|
+
}, variant === 'underline' ? dividerStyle : null, style, containerStyle],
|
|
156
219
|
accessibilityRole: "tablist",
|
|
157
220
|
accessibilityLabel: accessibilityLabel,
|
|
158
221
|
testID: testID,
|
|
@@ -182,23 +245,23 @@ const buildStyles = _theme => _reactNative.StyleSheet.create({
|
|
|
182
245
|
tabFlex: {
|
|
183
246
|
flex: 1
|
|
184
247
|
},
|
|
185
|
-
tabIcon: {
|
|
186
|
-
marginRight: 6
|
|
187
|
-
},
|
|
248
|
+
tabIcon: {},
|
|
188
249
|
tabLabel: {
|
|
189
250
|
includeFontPadding: false
|
|
190
251
|
},
|
|
252
|
+
tabBadge: {
|
|
253
|
+
minWidth: 18,
|
|
254
|
+
alignItems: 'center',
|
|
255
|
+
justifyContent: 'center'
|
|
256
|
+
},
|
|
191
257
|
indicatorUnderline: {
|
|
192
258
|
position: 'absolute',
|
|
193
259
|
left: 0,
|
|
194
|
-
bottom: 0
|
|
195
|
-
height: 2
|
|
260
|
+
bottom: 0
|
|
196
261
|
},
|
|
197
262
|
indicatorPill: {
|
|
198
263
|
position: 'absolute',
|
|
199
264
|
left: 0,
|
|
200
|
-
top: 4,
|
|
201
|
-
bottom: 4,
|
|
202
265
|
zIndex: -1
|
|
203
266
|
}
|
|
204
267
|
});
|
|
@@ -6,12 +6,26 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = exports.TimePicker = void 0;
|
|
7
7
|
var _react = _interopRequireWildcard(require("react"));
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
|
+
var _reactNativeSafeAreaContext = require("react-native-safe-area-context");
|
|
9
10
|
var _index = require("../../theme/index.js");
|
|
10
11
|
var _hapticUtils = require("../../utils/hapticUtils.js");
|
|
11
12
|
var _Button = _interopRequireDefault(require("../Button/Button.js"));
|
|
13
|
+
var _PickerTrigger = require("../PickerTrigger/PickerTrigger.js");
|
|
12
14
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
13
15
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
14
16
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
17
|
+
/**
|
|
18
|
+
* TimePicker supports two modes:
|
|
19
|
+
*
|
|
20
|
+
* 1. Controlled-modal mode — pass `visible`, `onSelect`, `onClose`. The
|
|
21
|
+
* component renders only the modal sheet and the caller owns open/close
|
|
22
|
+
* state plus its own trigger UI.
|
|
23
|
+
* 2. Trigger mode — omit `visible`. The component renders a PickerTrigger
|
|
24
|
+
* field (label / value / placeholder / chevron / clear / helper / error)
|
|
25
|
+
* and manages its own modal open state. `onSelect` is still called on
|
|
26
|
+
* confirm.
|
|
27
|
+
*/
|
|
28
|
+
|
|
15
29
|
const ITEM_HEIGHT = 40;
|
|
16
30
|
const VISIBLE_ITEMS = 5;
|
|
17
31
|
const PICKER_HEIGHT = ITEM_HEIGHT * VISIBLE_ITEMS;
|
|
@@ -84,6 +98,8 @@ const Wheel = ({
|
|
|
84
98
|
});
|
|
85
99
|
}
|
|
86
100
|
}, [selectedIndex, scrollY]);
|
|
101
|
+
const hapticEnabled = theme.components.timePicker?.haptic ?? false;
|
|
102
|
+
const hapticDebounceMs = theme.components.timePicker?.hapticDebounceMs ?? HAPTIC_DEBOUNCE_MS;
|
|
87
103
|
const onScroll = (0, _react.useMemo)(() => _reactNative.Animated.event([{
|
|
88
104
|
nativeEvent: {
|
|
89
105
|
contentOffset: {
|
|
@@ -97,14 +113,14 @@ const Wheel = ({
|
|
|
97
113
|
const idx = Math.round(y / ITEM_HEIGHT);
|
|
98
114
|
if (idx !== lastIndexRef.current && idx >= 0 && idx < data.length) {
|
|
99
115
|
const now = Date.now();
|
|
100
|
-
if (now - lastHapticAtRef.current >=
|
|
116
|
+
if (hapticEnabled && now - lastHapticAtRef.current >= hapticDebounceMs) {
|
|
101
117
|
(0, _hapticUtils.triggerHaptic)('selection');
|
|
102
118
|
lastHapticAtRef.current = now;
|
|
103
119
|
}
|
|
104
120
|
lastIndexRef.current = idx;
|
|
105
121
|
}
|
|
106
122
|
}
|
|
107
|
-
}), [scrollY, data.length]);
|
|
123
|
+
}), [scrollY, data.length, hapticEnabled, hapticDebounceMs]);
|
|
108
124
|
const onMomentumScrollEnd = (0, _react.useCallback)(event => {
|
|
109
125
|
const y = event.nativeEvent.contentOffset.y;
|
|
110
126
|
const idx = Math.max(0, Math.min(data.length - 1, Math.round(y / ITEM_HEIGHT)));
|
|
@@ -203,21 +219,48 @@ const Wheel = ({
|
|
|
203
219
|
})]
|
|
204
220
|
});
|
|
205
221
|
};
|
|
206
|
-
const TimePicker =
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
222
|
+
const TimePicker = props => {
|
|
223
|
+
const {
|
|
224
|
+
visible,
|
|
225
|
+
value,
|
|
226
|
+
onSelect,
|
|
227
|
+
onClose,
|
|
228
|
+
format = '12h',
|
|
229
|
+
minuteStep = 1,
|
|
230
|
+
title = 'Select Time',
|
|
231
|
+
confirmLabel = 'Confirm',
|
|
232
|
+
cancelLabel = 'Cancel',
|
|
233
|
+
testID,
|
|
234
|
+
style,
|
|
235
|
+
containerStyle,
|
|
236
|
+
headerLabelStyle,
|
|
237
|
+
footerButtonStyle,
|
|
238
|
+
label,
|
|
239
|
+
placeholder,
|
|
240
|
+
helperText,
|
|
241
|
+
error,
|
|
242
|
+
required,
|
|
243
|
+
disabled,
|
|
244
|
+
size,
|
|
245
|
+
variant,
|
|
246
|
+
clearable,
|
|
247
|
+
onClear,
|
|
248
|
+
formatValue,
|
|
249
|
+
triggerStyle
|
|
250
|
+
} = props;
|
|
251
|
+
const isControlled = props.visible !== undefined;
|
|
252
|
+
const [internalOpen, setInternalOpen] = (0, _react.useState)(false);
|
|
253
|
+
const open = isControlled ? visible : internalOpen;
|
|
219
254
|
const theme = (0, _index.useTheme)();
|
|
255
|
+
const insets = (0, _reactNativeSafeAreaContext.useSafeAreaInsets)();
|
|
220
256
|
const styles = (0, _react.useMemo)(() => buildStyles(theme), [theme]);
|
|
257
|
+
const handleCloseModal = (0, _react.useCallback)(() => {
|
|
258
|
+
if (isControlled) {
|
|
259
|
+
onClose?.();
|
|
260
|
+
} else {
|
|
261
|
+
setInternalOpen(false);
|
|
262
|
+
}
|
|
263
|
+
}, [isControlled, onClose]);
|
|
221
264
|
const hours = (0, _react.useMemo)(() => format === '24h' ? range(0, 23) : range(1, 12), [format]);
|
|
222
265
|
const minutes = (0, _react.useMemo)(() => range(0, 59, minuteStep), [minuteStep]);
|
|
223
266
|
const periods = (0, _react.useMemo)(() => ['AM', 'PM'], []);
|
|
@@ -246,9 +289,9 @@ const TimePicker = ({
|
|
|
246
289
|
});
|
|
247
290
|
const [periodIndex, setPeriodIndex] = (0, _react.useState)(() => initial.period === 'PM' ? 1 : 0);
|
|
248
291
|
|
|
249
|
-
// Re-sync on
|
|
292
|
+
// Re-sync on open toggle / value change / format / step.
|
|
250
293
|
(0, _react.useEffect)(() => {
|
|
251
|
-
if (!
|
|
294
|
+
if (!open) return;
|
|
252
295
|
const h24 = value && Number.isFinite(value.hour) ? value.hour : new Date().getHours();
|
|
253
296
|
const m = value && Number.isFinite(value.minute) ? value.minute : new Date().getMinutes();
|
|
254
297
|
const stepped = clampMinuteToStep(m, minuteStep);
|
|
@@ -261,18 +304,20 @@ const TimePicker = ({
|
|
|
261
304
|
const mIdx = minutes.indexOf(stepped);
|
|
262
305
|
setMinuteIndex(mIdx >= 0 ? mIdx : 0);
|
|
263
306
|
setPeriodIndex(period === 'PM' ? 1 : 0);
|
|
264
|
-
}, [
|
|
307
|
+
}, [open, value, format, minuteStep, hours, minutes]);
|
|
265
308
|
|
|
266
309
|
// Sheet animations.
|
|
267
310
|
const opacity = (0, _react.useRef)((0, _index.createAnimatedValue)(0)).current;
|
|
268
311
|
const translateY = (0, _react.useRef)((0, _index.createAnimatedValue)(40)).current;
|
|
269
312
|
(0, _react.useEffect)(() => {
|
|
270
|
-
if (
|
|
271
|
-
_reactNative.Animated.parallel([
|
|
313
|
+
if (open) {
|
|
314
|
+
_reactNative.Animated.parallel([
|
|
315
|
+
// Backdrop opacity uses JS driver — see Modal.tsx for the Fabric reason.
|
|
316
|
+
_reactNative.Animated.timing(opacity, {
|
|
272
317
|
toValue: 1,
|
|
273
318
|
duration: theme.motion.duration.normal,
|
|
274
319
|
easing: _reactNative.Easing.out(_reactNative.Easing.cubic),
|
|
275
|
-
useNativeDriver:
|
|
320
|
+
useNativeDriver: false
|
|
276
321
|
}), _reactNative.Animated.spring(translateY, {
|
|
277
322
|
toValue: 0,
|
|
278
323
|
damping: theme.motion.spring.snappy.damping,
|
|
@@ -281,10 +326,10 @@ const TimePicker = ({
|
|
|
281
326
|
useNativeDriver: true
|
|
282
327
|
})]).start();
|
|
283
328
|
} else {
|
|
284
|
-
|
|
329
|
+
opacity.setValue(0);
|
|
285
330
|
(0, _index.setNativeValue)(translateY, 40);
|
|
286
331
|
}
|
|
287
|
-
}, [
|
|
332
|
+
}, [open, opacity, translateY, theme.motion]);
|
|
288
333
|
const announce = (0, _react.useCallback)(msg => {
|
|
289
334
|
if (_reactNative.Platform.OS === 'ios' || _reactNative.Platform.OS === 'android') {
|
|
290
335
|
_reactNative.AccessibilityInfo.announceForAccessibility(msg);
|
|
@@ -303,21 +348,21 @@ const TimePicker = ({
|
|
|
303
348
|
announce(periods[idx] ?? '');
|
|
304
349
|
}, [periods, announce]);
|
|
305
350
|
const handleCancel = (0, _react.useCallback)(() => {
|
|
306
|
-
(0, _hapticUtils.triggerHaptic)('selection');
|
|
307
|
-
|
|
308
|
-
}, [
|
|
351
|
+
if (theme.components.timePicker?.haptic) (0, _hapticUtils.triggerHaptic)('selection');
|
|
352
|
+
handleCloseModal();
|
|
353
|
+
}, [handleCloseModal, theme.components.timePicker]);
|
|
309
354
|
const handleConfirm = (0, _react.useCallback)(() => {
|
|
310
355
|
const displayHour = hours[hourIndex] ?? 0;
|
|
311
356
|
const period = periods[periodIndex] ?? 'AM';
|
|
312
357
|
const hour24 = to24h(displayHour, period, format);
|
|
313
358
|
const minute = minutes[minuteIndex] ?? 0;
|
|
314
|
-
(0, _hapticUtils.triggerHaptic)('notificationSuccess');
|
|
315
|
-
onSelect({
|
|
359
|
+
if (theme.components.timePicker?.haptic) (0, _hapticUtils.triggerHaptic)('notificationSuccess');
|
|
360
|
+
onSelect?.({
|
|
316
361
|
hour: hour24,
|
|
317
362
|
minute
|
|
318
363
|
});
|
|
319
|
-
|
|
320
|
-
}, [hours, hourIndex, periods, periodIndex, minutes, minuteIndex, format, onSelect,
|
|
364
|
+
handleCloseModal();
|
|
365
|
+
}, [hours, hourIndex, periods, periodIndex, minutes, minuteIndex, format, onSelect, handleCloseModal, theme.components.timePicker]);
|
|
321
366
|
const summary = (0, _react.useMemo)(() => {
|
|
322
367
|
const displayHour = hours[hourIndex] ?? 0;
|
|
323
368
|
const minute = minutes[minuteIndex] ?? 0;
|
|
@@ -328,28 +373,43 @@ const TimePicker = ({
|
|
|
328
373
|
const formatHourItem = (0, _react.useCallback)(item => pad2(Number(item)), []);
|
|
329
374
|
const formatMinuteItem = (0, _react.useCallback)(item => pad2(Number(item)), []);
|
|
330
375
|
const formatPeriodItem = (0, _react.useCallback)(item => String(item), []);
|
|
331
|
-
|
|
332
|
-
|
|
376
|
+
const defaultFormatValue = (0, _react.useCallback)(t => {
|
|
377
|
+
if (format === '24h') return `${pad2(t.hour)}:${pad2(t.minute)}`;
|
|
378
|
+
const {
|
|
379
|
+
displayHour,
|
|
380
|
+
period
|
|
381
|
+
} = from24h(t.hour, '12h');
|
|
382
|
+
return `${displayHour}:${pad2(t.minute)} ${period}`;
|
|
383
|
+
}, [format]);
|
|
384
|
+
const triggerValue = !isControlled && value && Number.isFinite(value.hour) && Number.isFinite(value.minute) ? (formatValue ?? defaultFormatValue)(value) : undefined;
|
|
385
|
+
const modal = /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Modal, {
|
|
386
|
+
visible: open,
|
|
333
387
|
transparent: true,
|
|
334
|
-
statusBarTranslucent: true
|
|
388
|
+
statusBarTranslucent: true
|
|
389
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
390
|
+
// @ts-ignore — Android-only RN 0.71+; iOS ignores it.
|
|
391
|
+
,
|
|
392
|
+
navigationBarTranslucent: true,
|
|
335
393
|
animationType: "none",
|
|
336
|
-
onRequestClose:
|
|
394
|
+
onRequestClose: handleCloseModal,
|
|
337
395
|
testID: testID,
|
|
338
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
396
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View
|
|
397
|
+
// Plain View + collapsable={false} — see Modal.tsx backdrop comment.
|
|
398
|
+
, {
|
|
399
|
+
collapsable: false,
|
|
400
|
+
style: styles.backdrop,
|
|
342
401
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Pressable, {
|
|
343
402
|
style: _reactNative.StyleSheet.absoluteFill,
|
|
344
|
-
onPress:
|
|
403
|
+
onPress: handleCloseModal,
|
|
345
404
|
accessibilityLabel: "Close time picker",
|
|
346
405
|
accessibilityRole: "button"
|
|
347
406
|
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Animated.View, {
|
|
348
407
|
style: [styles.sheet, {
|
|
408
|
+
paddingBottom: insets.bottom + theme.spacing.xl,
|
|
349
409
|
transform: [{
|
|
350
410
|
translateY
|
|
351
411
|
}]
|
|
352
|
-
}, style],
|
|
412
|
+
}, style, containerStyle],
|
|
353
413
|
accessibilityViewIsModal: true,
|
|
354
414
|
accessible: true,
|
|
355
415
|
accessibilityRole: "none",
|
|
@@ -357,7 +417,7 @@ const TimePicker = ({
|
|
|
357
417
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
358
418
|
style: styles.handle
|
|
359
419
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
360
|
-
style: styles.title,
|
|
420
|
+
style: [styles.title, headerLabelStyle],
|
|
361
421
|
accessibilityRole: "header",
|
|
362
422
|
children: title
|
|
363
423
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
@@ -420,7 +480,7 @@ const TimePicker = ({
|
|
|
420
480
|
variant: "ghost",
|
|
421
481
|
tone: "neutral",
|
|
422
482
|
onPress: handleCancel,
|
|
423
|
-
style: styles.footerBtn,
|
|
483
|
+
style: [styles.footerBtn, footerButtonStyle],
|
|
424
484
|
accessibilityHint: "Dismiss without changes",
|
|
425
485
|
testID: "time-picker-cancel"
|
|
426
486
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Button.default, {
|
|
@@ -429,7 +489,7 @@ const TimePicker = ({
|
|
|
429
489
|
tone: "primary",
|
|
430
490
|
haptic: false,
|
|
431
491
|
onPress: handleConfirm,
|
|
432
|
-
style: styles.footerBtn,
|
|
492
|
+
style: [styles.footerBtn, footerButtonStyle],
|
|
433
493
|
accessibilityHint: "Confirm selected time",
|
|
434
494
|
testID: "time-picker-confirm"
|
|
435
495
|
})]
|
|
@@ -437,6 +497,24 @@ const TimePicker = ({
|
|
|
437
497
|
})]
|
|
438
498
|
})
|
|
439
499
|
});
|
|
500
|
+
if (isControlled) return modal;
|
|
501
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
502
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_PickerTrigger.PickerTrigger, {
|
|
503
|
+
label: label,
|
|
504
|
+
placeholder: placeholder,
|
|
505
|
+
helperText: helperText,
|
|
506
|
+
error: error,
|
|
507
|
+
required: required,
|
|
508
|
+
disabled: disabled,
|
|
509
|
+
size: size,
|
|
510
|
+
variant: variant,
|
|
511
|
+
clearable: clearable,
|
|
512
|
+
onClear: onClear,
|
|
513
|
+
value: triggerValue,
|
|
514
|
+
onPress: () => setInternalOpen(true),
|
|
515
|
+
triggerStyle: triggerStyle
|
|
516
|
+
}), modal]
|
|
517
|
+
});
|
|
440
518
|
};
|
|
441
519
|
exports.TimePicker = TimePicker;
|
|
442
520
|
const styles = _reactNative.StyleSheet.create({
|
|
@@ -482,8 +560,8 @@ const buildStyles = theme => {
|
|
|
482
560
|
},
|
|
483
561
|
handle: {
|
|
484
562
|
alignSelf: 'center',
|
|
485
|
-
width: 36,
|
|
486
|
-
height: 4,
|
|
563
|
+
width: theme.components.timePicker?.handleWidth ?? 36,
|
|
564
|
+
height: theme.components.timePicker?.handleHeight ?? 4,
|
|
487
565
|
borderRadius: theme.radius.full,
|
|
488
566
|
backgroundColor: theme.colors.border.secondary,
|
|
489
567
|
marginBottom: theme.spacing.sm
|