@webority-technologies/mobile 0.0.22 → 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 +9 -7
- 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/Avatar/Avatar.js +4 -2
- package/lib/commonjs/components/Badge/Badge.js +5 -5
- package/lib/commonjs/components/Banner/Banner.js +20 -6
- package/lib/commonjs/components/BottomNavigation/BottomNavigation.js +6 -4
- package/lib/commonjs/components/BottomSheet/BottomSheet.js +8 -9
- package/lib/commonjs/components/Box/Box.js +162 -0
- package/lib/commonjs/components/Box/index.js +37 -0
- package/lib/commonjs/components/Button/Button.js +7 -7
- package/lib/commonjs/components/Card/Card.js +3 -3
- package/lib/commonjs/components/Carousel/Carousel.js +4 -2
- package/lib/commonjs/components/Checkbox/Checkbox.js +17 -7
- package/lib/commonjs/components/Chip/Chip.js +4 -2
- package/lib/commonjs/components/DatePicker/DatePicker.js +31 -24
- package/lib/commonjs/components/DateRangePicker/DateRangePicker.js +16 -11
- package/lib/commonjs/components/Dialog/Dialog.js +6 -4
- package/lib/commonjs/components/Drawer/Drawer.js +4 -2
- package/lib/commonjs/components/FieldBase/FieldBase.js +8 -4
- package/lib/commonjs/components/FloatingActionButton/FloatingActionButton.js +23 -13
- package/lib/commonjs/components/FormField/FormField.js +61 -25
- package/lib/commonjs/components/ImageGallery/ImageGallery.js +17 -15
- package/lib/commonjs/components/Input/Input.js +41 -29
- package/lib/commonjs/components/KeyboardAwareScrollView/KeyboardAwareScrollView.js +102 -0
- package/lib/commonjs/components/KeyboardAwareScrollView/index.js +13 -0
- package/lib/commonjs/components/KeyboardToolbar/KeyboardToolbar.js +130 -0
- package/lib/commonjs/components/KeyboardToolbar/index.js +13 -0
- package/lib/commonjs/components/ListItem/ListItem.js +4 -3
- package/lib/commonjs/components/Modal/Modal.js +21 -9
- package/lib/commonjs/components/NumberInput/NumberInput.js +38 -29
- package/lib/commonjs/components/OTPInput/OTPInput.js +37 -22
- package/lib/commonjs/components/Radio/Radio.js +9 -8
- package/lib/commonjs/components/Radio/RadioGroup.js +10 -3
- package/lib/commonjs/components/Rating/Rating.js +4 -3
- package/lib/commonjs/components/SearchBar/SearchBar.js +11 -6
- package/lib/commonjs/components/SegmentedControl/SegmentedControl.js +23 -12
- package/lib/commonjs/components/Select/Select.js +40 -36
- package/lib/commonjs/components/Skeleton/SkeletonContent.js +5 -2
- package/lib/commonjs/components/Slider/Slider.js +241 -225
- package/lib/commonjs/components/Spinner/Spinner.js +5 -5
- package/lib/commonjs/components/Stepper/Stepper.js +6 -5
- package/lib/commonjs/components/Swipeable/Swipeable.js +8 -9
- package/lib/commonjs/components/Switch/Switch.js +29 -16
- package/lib/commonjs/components/Tabs/Tabs.js +8 -5
- package/lib/commonjs/components/Text/Text.js +142 -0
- package/lib/commonjs/components/Text/index.js +13 -0
- package/lib/commonjs/components/TimePicker/TimePicker.js +23 -15
- package/lib/commonjs/components/Toast/Toast.js +22 -10
- package/lib/commonjs/components/Tooltip/Tooltip.js +6 -2
- package/lib/commonjs/components/index.js +156 -103
- package/lib/commonjs/form/FormContext.js +40 -0
- package/lib/commonjs/form/index.js +68 -0
- package/lib/commonjs/form/path.js +79 -0
- package/lib/commonjs/form/rules.js +67 -0
- package/lib/commonjs/form/types.js +2 -0
- package/lib/commonjs/form/useField.js +54 -0
- package/lib/commonjs/form/useForm.js +316 -0
- package/lib/commonjs/hooks/index.js +14 -0
- package/lib/commonjs/hooks/useControllableState.js +30 -0
- package/lib/commonjs/hooks/useReducedMotion.js +31 -0
- package/lib/commonjs/index.js +96 -11
- package/lib/commonjs/theme/ThemeContext.js +30 -2
- package/lib/commonjs/theme/tokens.js +12 -0
- package/lib/commonjs/utils/hapticUtils.js +11 -1
- package/lib/commonjs/utils/index.js +6 -0
- package/lib/module/components/Accordion/Accordion.js +10 -8
- 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/Avatar/Avatar.js +4 -2
- package/lib/module/components/Badge/Badge.js +5 -5
- package/lib/module/components/Banner/Banner.js +20 -6
- package/lib/module/components/BottomNavigation/BottomNavigation.js +6 -4
- package/lib/module/components/BottomSheet/BottomSheet.js +8 -9
- package/lib/module/components/Box/Box.js +156 -0
- package/lib/module/components/Box/index.js +4 -0
- package/lib/module/components/Button/Button.js +7 -7
- package/lib/module/components/Card/Card.js +4 -4
- package/lib/module/components/Carousel/Carousel.js +4 -2
- package/lib/module/components/Checkbox/Checkbox.js +18 -8
- package/lib/module/components/Chip/Chip.js +5 -3
- package/lib/module/components/DatePicker/DatePicker.js +32 -25
- package/lib/module/components/DateRangePicker/DateRangePicker.js +17 -12
- package/lib/module/components/Dialog/Dialog.js +7 -5
- package/lib/module/components/Drawer/Drawer.js +5 -3
- package/lib/module/components/FieldBase/FieldBase.js +8 -4
- package/lib/module/components/FloatingActionButton/FloatingActionButton.js +24 -14
- package/lib/module/components/FormField/FormField.js +62 -26
- package/lib/module/components/ImageGallery/ImageGallery.js +18 -16
- package/lib/module/components/Input/Input.js +41 -29
- package/lib/module/components/KeyboardAwareScrollView/KeyboardAwareScrollView.js +98 -0
- package/lib/module/components/KeyboardAwareScrollView/index.js +4 -0
- package/lib/module/components/KeyboardToolbar/KeyboardToolbar.js +125 -0
- package/lib/module/components/KeyboardToolbar/index.js +4 -0
- package/lib/module/components/ListItem/ListItem.js +5 -4
- package/lib/module/components/Modal/Modal.js +22 -10
- package/lib/module/components/NumberInput/NumberInput.js +36 -27
- package/lib/module/components/OTPInput/OTPInput.js +37 -22
- package/lib/module/components/Radio/Radio.js +10 -9
- package/lib/module/components/Radio/RadioGroup.js +10 -3
- package/lib/module/components/Rating/Rating.js +5 -4
- package/lib/module/components/SearchBar/SearchBar.js +12 -7
- package/lib/module/components/SegmentedControl/SegmentedControl.js +24 -13
- package/lib/module/components/Select/Select.js +41 -37
- package/lib/module/components/Skeleton/SkeletonContent.js +5 -2
- package/lib/module/components/Slider/Slider.js +244 -228
- package/lib/module/components/Spinner/Spinner.js +5 -5
- package/lib/module/components/Stepper/Stepper.js +7 -6
- package/lib/module/components/Swipeable/Swipeable.js +9 -10
- package/lib/module/components/Switch/Switch.js +29 -16
- package/lib/module/components/Tabs/Tabs.js +9 -6
- package/lib/module/components/Text/Text.js +138 -0
- package/lib/module/components/Text/index.js +4 -0
- package/lib/module/components/TimePicker/TimePicker.js +24 -16
- package/lib/module/components/Toast/Toast.js +22 -10
- package/lib/module/components/Tooltip/Tooltip.js +6 -2
- package/lib/module/components/index.js +5 -0
- package/lib/module/form/FormContext.js +32 -0
- package/lib/module/form/index.js +12 -0
- package/lib/module/form/path.js +72 -0
- package/lib/module/form/rules.js +52 -0
- package/lib/module/form/types.js +2 -0
- package/lib/module/form/useField.js +49 -0
- package/lib/module/form/useForm.js +312 -0
- package/lib/module/hooks/index.js +2 -0
- package/lib/module/hooks/useControllableState.js +26 -0
- package/lib/module/hooks/useReducedMotion.js +27 -0
- package/lib/module/index.js +3 -1
- package/lib/module/theme/ThemeContext.js +30 -2
- package/lib/module/theme/tokens.js +12 -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/BottomNavigation/BottomNavigation.d.ts +1 -1
- package/lib/typescript/commonjs/components/Box/Box.d.ts +60 -0
- package/lib/typescript/commonjs/components/Box/index.d.ts +3 -0
- package/lib/typescript/commonjs/components/Button/Button.d.ts +1 -1
- package/lib/typescript/commonjs/components/Card/Card.d.ts +3 -0
- package/lib/typescript/commonjs/components/Checkbox/Checkbox.d.ts +4 -2
- package/lib/typescript/commonjs/components/Chip/Chip.d.ts +3 -0
- package/lib/typescript/commonjs/components/DatePicker/DatePicker.d.ts +6 -3
- package/lib/typescript/commonjs/components/DateRangePicker/DateRangePicker.d.ts +6 -0
- package/lib/typescript/commonjs/components/Dialog/Dialog.d.ts +5 -2
- 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/FormField/FormField.d.ts +13 -2
- package/lib/typescript/commonjs/components/ImageGallery/ImageGallery.d.ts +6 -0
- package/lib/typescript/commonjs/components/KeyboardAwareScrollView/KeyboardAwareScrollView.d.ts +20 -0
- package/lib/typescript/commonjs/components/KeyboardAwareScrollView/index.d.ts +3 -0
- package/lib/typescript/commonjs/components/KeyboardToolbar/KeyboardToolbar.d.ts +29 -0
- package/lib/typescript/commonjs/components/KeyboardToolbar/index.d.ts +3 -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 +6 -2
- package/lib/typescript/commonjs/components/OTPInput/OTPInput.d.ts +9 -2
- package/lib/typescript/commonjs/components/Radio/Radio.d.ts +2 -2
- package/lib/typescript/commonjs/components/Radio/RadioGroup.d.ts +3 -2
- 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 +6 -2
- package/lib/typescript/commonjs/components/Select/Select.d.ts +6 -0
- package/lib/typescript/commonjs/components/Slider/Slider.d.ts +9 -4
- package/lib/typescript/commonjs/components/Spinner/Spinner.d.ts +1 -1
- 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/Switch/Switch.d.ts +3 -2
- package/lib/typescript/commonjs/components/Tabs/Tabs.d.ts +3 -0
- package/lib/typescript/commonjs/components/Text/Text.d.ts +25 -0
- package/lib/typescript/commonjs/components/Text/index.d.ts +3 -0
- package/lib/typescript/commonjs/components/TimePicker/TimePicker.d.ts +6 -3
- package/lib/typescript/commonjs/components/index.d.ts +10 -0
- package/lib/typescript/commonjs/form/FormContext.d.ts +17 -0
- package/lib/typescript/commonjs/form/index.d.ts +9 -0
- package/lib/typescript/commonjs/form/path.d.ts +10 -0
- package/lib/typescript/commonjs/form/rules.d.ts +31 -0
- package/lib/typescript/commonjs/form/types.d.ts +94 -0
- package/lib/typescript/commonjs/form/useField.d.ts +27 -0
- package/lib/typescript/commonjs/form/useForm.d.ts +10 -0
- package/lib/typescript/commonjs/hooks/index.d.ts +3 -0
- package/lib/typescript/commonjs/hooks/useControllableState.d.ts +17 -0
- package/lib/typescript/commonjs/hooks/useReducedMotion.d.ts +8 -0
- package/lib/typescript/commonjs/index.d.ts +4 -2
- package/lib/typescript/commonjs/theme/types.d.ts +17 -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/BottomNavigation/BottomNavigation.d.ts +1 -1
- package/lib/typescript/module/components/Box/Box.d.ts +60 -0
- package/lib/typescript/module/components/Box/index.d.ts +3 -0
- package/lib/typescript/module/components/Button/Button.d.ts +1 -1
- package/lib/typescript/module/components/Card/Card.d.ts +3 -0
- package/lib/typescript/module/components/Checkbox/Checkbox.d.ts +4 -2
- package/lib/typescript/module/components/Chip/Chip.d.ts +3 -0
- package/lib/typescript/module/components/DatePicker/DatePicker.d.ts +6 -3
- package/lib/typescript/module/components/DateRangePicker/DateRangePicker.d.ts +6 -0
- package/lib/typescript/module/components/Dialog/Dialog.d.ts +5 -2
- 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/FormField/FormField.d.ts +13 -2
- package/lib/typescript/module/components/ImageGallery/ImageGallery.d.ts +6 -0
- package/lib/typescript/module/components/KeyboardAwareScrollView/KeyboardAwareScrollView.d.ts +20 -0
- package/lib/typescript/module/components/KeyboardAwareScrollView/index.d.ts +3 -0
- package/lib/typescript/module/components/KeyboardToolbar/KeyboardToolbar.d.ts +29 -0
- package/lib/typescript/module/components/KeyboardToolbar/index.d.ts +3 -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 +6 -2
- package/lib/typescript/module/components/OTPInput/OTPInput.d.ts +9 -2
- package/lib/typescript/module/components/Radio/Radio.d.ts +2 -2
- package/lib/typescript/module/components/Radio/RadioGroup.d.ts +3 -2
- 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 +6 -2
- package/lib/typescript/module/components/Select/Select.d.ts +6 -0
- package/lib/typescript/module/components/Slider/Slider.d.ts +9 -4
- package/lib/typescript/module/components/Spinner/Spinner.d.ts +1 -1
- 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/Switch/Switch.d.ts +3 -2
- package/lib/typescript/module/components/Tabs/Tabs.d.ts +3 -0
- package/lib/typescript/module/components/Text/Text.d.ts +25 -0
- package/lib/typescript/module/components/Text/index.d.ts +3 -0
- package/lib/typescript/module/components/TimePicker/TimePicker.d.ts +6 -3
- package/lib/typescript/module/components/index.d.ts +10 -0
- package/lib/typescript/module/form/FormContext.d.ts +17 -0
- package/lib/typescript/module/form/index.d.ts +9 -0
- package/lib/typescript/module/form/path.d.ts +10 -0
- package/lib/typescript/module/form/rules.d.ts +31 -0
- package/lib/typescript/module/form/types.d.ts +94 -0
- package/lib/typescript/module/form/useField.d.ts +27 -0
- package/lib/typescript/module/form/useForm.d.ts +10 -0
- package/lib/typescript/module/hooks/index.d.ts +3 -0
- package/lib/typescript/module/hooks/useControllableState.d.ts +17 -0
- package/lib/typescript/module/hooks/useReducedMotion.d.ts +8 -0
- package/lib/typescript/module/index.d.ts +4 -2
- package/lib/typescript/module/theme/types.d.ts +17 -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
|
@@ -4,7 +4,7 @@ import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState }
|
|
|
4
4
|
import { AccessibilityInfo, Animated, Easing, Modal, Pressable, StyleSheet, Text, View } from 'react-native';
|
|
5
5
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
6
6
|
import { useTheme, createAnimatedValue, setNativeValue } from "../../theme/index.js";
|
|
7
|
-
import { triggerHaptic } from "../../utils/index.js";
|
|
7
|
+
import { triggerHaptic, resolveHaptic } from "../../utils/index.js";
|
|
8
8
|
import { PickerTrigger } from "../PickerTrigger/index.js";
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -103,6 +103,7 @@ const DateRangePicker = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
103
103
|
confirmLabel = 'Confirm',
|
|
104
104
|
cancelLabel = 'Cancel',
|
|
105
105
|
maxRange,
|
|
106
|
+
haptic,
|
|
106
107
|
style,
|
|
107
108
|
containerStyle,
|
|
108
109
|
headerLabelStyle,
|
|
@@ -147,10 +148,11 @@ const DateRangePicker = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
147
148
|
// Modal open / close animation. Backdrop opacity uses JS driver — see
|
|
148
149
|
// Modal.tsx for the Fabric reason. Sheet transform stays native.
|
|
149
150
|
useEffect(() => {
|
|
151
|
+
let anim;
|
|
150
152
|
if (open) {
|
|
151
153
|
backdrop.setValue(0);
|
|
152
154
|
setNativeValue(sheet, 0);
|
|
153
|
-
Animated.parallel([Animated.timing(backdrop, {
|
|
155
|
+
anim = Animated.parallel([Animated.timing(backdrop, {
|
|
154
156
|
toValue: 1,
|
|
155
157
|
duration: theme.motion.duration.normal,
|
|
156
158
|
easing: Easing.out(Easing.cubic),
|
|
@@ -161,8 +163,10 @@ const DateRangePicker = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
161
163
|
stiffness: theme.motion.spring.gentle.stiffness,
|
|
162
164
|
mass: theme.motion.spring.gentle.mass,
|
|
163
165
|
useNativeDriver: true
|
|
164
|
-
})])
|
|
166
|
+
})]);
|
|
167
|
+
anim.start();
|
|
165
168
|
}
|
|
169
|
+
return () => anim?.stop();
|
|
166
170
|
}, [open, backdrop, sheet, theme.motion]);
|
|
167
171
|
const disabledIsoSet = useMemo(() => {
|
|
168
172
|
const set = new Set();
|
|
@@ -181,7 +185,7 @@ const DateRangePicker = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
181
185
|
const headerLabel = useMemo(() => formatMonthYear(anchor, locale), [anchor, locale]);
|
|
182
186
|
const weekdays = useMemo(() => weekdayLabels(locale, weekStartsOn), [locale, weekStartsOn]);
|
|
183
187
|
const animateMonthChange = useCallback(delta => {
|
|
184
|
-
if (
|
|
188
|
+
if (haptic !== false) triggerHaptic('impactLight');
|
|
185
189
|
const direction = delta > 0 ? 1 : -1;
|
|
186
190
|
const distance = theme.components.dateRangePicker?.monthSlideDistance ?? 32;
|
|
187
191
|
const outDuration = theme.components.dateRangePicker?.monthSlideOutDuration ?? theme.motion.duration.fast ?? 140;
|
|
@@ -212,12 +216,13 @@ const DateRangePicker = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
212
216
|
const next = addMonths(anchor, delta);
|
|
213
217
|
setAnchor(next);
|
|
214
218
|
AccessibilityInfo.announceForAccessibility(formatMonthYear(next, locale));
|
|
215
|
-
}, [anchor, locale, monthFade, monthSlide, theme.components.dateRangePicker, theme.motion.duration.fast]);
|
|
219
|
+
}, [anchor, haptic, locale, monthFade, monthSlide, theme.components.dateRangePicker, theme.motion.duration.fast]);
|
|
216
220
|
const goPrev = useCallback(() => animateMonthChange(-1), [animateMonthChange]);
|
|
217
221
|
const goNext = useCallback(() => animateMonthChange(1), [animateMonthChange]);
|
|
218
222
|
const pressDay = useCallback(cell => {
|
|
219
223
|
if (isDisabled(cell.date)) return;
|
|
220
|
-
|
|
224
|
+
const h = resolveHaptic(haptic, 'selection');
|
|
225
|
+
if (h) triggerHaptic(h);
|
|
221
226
|
const target = cell.date;
|
|
222
227
|
|
|
223
228
|
// First tap: pick start. Or, when there's already a complete range, restart.
|
|
@@ -236,7 +241,7 @@ const DateRangePicker = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
236
241
|
} else {
|
|
237
242
|
if (maxRange && daysBetween(pendingStart, target) + 1 > maxRange) {
|
|
238
243
|
// Reject ranges longer than maxRange — restart from the new tap.
|
|
239
|
-
if (
|
|
244
|
+
if (haptic !== false) triggerHaptic('notificationWarning');
|
|
240
245
|
setPendingStart(target);
|
|
241
246
|
setPendingEnd(null);
|
|
242
247
|
return;
|
|
@@ -247,7 +252,7 @@ const DateRangePicker = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
247
252
|
if (!cell.inMonth) {
|
|
248
253
|
setAnchor(new Date(cell.date.getFullYear(), cell.date.getMonth(), 1));
|
|
249
254
|
}
|
|
250
|
-
}, [isDisabled, pendingStart, pendingEnd, maxRange,
|
|
255
|
+
}, [isDisabled, pendingStart, pendingEnd, maxRange, haptic]);
|
|
251
256
|
const handleCloseModal = useCallback(() => {
|
|
252
257
|
if (isControlled) {
|
|
253
258
|
onClose?.();
|
|
@@ -271,18 +276,18 @@ const DateRangePicker = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
271
276
|
});
|
|
272
277
|
}, [backdrop, handleCloseModal, sheet, theme.motion.duration.fast]);
|
|
273
278
|
const handleCancel = useCallback(() => {
|
|
274
|
-
if (
|
|
279
|
+
if (haptic !== false) triggerHaptic('selection');
|
|
275
280
|
handleClose();
|
|
276
|
-
}, [handleClose,
|
|
281
|
+
}, [handleClose, haptic]);
|
|
277
282
|
const handleConfirm = useCallback(() => {
|
|
278
283
|
if (!pendingStart || !pendingEnd) return;
|
|
279
|
-
if (
|
|
284
|
+
if (haptic !== false) triggerHaptic('notificationSuccess');
|
|
280
285
|
onChange?.({
|
|
281
286
|
start: pendingStart,
|
|
282
287
|
end: pendingEnd
|
|
283
288
|
});
|
|
284
289
|
handleClose();
|
|
285
|
-
}, [handleClose, onChange, pendingStart, pendingEnd,
|
|
290
|
+
}, [handleClose, onChange, pendingStart, pendingEnd, haptic]);
|
|
286
291
|
const sheetTranslate = sheet.interpolate({
|
|
287
292
|
inputRange: [0, 1],
|
|
288
293
|
outputRange: [320, 0]
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import React, { useCallback, useMemo } from 'react';
|
|
4
4
|
import { ActivityIndicator, Pressable, StyleSheet, Text, View } from 'react-native';
|
|
5
5
|
import { fontFor, useTheme } from "../../theme/index.js";
|
|
6
|
-
import { triggerHaptic } from "../../utils/hapticUtils.js";
|
|
6
|
+
import { resolveHaptic, triggerHaptic } from "../../utils/hapticUtils.js";
|
|
7
7
|
import { Modal } from "../Modal/Modal.js";
|
|
8
8
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
9
|
const Dialog = props => {
|
|
@@ -25,6 +25,7 @@ const Dialog = props => {
|
|
|
25
25
|
actionsRowStyle,
|
|
26
26
|
actionButtonStyle,
|
|
27
27
|
actionTextStyle,
|
|
28
|
+
haptic,
|
|
28
29
|
testID
|
|
29
30
|
} = props;
|
|
30
31
|
const theme = useTheme();
|
|
@@ -36,12 +37,13 @@ const Dialog = props => {
|
|
|
36
37
|
const actionButtonMinHeight = dialogTokens?.actionButtonMinHeight ?? 44;
|
|
37
38
|
const handleAction = useCallback(action => {
|
|
38
39
|
if (action.loading) return;
|
|
39
|
-
|
|
40
|
+
const h = resolveHaptic(haptic, 'selection');
|
|
41
|
+
if (h) triggerHaptic(h);
|
|
40
42
|
action.onPress();
|
|
41
43
|
if (dismissOnAction) {
|
|
42
44
|
onClose();
|
|
43
45
|
}
|
|
44
|
-
}, [dismissOnAction, onClose,
|
|
46
|
+
}, [dismissOnAction, onClose, haptic]);
|
|
45
47
|
return /*#__PURE__*/_jsx(Modal, {
|
|
46
48
|
visible: visible,
|
|
47
49
|
onRequestClose: onClose,
|
|
@@ -144,7 +146,7 @@ const tintForVariant = (theme, variant) => {
|
|
|
144
146
|
base: theme.colors.warning,
|
|
145
147
|
muted: theme.colors.background.secondary
|
|
146
148
|
};
|
|
147
|
-
case '
|
|
149
|
+
case 'error':
|
|
148
150
|
return {
|
|
149
151
|
base: theme.colors.error,
|
|
150
152
|
muted: theme.colors.background.secondary
|
|
@@ -175,7 +177,7 @@ const actionStyleFor = (theme, tone, variant) => {
|
|
|
175
177
|
textColor: theme.colors.text.inverse
|
|
176
178
|
};
|
|
177
179
|
}
|
|
178
|
-
case '
|
|
180
|
+
case 'error':
|
|
179
181
|
return {
|
|
180
182
|
backgroundColor: theme.colors.error,
|
|
181
183
|
borderColor: 'transparent',
|
|
@@ -26,7 +26,7 @@ import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
|
26
26
|
import Animated, { Extrapolation, interpolate, runOnJS, useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';
|
|
27
27
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
28
28
|
import { useTheme } from "../../theme/index.js";
|
|
29
|
-
import { triggerHaptic } from "../../utils/index.js";
|
|
29
|
+
import { resolveHaptic, triggerHaptic } from "../../utils/index.js";
|
|
30
30
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
31
31
|
const DEFAULT_CLOSE_VELOCITY_THRESHOLD = 1000;
|
|
32
32
|
const DEFAULT_DRAG_ACTIVATION_OFFSET = 10;
|
|
@@ -40,6 +40,7 @@ const Drawer = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
40
40
|
enableSwipe = true,
|
|
41
41
|
enableBackdropPress = true,
|
|
42
42
|
backdropOpacity = 0.5,
|
|
43
|
+
haptic,
|
|
43
44
|
containerStyle,
|
|
44
45
|
children,
|
|
45
46
|
accessibilityLabel,
|
|
@@ -219,9 +220,10 @@ const Drawer = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
219
220
|
const styles = useMemo(() => buildStyles(theme), [theme]);
|
|
220
221
|
const handleBackdropPress = useCallback(() => {
|
|
221
222
|
if (!enableBackdropPress) return;
|
|
222
|
-
|
|
223
|
+
const h = resolveHaptic(haptic, 'selection');
|
|
224
|
+
if (h) triggerHaptic(h);
|
|
223
225
|
close();
|
|
224
|
-
}, [enableBackdropPress, close,
|
|
226
|
+
}, [enableBackdropPress, close, haptic]);
|
|
225
227
|
|
|
226
228
|
// Per-side container layout — must be a hook on every render path so we
|
|
227
229
|
// can't gate it on the lazy-mount early-return below.
|
|
@@ -286,20 +286,24 @@ export const FieldBase = props => {
|
|
|
286
286
|
const focusAnim = useRef(createAnimatedValue(focused ? 1 : 0)).current;
|
|
287
287
|
const errorAnim = useRef(createAnimatedValue(error ? 1 : 0)).current;
|
|
288
288
|
useEffect(() => {
|
|
289
|
-
Animated.timing(focusAnim, {
|
|
289
|
+
const anim = Animated.timing(focusAnim, {
|
|
290
290
|
toValue: focused ? 1 : 0,
|
|
291
291
|
duration: theme.motion.duration.fast,
|
|
292
292
|
easing: Easing.bezier(...theme.motion.easing.standard),
|
|
293
293
|
useNativeDriver: false
|
|
294
|
-
})
|
|
294
|
+
});
|
|
295
|
+
anim.start();
|
|
296
|
+
return () => anim.stop();
|
|
295
297
|
}, [focused, focusAnim, theme.motion.duration.fast, theme.motion.easing.standard]);
|
|
296
298
|
useEffect(() => {
|
|
297
|
-
Animated.timing(errorAnim, {
|
|
299
|
+
const anim = Animated.timing(errorAnim, {
|
|
298
300
|
toValue: error ? 1 : 0,
|
|
299
301
|
duration: theme.motion.duration.fast,
|
|
300
302
|
easing: Easing.bezier(...theme.motion.easing.standard),
|
|
301
303
|
useNativeDriver: false
|
|
302
|
-
})
|
|
304
|
+
});
|
|
305
|
+
anim.start();
|
|
306
|
+
return () => anim.stop();
|
|
303
307
|
}, [error, errorAnim, theme.motion.duration.fast, theme.motion.easing.standard]);
|
|
304
308
|
|
|
305
309
|
// Resting border + fill (pre-animation). Error wins over focus when both
|
|
@@ -5,7 +5,7 @@ import { Animated, Easing, Pressable, StyleSheet, Text, View } from 'react-nativ
|
|
|
5
5
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
6
6
|
import { useTheme, createAnimatedValue, setNativeValue } from "../../theme/index.js";
|
|
7
7
|
import { usePressAnimation } from "../../hooks/usePressAnimation.js";
|
|
8
|
-
import { triggerHaptic } from "../../utils/hapticUtils.js";
|
|
8
|
+
import { triggerHaptic, resolveHaptic } from "../../utils/hapticUtils.js";
|
|
9
9
|
|
|
10
10
|
// Local shape mirror — see types.ts FloatingActionButtonTokens for the canonical
|
|
11
11
|
// definition. Declared here so the component can read tokens before types.ts is
|
|
@@ -64,6 +64,7 @@ const FloatingActionButton = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
64
64
|
isScrolling = false,
|
|
65
65
|
accessibilityLabel,
|
|
66
66
|
accessibilityHint,
|
|
67
|
+
haptic,
|
|
67
68
|
style,
|
|
68
69
|
containerStyle,
|
|
69
70
|
labelStyle,
|
|
@@ -83,19 +84,20 @@ const FloatingActionButton = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
83
84
|
const fabTokens = theme.components.floatingActionButton;
|
|
84
85
|
const edgeOffset = fabTokens?.edgeOffset ?? 24;
|
|
85
86
|
const defaultBottomOffset = fabTokens?.bottomOffset ?? 24;
|
|
86
|
-
const pressHaptic = fabTokens?.pressHaptic ?? false;
|
|
87
87
|
const hideAnim = useRef(createAnimatedValue(0)).current;
|
|
88
88
|
useEffect(() => {
|
|
89
89
|
if (!hideOnScroll) {
|
|
90
90
|
setNativeValue(hideAnim, 0);
|
|
91
91
|
return;
|
|
92
92
|
}
|
|
93
|
-
Animated.timing(hideAnim, {
|
|
93
|
+
const anim = Animated.timing(hideAnim, {
|
|
94
94
|
toValue: isScrolling ? 1 : 0,
|
|
95
95
|
duration: theme.motion.duration.normal,
|
|
96
96
|
easing: Easing.bezier(...theme.motion.easing.standard),
|
|
97
97
|
useNativeDriver: true
|
|
98
|
-
})
|
|
98
|
+
});
|
|
99
|
+
anim.start();
|
|
100
|
+
return () => anim.stop();
|
|
99
101
|
}, [hideOnScroll, isScrolling, hideAnim, theme.motion.duration.normal, theme.motion.easing.standard]);
|
|
100
102
|
const hideTranslateY = hideAnim.interpolate({
|
|
101
103
|
inputRange: [0, 1],
|
|
@@ -107,7 +109,8 @@ const FloatingActionButton = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
107
109
|
});
|
|
108
110
|
const handlePress = _event => {
|
|
109
111
|
if (disabled) return;
|
|
110
|
-
|
|
112
|
+
const h = resolveHaptic(haptic, 'impactLight');
|
|
113
|
+
if (h) triggerHaptic(h);
|
|
111
114
|
onPress();
|
|
112
115
|
};
|
|
113
116
|
const renderedIcon = icon;
|
|
@@ -176,7 +179,7 @@ const FloatingActionButton = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
176
179
|
borderRadius: sizeStyles.diameter / 2
|
|
177
180
|
}, theme.shadows.lg, {
|
|
178
181
|
backgroundColor,
|
|
179
|
-
opacity: disabled ? 0.
|
|
182
|
+
opacity: disabled ? 0.55 : 1
|
|
180
183
|
}],
|
|
181
184
|
children: [renderedIcon, isExtended ? /*#__PURE__*/_jsx(Text, {
|
|
182
185
|
style: [styles.label, {
|
|
@@ -222,6 +225,7 @@ const FloatingActionButtonGroup = props => {
|
|
|
222
225
|
bottomOffset,
|
|
223
226
|
size = 'md',
|
|
224
227
|
accessibilityLabel = 'Quick actions',
|
|
228
|
+
haptic,
|
|
225
229
|
containerStyle,
|
|
226
230
|
secondaryActionStyle,
|
|
227
231
|
labelPillStyle,
|
|
@@ -237,7 +241,6 @@ const FloatingActionButtonGroup = props => {
|
|
|
237
241
|
const defaultBottomOffset = fabTokens?.bottomOffset ?? 24;
|
|
238
242
|
const secondaryGap = fabTokens?.secondaryGap ?? 16;
|
|
239
243
|
const staggerMs = fabTokens?.staggerMs ?? 50;
|
|
240
|
-
const pressHaptic = fabTokens?.pressHaptic ?? false;
|
|
241
244
|
const isControlled = typeof controlledOpen === 'boolean';
|
|
242
245
|
const [internalOpen, setInternalOpen] = useState(defaultOpen);
|
|
243
246
|
const isOpen = isControlled ? controlledOpen : internalOpen;
|
|
@@ -271,12 +274,13 @@ const FloatingActionButtonGroup = props => {
|
|
|
271
274
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
272
275
|
}, [actions.length]);
|
|
273
276
|
useEffect(() => {
|
|
274
|
-
Animated.timing(progress, {
|
|
277
|
+
const progressAnim = Animated.timing(progress, {
|
|
275
278
|
toValue: isOpen ? 1 : 0,
|
|
276
279
|
duration: theme.motion.duration.normal,
|
|
277
280
|
easing: Easing.bezier(...theme.motion.easing.standard),
|
|
278
281
|
useNativeDriver: true
|
|
279
|
-
})
|
|
282
|
+
});
|
|
283
|
+
progressAnim.start();
|
|
280
284
|
const animations = itemAnims.current.map(value => Animated.timing(value, {
|
|
281
285
|
toValue: isOpen ? 1 : 0,
|
|
282
286
|
duration: theme.motion.duration.normal,
|
|
@@ -285,23 +289,29 @@ const FloatingActionButtonGroup = props => {
|
|
|
285
289
|
}));
|
|
286
290
|
// Reverse stagger order on close so items closest to primary collapse last
|
|
287
291
|
const ordered = isOpen ? animations : [...animations].reverse();
|
|
288
|
-
Animated.stagger(staggerMs, ordered)
|
|
292
|
+
const staggerAnim = Animated.stagger(staggerMs, ordered);
|
|
293
|
+
staggerAnim.start();
|
|
294
|
+
return () => {
|
|
295
|
+
progressAnim.stop();
|
|
296
|
+
staggerAnim.stop();
|
|
297
|
+
};
|
|
289
298
|
}, [isOpen, progress, theme.motion.duration.normal, theme.motion.easing.standard, actions.length, itemAnimsVersion, staggerMs]);
|
|
290
299
|
const setOpen = useCallback(next => {
|
|
291
|
-
|
|
300
|
+
const h = resolveHaptic(haptic, 'impactLight');
|
|
301
|
+
if (h) triggerHaptic(h);
|
|
292
302
|
if (!isControlled) setInternalOpen(next);
|
|
293
303
|
onOpenChange?.(next);
|
|
294
|
-
}, [isControlled, onOpenChange,
|
|
304
|
+
}, [isControlled, onOpenChange, haptic]);
|
|
295
305
|
const handlePrimaryPress = useCallback(() => {
|
|
296
306
|
setOpen(!isOpen);
|
|
297
307
|
}, [isOpen, setOpen]);
|
|
298
308
|
const handleActionPress = useCallback(action => {
|
|
299
|
-
if (
|
|
309
|
+
if (haptic !== false) triggerHaptic('selection');
|
|
300
310
|
action.onPress();
|
|
301
311
|
// Close after action runs
|
|
302
312
|
if (!isControlled) setInternalOpen(false);
|
|
303
313
|
onOpenChange?.(false);
|
|
304
|
-
}, [isControlled, onOpenChange,
|
|
314
|
+
}, [isControlled, onOpenChange, haptic]);
|
|
305
315
|
const handleBackdropPress = useCallback(() => {
|
|
306
316
|
if (isOpen) setOpen(false);
|
|
307
317
|
}, [isOpen, setOpen]);
|
|
@@ -3,13 +3,15 @@
|
|
|
3
3
|
import React, { forwardRef, useEffect, useMemo, useRef } from 'react';
|
|
4
4
|
import { Animated, StyleSheet, Text, View } from 'react-native';
|
|
5
5
|
import { fontFor, useTheme, createAnimatedValue } from "../../theme/index.js";
|
|
6
|
-
import {
|
|
6
|
+
import { useOptionalFormContext } from "../../form/FormContext.js";
|
|
7
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
7
8
|
const FormField = /*#__PURE__*/forwardRef((props, ref) => {
|
|
8
9
|
const {
|
|
9
10
|
label,
|
|
10
11
|
helperText,
|
|
11
12
|
error,
|
|
12
13
|
required = false,
|
|
14
|
+
name,
|
|
13
15
|
children,
|
|
14
16
|
layout = 'stacked',
|
|
15
17
|
labelStyle,
|
|
@@ -22,19 +24,32 @@ const FormField = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
22
24
|
} = props;
|
|
23
25
|
const theme = useTheme();
|
|
24
26
|
const styles = useMemo(() => buildStyles(theme), [theme]);
|
|
25
|
-
const
|
|
27
|
+
const formCtx = useOptionalFormContext();
|
|
28
|
+
const isConnected = name != null && formCtx != null;
|
|
29
|
+
const field = isConnected ? formCtx.getFieldProps(name) : null;
|
|
30
|
+
const isRenderProp = typeof children === 'function';
|
|
31
|
+
|
|
32
|
+
// Error the FormField renders itself. When connected to an element child the
|
|
33
|
+
// child (Input/Select etc.) shows the injected error, so FormField stays
|
|
34
|
+
// quiet to avoid a duplicate message; the render-prop form keeps it here.
|
|
35
|
+
const ownError = isConnected ? isRenderProp ? field?.error : undefined : error;
|
|
36
|
+
// Error used purely for assistive tech — always reflects the real error.
|
|
37
|
+
const a11yError = isConnected ? field?.error : error;
|
|
38
|
+
const errorOpacity = useRef(createAnimatedValue(ownError ? 1 : 0)).current;
|
|
26
39
|
useEffect(() => {
|
|
27
|
-
Animated.timing(errorOpacity, {
|
|
28
|
-
toValue:
|
|
29
|
-
duration:
|
|
40
|
+
const anim = Animated.timing(errorOpacity, {
|
|
41
|
+
toValue: ownError ? 1 : 0,
|
|
42
|
+
duration: theme.motion.duration.fast,
|
|
30
43
|
useNativeDriver: true
|
|
31
|
-
})
|
|
32
|
-
|
|
44
|
+
});
|
|
45
|
+
anim.start();
|
|
46
|
+
return () => anim.stop();
|
|
47
|
+
}, [ownError, errorOpacity, theme.motion.duration.fast]);
|
|
33
48
|
const computedAccessibilityLabel = useMemo(() => {
|
|
34
49
|
if (accessibilityLabel) return accessibilityLabel;
|
|
35
50
|
if (!label) return undefined;
|
|
36
|
-
return
|
|
37
|
-
}, [accessibilityLabel, label,
|
|
51
|
+
return a11yError ? `${label}, error: ${a11yError}` : label;
|
|
52
|
+
}, [accessibilityLabel, label, a11yError]);
|
|
38
53
|
const labelNode = label ? /*#__PURE__*/_jsxs(Text, {
|
|
39
54
|
style: [styles.label, layout === 'inline' ? styles.labelInline : null, labelStyle],
|
|
40
55
|
children: [label, required ? /*#__PURE__*/_jsx(Text, {
|
|
@@ -43,39 +58,60 @@ const FormField = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
43
58
|
}) : null]
|
|
44
59
|
}) : null;
|
|
45
60
|
const inputContainerStyle = layout === 'inline' ? styles.inputInline : styles.inputStacked;
|
|
46
|
-
const showError = Boolean(
|
|
61
|
+
const showError = Boolean(ownError);
|
|
47
62
|
const showHelper = !showError && Boolean(helperText);
|
|
63
|
+
const renderedChildren = (() => {
|
|
64
|
+
if (isRenderProp) {
|
|
65
|
+
return field ? children(field) : null;
|
|
66
|
+
}
|
|
67
|
+
if (isConnected && field && /*#__PURE__*/React.isValidElement(children)) {
|
|
68
|
+
const child = children;
|
|
69
|
+
const childOnChangeText = child.props.onChangeText;
|
|
70
|
+
const childOnBlur = child.props.onBlur;
|
|
71
|
+
const childOnFocus = child.props.onFocus;
|
|
72
|
+
return /*#__PURE__*/React.cloneElement(child, {
|
|
73
|
+
value: field.value,
|
|
74
|
+
onChangeText: text => {
|
|
75
|
+
field.onChangeText(text);
|
|
76
|
+
childOnChangeText?.(text);
|
|
77
|
+
},
|
|
78
|
+
onBlur: e => {
|
|
79
|
+
field.onBlur();
|
|
80
|
+
childOnBlur?.(e);
|
|
81
|
+
},
|
|
82
|
+
onFocus: e => {
|
|
83
|
+
field.onFocus();
|
|
84
|
+
childOnFocus?.(e);
|
|
85
|
+
},
|
|
86
|
+
error: field.error,
|
|
87
|
+
ref: node => formCtx.registerField(name, node ?? null)
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return children;
|
|
91
|
+
})();
|
|
48
92
|
return /*#__PURE__*/_jsxs(View, {
|
|
49
93
|
ref: ref,
|
|
50
94
|
style: [layout === 'inline' ? styles.rootInline : styles.rootStacked, containerStyle],
|
|
51
95
|
accessibilityLabel: computedAccessibilityLabel
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
// error message itself is folded into the label and hint.
|
|
96
|
+
// RN's AccessibilityState type has no `invalid` field, so the error is
|
|
97
|
+
// folded into the label and surfaced again via the hint.
|
|
55
98
|
,
|
|
56
|
-
accessibilityHint:
|
|
99
|
+
accessibilityHint: a11yError ? a11yError : undefined,
|
|
57
100
|
testID: testID,
|
|
58
|
-
children: [
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
children: children
|
|
62
|
-
})]
|
|
63
|
-
}) : /*#__PURE__*/_jsxs(_Fragment, {
|
|
64
|
-
children: [labelNode, /*#__PURE__*/_jsx(View, {
|
|
65
|
-
style: [inputContainerStyle, inputContainerStyleProp],
|
|
66
|
-
children: children
|
|
67
|
-
})]
|
|
101
|
+
children: [labelNode, /*#__PURE__*/_jsx(View, {
|
|
102
|
+
style: [inputContainerStyle, inputContainerStyleProp],
|
|
103
|
+
children: renderedChildren
|
|
68
104
|
}), showHelper ? /*#__PURE__*/_jsx(Text, {
|
|
69
105
|
style: [styles.helper, helperStyle],
|
|
70
106
|
numberOfLines: 2,
|
|
71
107
|
children: helperText
|
|
72
|
-
}) : null,
|
|
108
|
+
}) : null, ownError ? /*#__PURE__*/_jsx(Animated.Text, {
|
|
73
109
|
style: [styles.error, {
|
|
74
110
|
opacity: errorOpacity
|
|
75
111
|
}, errorStyle],
|
|
76
112
|
numberOfLines: 2,
|
|
77
113
|
accessibilityLiveRegion: "polite",
|
|
78
|
-
children:
|
|
114
|
+
children: ownError
|
|
79
115
|
}) : null]
|
|
80
116
|
});
|
|
81
117
|
});
|
|
@@ -26,7 +26,7 @@ import Animated, { runOnJS, useAnimatedStyle, useSharedValue, withSpring, withTi
|
|
|
26
26
|
import { Carousel } from "../Carousel/index.js";
|
|
27
27
|
import { Skeleton } from "../Skeleton/index.js";
|
|
28
28
|
import { useTheme } from "../../theme/index.js";
|
|
29
|
-
import { triggerHaptic } from "../../utils/index.js";
|
|
29
|
+
import { resolveHaptic, triggerHaptic } from "../../utils/index.js";
|
|
30
30
|
|
|
31
31
|
// Local shape mirror — see types.ts ImageGalleryTokens for the canonical definition.
|
|
32
32
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
@@ -46,6 +46,7 @@ const ImageGallery = ({
|
|
|
46
46
|
enableLightbox = true,
|
|
47
47
|
enablePinchZoom = true,
|
|
48
48
|
onIndexChange,
|
|
49
|
+
haptic,
|
|
49
50
|
loading = false,
|
|
50
51
|
accessibilityLabel,
|
|
51
52
|
containerStyle,
|
|
@@ -53,8 +54,6 @@ const ImageGallery = ({
|
|
|
53
54
|
}) => {
|
|
54
55
|
const theme = useTheme();
|
|
55
56
|
const styles = useMemo(() => buildStyles(theme), [theme]);
|
|
56
|
-
const galleryTokens = theme.components.imageGallery;
|
|
57
|
-
const pressHaptic = galleryTokens?.pressHaptic ?? false;
|
|
58
57
|
const [currentIndex, setCurrentIndex] = useState(clamp(initialIndex, 0, Math.max(0, images.length - 1)));
|
|
59
58
|
const [lightboxOpen, setLightboxOpen] = useState(false);
|
|
60
59
|
const handleIndexChange = useCallback(idx => {
|
|
@@ -63,13 +62,15 @@ const ImageGallery = ({
|
|
|
63
62
|
}, [onIndexChange]);
|
|
64
63
|
const openLightbox = useCallback(() => {
|
|
65
64
|
if (!enableLightbox) return;
|
|
66
|
-
|
|
65
|
+
const h = resolveHaptic(haptic, 'selection');
|
|
66
|
+
if (h) triggerHaptic(h);
|
|
67
67
|
setLightboxOpen(true);
|
|
68
|
-
}, [enableLightbox,
|
|
68
|
+
}, [enableLightbox, haptic]);
|
|
69
69
|
const closeLightbox = useCallback(() => {
|
|
70
|
-
|
|
70
|
+
const h = resolveHaptic(haptic, 'selection');
|
|
71
|
+
if (h) triggerHaptic(h);
|
|
71
72
|
setLightboxOpen(false);
|
|
72
|
-
}, [
|
|
73
|
+
}, [haptic]);
|
|
73
74
|
const renderImage = useCallback((image, idx) => {
|
|
74
75
|
const a11y = image.alt ?? `Image ${idx + 1} of ${images.length}`;
|
|
75
76
|
return /*#__PURE__*/_jsx(Pressable, {
|
|
@@ -123,7 +124,7 @@ const ImageGallery = ({
|
|
|
123
124
|
showCounter: showCounter,
|
|
124
125
|
onClose: closeLightbox,
|
|
125
126
|
onIndexChange: handleIndexChange,
|
|
126
|
-
|
|
127
|
+
haptic: haptic
|
|
127
128
|
}) : null]
|
|
128
129
|
});
|
|
129
130
|
if (loading) {
|
|
@@ -212,7 +213,7 @@ const Lightbox = ({
|
|
|
212
213
|
showCounter,
|
|
213
214
|
onClose,
|
|
214
215
|
onIndexChange,
|
|
215
|
-
|
|
216
|
+
haptic
|
|
216
217
|
}) => {
|
|
217
218
|
const theme = useTheme();
|
|
218
219
|
const styles = useMemo(() => buildLightboxStyles(theme), [theme]);
|
|
@@ -224,11 +225,12 @@ const Lightbox = ({
|
|
|
224
225
|
const [activeIndex, setActiveIndex] = useState(initialIndex);
|
|
225
226
|
const handleSwipe = useCallback(idx => {
|
|
226
227
|
if (idx !== activeIndex) {
|
|
227
|
-
|
|
228
|
+
const h = resolveHaptic(haptic, 'selection');
|
|
229
|
+
if (h) triggerHaptic(h);
|
|
228
230
|
setActiveIndex(idx);
|
|
229
231
|
onIndexChange(idx);
|
|
230
232
|
}
|
|
231
|
-
}, [activeIndex, onIndexChange,
|
|
233
|
+
}, [activeIndex, onIndexChange, haptic]);
|
|
232
234
|
const renderItem = useCallback((image, idx) => /*#__PURE__*/_jsx(ZoomableImage, {
|
|
233
235
|
image: image,
|
|
234
236
|
index: idx,
|
|
@@ -238,8 +240,8 @@ const Lightbox = ({
|
|
|
238
240
|
maxScale: maxScale,
|
|
239
241
|
minScale: minScale,
|
|
240
242
|
doubleTapScale: doubleTapScale,
|
|
241
|
-
|
|
242
|
-
}), [activeIndex, enablePinchZoom, images.length, maxScale, minScale, doubleTapScale,
|
|
243
|
+
haptic: haptic
|
|
244
|
+
}), [activeIndex, enablePinchZoom, images.length, maxScale, minScale, doubleTapScale, haptic]);
|
|
243
245
|
const caption = images[activeIndex]?.caption;
|
|
244
246
|
return /*#__PURE__*/_jsx(RNModal, {
|
|
245
247
|
visible: visible,
|
|
@@ -310,7 +312,7 @@ const ZoomableImage = ({
|
|
|
310
312
|
maxScale,
|
|
311
313
|
minScale,
|
|
312
314
|
doubleTapScale,
|
|
313
|
-
|
|
315
|
+
haptic
|
|
314
316
|
}) => {
|
|
315
317
|
const screen = Dimensions.get('window');
|
|
316
318
|
const scale = useSharedValue(1);
|
|
@@ -346,8 +348,8 @@ const ZoomableImage = ({
|
|
|
346
348
|
}
|
|
347
349
|
}, [active, scale, translateX, translateY, savedScale, savedTranslateX, savedTranslateY]);
|
|
348
350
|
const triggerImpact = useCallback(() => {
|
|
349
|
-
if (
|
|
350
|
-
}, [
|
|
351
|
+
if (haptic !== false) triggerHaptic('impactLight');
|
|
352
|
+
}, [haptic]);
|
|
351
353
|
const pinch = useMemo(() => Gesture.Pinch().enabled(enabled).onStart(() => {
|
|
352
354
|
'worklet';
|
|
353
355
|
|