@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
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { Animated, Easing, Pressable, StyleSheet, Text, TextInput, View } from 'react-native';
|
|
5
5
|
import { fontFor, useTheme, createAnimatedValue, setNativeValue } from "../../theme/index.js";
|
|
6
|
-
import {
|
|
6
|
+
import { useReducedMotion, useControllableState } from "../../hooks/index.js";
|
|
7
|
+
import { triggerHaptic, resolveHaptic } from "../../utils/index.js";
|
|
7
8
|
import { FieldBase } from "../FieldBase/FieldBase.js";
|
|
8
9
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
10
|
const sizeMap = {
|
|
@@ -36,6 +37,7 @@ const sanitizeChar = (input, kind) => {
|
|
|
36
37
|
const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
37
38
|
const {
|
|
38
39
|
value,
|
|
40
|
+
defaultValue,
|
|
39
41
|
onChange,
|
|
40
42
|
onComplete,
|
|
41
43
|
length = 6,
|
|
@@ -45,12 +47,18 @@ const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
45
47
|
error,
|
|
46
48
|
size = 'md',
|
|
47
49
|
secure = false,
|
|
50
|
+
haptic,
|
|
48
51
|
accessibilityLabel,
|
|
49
52
|
style,
|
|
50
53
|
cellStyle,
|
|
51
54
|
textStyle,
|
|
52
55
|
testID
|
|
53
56
|
} = props;
|
|
57
|
+
const [current, setCurrent] = useControllableState({
|
|
58
|
+
value,
|
|
59
|
+
defaultValue: defaultValue ?? '',
|
|
60
|
+
onChange
|
|
61
|
+
});
|
|
54
62
|
const theme = useTheme();
|
|
55
63
|
const styles = useMemo(() => buildStyles(theme), [theme]);
|
|
56
64
|
const sizeStyles = {
|
|
@@ -81,8 +89,7 @@ const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
81
89
|
}, [length]);
|
|
82
90
|
const previousFocusedIndexRef = useRef(-1);
|
|
83
91
|
const shakeOnError = theme.components.otpInput?.shakeOnError ?? false;
|
|
84
|
-
const
|
|
85
|
-
const selectionHaptic = theme.components.otpInput?.selectionHaptic ?? false;
|
|
92
|
+
const reduceMotion = useReducedMotion();
|
|
86
93
|
const hasError = Boolean(error);
|
|
87
94
|
const errorMessage = typeof error === 'string' ? error : undefined;
|
|
88
95
|
|
|
@@ -90,11 +97,12 @@ const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
90
97
|
// error is initially true — there's no transition to animate.
|
|
91
98
|
useEffect(() => {
|
|
92
99
|
const isFirstRun = previousErrorRef.current === null;
|
|
100
|
+
let anim;
|
|
93
101
|
if (!isFirstRun && hasError && !previousErrorRef.current) {
|
|
94
|
-
if (
|
|
95
|
-
if (shakeOnError) {
|
|
102
|
+
if (haptic !== false) triggerHaptic('notificationError');
|
|
103
|
+
if (shakeOnError && !reduceMotion) {
|
|
96
104
|
setNativeValue(shake, 0);
|
|
97
|
-
Animated.sequence([Animated.timing(shake, {
|
|
105
|
+
anim = Animated.sequence([Animated.timing(shake, {
|
|
98
106
|
toValue: 1,
|
|
99
107
|
duration: 75,
|
|
100
108
|
easing: Easing.linear,
|
|
@@ -114,11 +122,13 @@ const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
114
122
|
duration: 75,
|
|
115
123
|
easing: Easing.linear,
|
|
116
124
|
useNativeDriver: true
|
|
117
|
-
})])
|
|
125
|
+
})]);
|
|
126
|
+
anim.start();
|
|
118
127
|
}
|
|
119
128
|
}
|
|
120
129
|
previousErrorRef.current = hasError;
|
|
121
|
-
|
|
130
|
+
return () => anim?.stop();
|
|
131
|
+
}, [hasError, shake, shakeOnError, haptic]);
|
|
122
132
|
|
|
123
133
|
// Animate underline opacity for the focused cell. Skip on first mount
|
|
124
134
|
// (no prior focus state) — values are already at 0 and there's nothing to animate.
|
|
@@ -127,17 +137,21 @@ const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
127
137
|
previousFocusedIndexRef.current = focusedIndex;
|
|
128
138
|
if (previous === focusedIndex) return;
|
|
129
139
|
if (previous === -1 && focusedIndex === -1) return;
|
|
140
|
+
const anims = [];
|
|
130
141
|
underlines.forEach((anim, idx) => {
|
|
131
142
|
const target = idx === focusedIndex ? 1 : 0;
|
|
132
143
|
// Only animate the cells whose target changed.
|
|
133
144
|
if (idx !== focusedIndex && idx !== previous) return;
|
|
134
|
-
Animated.timing(anim, {
|
|
145
|
+
const animation = Animated.timing(anim, {
|
|
135
146
|
toValue: target,
|
|
136
147
|
duration: theme.motion.duration.fast,
|
|
137
148
|
easing: Easing.bezier(...theme.motion.easing.standard),
|
|
138
149
|
useNativeDriver: true
|
|
139
|
-
})
|
|
150
|
+
});
|
|
151
|
+
animation.start();
|
|
152
|
+
anims.push(animation);
|
|
140
153
|
});
|
|
154
|
+
return () => anims.forEach(a => a.stop());
|
|
141
155
|
}, [focusedIndex, underlines, theme]);
|
|
142
156
|
const focusCell = useCallback(index => {
|
|
143
157
|
const target = inputsRef.current[index];
|
|
@@ -152,10 +166,10 @@ const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
152
166
|
current?.blur();
|
|
153
167
|
},
|
|
154
168
|
clear: () => {
|
|
155
|
-
|
|
169
|
+
setCurrent('');
|
|
156
170
|
focusCell(0);
|
|
157
171
|
}
|
|
158
|
-
}), [focusCell, focusedIndex,
|
|
172
|
+
}), [focusCell, focusedIndex, setCurrent]);
|
|
159
173
|
useEffect(() => {
|
|
160
174
|
if (autoFocus && !disabled) {
|
|
161
175
|
// Defer one frame so the inputs are mounted.
|
|
@@ -166,19 +180,19 @@ const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
166
180
|
}, [autoFocus, disabled, focusCell]);
|
|
167
181
|
const cells = useMemo(() => {
|
|
168
182
|
const arr = new Array(length).fill('');
|
|
169
|
-
for (let i = 0; i < Math.min(
|
|
170
|
-
arr[i] =
|
|
183
|
+
for (let i = 0; i < Math.min(current.length, length); i += 1) {
|
|
184
|
+
arr[i] = current[i] ?? '';
|
|
171
185
|
}
|
|
172
186
|
return arr;
|
|
173
|
-
}, [
|
|
187
|
+
}, [current, length]);
|
|
174
188
|
const updateValue = useCallback(next => {
|
|
175
189
|
const trimmed = next.slice(0, length);
|
|
176
|
-
if (trimmed ===
|
|
177
|
-
|
|
190
|
+
if (trimmed === current) return;
|
|
191
|
+
setCurrent(trimmed);
|
|
178
192
|
if (trimmed.length === length && onComplete) {
|
|
179
193
|
onComplete(trimmed);
|
|
180
194
|
}
|
|
181
|
-
}, [length,
|
|
195
|
+
}, [length, setCurrent, onComplete, current]);
|
|
182
196
|
const handleChangeText = useCallback((index, raw) => {
|
|
183
197
|
// Strip the ZWSP placeholder (used so iOS fires onKeyPress/Backspace on otherwise-empty cells).
|
|
184
198
|
const stripped = raw.replace(/\u200B/g, '');
|
|
@@ -208,10 +222,11 @@ const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
208
222
|
writeIndex += 1;
|
|
209
223
|
}
|
|
210
224
|
const next = chars.join('').slice(0, length);
|
|
211
|
-
const previousLength =
|
|
225
|
+
const previousLength = current.length;
|
|
212
226
|
updateValue(next);
|
|
213
|
-
if (next.length !== previousLength
|
|
214
|
-
|
|
227
|
+
if (next.length !== previousLength) {
|
|
228
|
+
const h = resolveHaptic(haptic, 'selection');
|
|
229
|
+
if (h) triggerHaptic(h);
|
|
215
230
|
}
|
|
216
231
|
|
|
217
232
|
// Move focus to the next empty cell or last cell.
|
|
@@ -221,7 +236,7 @@ const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
221
236
|
} else {
|
|
222
237
|
focusCell(nextFocus);
|
|
223
238
|
}
|
|
224
|
-
}, [cells, focusCell, keyboardType, length,
|
|
239
|
+
}, [cells, focusCell, keyboardType, length, haptic, updateValue, current]);
|
|
225
240
|
const handleKeyPress = useCallback((index, e) => {
|
|
226
241
|
const key = e.nativeEvent.key;
|
|
227
242
|
if (key !== 'Backspace') return;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import React, { createContext, forwardRef, useContext, useEffect, useMemo, useRef } from 'react';
|
|
4
4
|
import { Animated, 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 { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
8
|
export const RadioGroupContext = /*#__PURE__*/createContext(null);
|
|
9
9
|
export const useRadioGroup = () => useContext(RadioGroupContext);
|
|
@@ -38,7 +38,7 @@ const Radio = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
38
38
|
const {
|
|
39
39
|
value,
|
|
40
40
|
selected: selectedProp,
|
|
41
|
-
|
|
41
|
+
onChange,
|
|
42
42
|
disabled: disabledProp = false,
|
|
43
43
|
label,
|
|
44
44
|
size = 'md',
|
|
@@ -64,17 +64,17 @@ const Radio = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
64
64
|
const inner = sizeOverrides?.inner ?? sizeMap[size].inner;
|
|
65
65
|
const radioBorderWidth = theme.colors.border.width;
|
|
66
66
|
const radioLabelGap = theme.components.radio?.labelGap ?? 10;
|
|
67
|
-
const pressHapticEnabled = theme.components.radio?.pressHaptic ?? false;
|
|
68
|
-
const resolvedHaptic = haptic === undefined ? pressHapticEnabled ? 'selection' : false : haptic;
|
|
69
67
|
const progress = useRef(createAnimatedValue(selected ? 1 : 0)).current;
|
|
70
68
|
useEffect(() => {
|
|
71
|
-
Animated.spring(progress, {
|
|
69
|
+
const anim = Animated.spring(progress, {
|
|
72
70
|
toValue: selected ? 1 : 0,
|
|
73
71
|
damping: theme.motion.spring.snappy.damping,
|
|
74
72
|
stiffness: theme.motion.spring.snappy.stiffness,
|
|
75
73
|
mass: theme.motion.spring.snappy.mass,
|
|
76
74
|
useNativeDriver: true
|
|
77
|
-
})
|
|
75
|
+
});
|
|
76
|
+
anim.start();
|
|
77
|
+
return () => anim.stop();
|
|
78
78
|
}, [selected, progress, theme.motion.spring.snappy]);
|
|
79
79
|
const scale = progress.interpolate({
|
|
80
80
|
inputRange: [0, 1],
|
|
@@ -82,11 +82,12 @@ const Radio = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
82
82
|
});
|
|
83
83
|
const handlePress = event => {
|
|
84
84
|
if (disabled) return;
|
|
85
|
-
|
|
85
|
+
const h = resolveHaptic(haptic, 'selection');
|
|
86
|
+
if (h) triggerHaptic(h);
|
|
86
87
|
if (ctx) {
|
|
87
|
-
ctx.
|
|
88
|
+
ctx.onChange(value);
|
|
88
89
|
} else {
|
|
89
|
-
|
|
90
|
+
onChange?.(value);
|
|
90
91
|
}
|
|
91
92
|
rest.onPressOut?.(event);
|
|
92
93
|
};
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
import React, { useMemo } from 'react';
|
|
4
4
|
import { View } from 'react-native';
|
|
5
5
|
import { RadioGroupContext } from "./Radio.js";
|
|
6
|
+
import { useControllableState } from "../../hooks/index.js";
|
|
6
7
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
7
8
|
const RadioGroup = ({
|
|
8
9
|
value,
|
|
10
|
+
defaultValue,
|
|
9
11
|
onChange,
|
|
10
12
|
children,
|
|
11
13
|
disabled = false,
|
|
@@ -13,11 +15,16 @@ const RadioGroup = ({
|
|
|
13
15
|
style,
|
|
14
16
|
testID
|
|
15
17
|
}) => {
|
|
18
|
+
const [current, setCurrent] = useControllableState({
|
|
19
|
+
value,
|
|
20
|
+
defaultValue: defaultValue ?? undefined,
|
|
21
|
+
onChange: onChange
|
|
22
|
+
});
|
|
16
23
|
const ctx = useMemo(() => ({
|
|
17
|
-
selectedValue:
|
|
18
|
-
|
|
24
|
+
selectedValue: current,
|
|
25
|
+
onChange: setCurrent,
|
|
19
26
|
disabled
|
|
20
|
-
}), [
|
|
27
|
+
}), [current, setCurrent, disabled]);
|
|
21
28
|
return /*#__PURE__*/_jsx(RadioGroupContext.Provider, {
|
|
22
29
|
value: ctx,
|
|
23
30
|
children: /*#__PURE__*/_jsx(View, {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import React, { forwardRef, useCallback, useMemo, useRef } from 'react';
|
|
4
4
|
import { Animated, Easing, Pressable, StyleSheet, Text, View } from 'react-native';
|
|
5
5
|
import { useTheme } from "../../theme/index.js";
|
|
6
|
-
import { triggerHaptic } from "../../utils/hapticUtils.js";
|
|
6
|
+
import { resolveHaptic, triggerHaptic } 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 sizePxMap = {
|
|
@@ -80,6 +80,7 @@ const Rating = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
80
80
|
tone = 'warning',
|
|
81
81
|
label,
|
|
82
82
|
loading = false,
|
|
83
|
+
haptic,
|
|
83
84
|
accessibilityLabel,
|
|
84
85
|
style,
|
|
85
86
|
containerStyle,
|
|
@@ -116,7 +117,6 @@ const Rating = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
116
117
|
}, [max]);
|
|
117
118
|
const pulseOnPress = theme.components.rating?.pulseOnPress ?? false;
|
|
118
119
|
const pulseScale = theme.components.rating?.pulseScale ?? 1.2;
|
|
119
|
-
const pressHapticEnabled = theme.components.rating?.pressHaptic ?? false;
|
|
120
120
|
const pulse = useCallback(idx => {
|
|
121
121
|
const v = scaleRefs[idx];
|
|
122
122
|
if (!v) return;
|
|
@@ -136,10 +136,11 @@ const Rating = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
136
136
|
const commit = useCallback((next, animatedIndex) => {
|
|
137
137
|
if (!interactive || !onChange) return;
|
|
138
138
|
const cleaned = clampToStep(next, max, step);
|
|
139
|
-
|
|
139
|
+
const h = resolveHaptic(haptic, 'selection');
|
|
140
|
+
if (h) triggerHaptic(h);
|
|
140
141
|
if (pulseOnPress && typeof animatedIndex === 'number') pulse(animatedIndex);
|
|
141
142
|
onChange(cleaned);
|
|
142
|
-
}, [interactive, onChange, max, step, pulse,
|
|
143
|
+
}, [interactive, onChange, max, step, pulse, haptic, pulseOnPress]);
|
|
143
144
|
const handleStarPress = useCallback(index => e => {
|
|
144
145
|
if (!interactive) return;
|
|
145
146
|
const w = starWidthRef.current || starSize;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { Animated, Easing, Pressable, StyleSheet, Text, TextInput, View } from 'react-native';
|
|
5
5
|
import { fontFor, useTheme, createAnimatedValue } from "../../theme/index.js";
|
|
6
|
-
import { triggerHaptic } from "../../utils/hapticUtils.js";
|
|
6
|
+
import { resolveHaptic, triggerHaptic } from "../../utils/hapticUtils.js";
|
|
7
7
|
import { useDebounce } from "../../hooks/useDebounce.js";
|
|
8
8
|
import { FieldBase, resolveFieldSize, resolveFieldTextStyle, resolveVariantColors } from "../FieldBase/FieldBase.js";
|
|
9
9
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
@@ -24,6 +24,7 @@ const SearchBar = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
24
24
|
size = 'md',
|
|
25
25
|
variant: variantProp,
|
|
26
26
|
cancelLabel = 'Cancel',
|
|
27
|
+
haptic,
|
|
27
28
|
style,
|
|
28
29
|
accessibilityLabel,
|
|
29
30
|
testID,
|
|
@@ -75,12 +76,14 @@ const SearchBar = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
75
76
|
}, [debouncedValue, debounceMs, onChangeText]);
|
|
76
77
|
const showCancelButton = showCancel && isFocused;
|
|
77
78
|
useEffect(() => {
|
|
78
|
-
Animated.timing(cancelAnim, {
|
|
79
|
+
const anim = Animated.timing(cancelAnim, {
|
|
79
80
|
toValue: showCancelButton ? 1 : 0,
|
|
80
81
|
duration: 200,
|
|
81
82
|
easing: Easing.bezier(...theme.motion.easing.standard),
|
|
82
83
|
useNativeDriver: false
|
|
83
|
-
})
|
|
84
|
+
});
|
|
85
|
+
anim.start();
|
|
86
|
+
return () => anim.stop();
|
|
84
87
|
}, [showCancelButton, cancelAnim, theme.motion.easing.standard]);
|
|
85
88
|
const handleChangeText = useCallback(text => {
|
|
86
89
|
if (debounceMs !== undefined) {
|
|
@@ -90,16 +93,18 @@ const SearchBar = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
90
93
|
onChangeText(text);
|
|
91
94
|
}, [debounceMs, onChangeText]);
|
|
92
95
|
const handleClear = useCallback(() => {
|
|
93
|
-
|
|
96
|
+
const h = resolveHaptic(haptic, 'selection');
|
|
97
|
+
if (h) triggerHaptic(h);
|
|
94
98
|
if (debounceMs !== undefined) {
|
|
95
99
|
setInternalValue('');
|
|
96
100
|
lastSentRef.current = '';
|
|
97
101
|
}
|
|
98
102
|
onChangeText('');
|
|
99
103
|
onClear?.();
|
|
100
|
-
}, [debounceMs, onChangeText, onClear]);
|
|
104
|
+
}, [haptic, debounceMs, onChangeText, onClear]);
|
|
101
105
|
const handleCancel = useCallback(() => {
|
|
102
|
-
|
|
106
|
+
const h = resolveHaptic(haptic, 'selection');
|
|
107
|
+
if (h) triggerHaptic(h);
|
|
103
108
|
if (debounceMs !== undefined) {
|
|
104
109
|
setInternalValue('');
|
|
105
110
|
lastSentRef.current = '';
|
|
@@ -107,7 +112,7 @@ const SearchBar = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
107
112
|
onChangeText('');
|
|
108
113
|
setIsFocused(false);
|
|
109
114
|
onCancel?.();
|
|
110
|
-
}, [debounceMs, onChangeText, onCancel]);
|
|
115
|
+
}, [haptic, debounceMs, onChangeText, onCancel]);
|
|
111
116
|
const handleSubmit = useCallback(() => {
|
|
112
117
|
onSubmit?.(debounceMs !== undefined ? internalValue : value);
|
|
113
118
|
}, [onSubmit, debounceMs, internalValue, value]);
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
12
12
|
import { Animated, Pressable, StyleSheet, Text, View } from 'react-native';
|
|
13
13
|
import { useTheme, createAnimatedValue } from "../../theme/index.js";
|
|
14
|
-
import { triggerHaptic } from "../../utils/index.js";
|
|
14
|
+
import { triggerHaptic, resolveHaptic } from "../../utils/index.js";
|
|
15
|
+
import { useControllableState } from "../../hooks/index.js";
|
|
15
16
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
16
17
|
const sizeMap = {
|
|
17
18
|
sm: {
|
|
@@ -29,12 +30,14 @@ const TRACK_PADDING = 2;
|
|
|
29
30
|
const SegmentedControl = /*#__PURE__*/forwardRef((props, ref) => {
|
|
30
31
|
const {
|
|
31
32
|
segments,
|
|
32
|
-
value,
|
|
33
|
+
value: valueProp,
|
|
34
|
+
defaultValue,
|
|
33
35
|
onChange,
|
|
34
36
|
size = 'md',
|
|
35
37
|
fullWidth = true,
|
|
36
38
|
disabled = false,
|
|
37
39
|
tone = 'primary',
|
|
40
|
+
haptic,
|
|
38
41
|
accessibilityLabel,
|
|
39
42
|
style,
|
|
40
43
|
containerStyle,
|
|
@@ -44,6 +47,11 @@ const SegmentedControl = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
44
47
|
labelStyle,
|
|
45
48
|
testID
|
|
46
49
|
} = props;
|
|
50
|
+
const [current, setCurrent] = useControllableState({
|
|
51
|
+
value: valueProp,
|
|
52
|
+
defaultValue: defaultValue ?? segments[0]?.value,
|
|
53
|
+
onChange
|
|
54
|
+
});
|
|
47
55
|
const theme = useTheme();
|
|
48
56
|
const segTheme = theme.components.segmentedControl;
|
|
49
57
|
const styles = useMemo(() => buildStyles(theme), [theme]);
|
|
@@ -54,7 +62,6 @@ const SegmentedControl = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
54
62
|
paddingHorizontal: segTheme?.[size]?.paddingHorizontal ?? baseSize.paddingHorizontal
|
|
55
63
|
};
|
|
56
64
|
const trackPadding = segTheme?.trackPadding ?? TRACK_PADDING;
|
|
57
|
-
const changeHapticEnabled = segTheme?.changeHaptic ?? false;
|
|
58
65
|
|
|
59
66
|
// Track width is measured from onLayout. Thumb width is a regular number (not
|
|
60
67
|
// animated) — `width` cannot be driven by the native animated module, and mixing
|
|
@@ -64,23 +71,26 @@ const SegmentedControl = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
64
71
|
// only animate translateX via the native driver.
|
|
65
72
|
const [trackWidth, setTrackWidth] = useState(0);
|
|
66
73
|
const thumbTranslateX = useRef(createAnimatedValue(0)).current;
|
|
67
|
-
const activeIndex = Math.max(0, segments.findIndex(s => s.value ===
|
|
74
|
+
const activeIndex = Math.max(0, segments.findIndex(s => s.value === current));
|
|
68
75
|
const segmentWidth = trackWidth > 0 ? (trackWidth - trackPadding * 2) / Math.max(segments.length, 1) : 0;
|
|
69
76
|
const animateThumb = useCallback((index, segWidth) => {
|
|
70
|
-
if (segWidth <= 0) return;
|
|
77
|
+
if (segWidth <= 0) return undefined;
|
|
71
78
|
const targetX = trackPadding + segWidth * index;
|
|
72
79
|
const spring = theme.motion.spring.snappy;
|
|
73
|
-
Animated.spring(thumbTranslateX, {
|
|
80
|
+
const anim = Animated.spring(thumbTranslateX, {
|
|
74
81
|
toValue: targetX,
|
|
75
82
|
damping: spring.damping,
|
|
76
83
|
stiffness: spring.stiffness,
|
|
77
84
|
mass: spring.mass,
|
|
78
85
|
useNativeDriver: true
|
|
79
|
-
})
|
|
86
|
+
});
|
|
87
|
+
anim.start();
|
|
88
|
+
return anim;
|
|
80
89
|
}, [theme.motion.spring.snappy, thumbTranslateX]);
|
|
81
90
|
useEffect(() => {
|
|
82
91
|
if (segmentWidth > 0) {
|
|
83
|
-
animateThumb(activeIndex, segmentWidth);
|
|
92
|
+
const anim = animateThumb(activeIndex, segmentWidth);
|
|
93
|
+
return () => anim?.stop();
|
|
84
94
|
}
|
|
85
95
|
}, [activeIndex, segmentWidth, animateThumb]);
|
|
86
96
|
const handleTrackLayout = useCallback(e => {
|
|
@@ -91,10 +101,11 @@ const SegmentedControl = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
91
101
|
}, [trackWidth]);
|
|
92
102
|
const handlePress = useCallback(segment => {
|
|
93
103
|
if (disabled) return;
|
|
94
|
-
if (segment.value ===
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
104
|
+
if (segment.value === current) return;
|
|
105
|
+
const h = resolveHaptic(haptic, 'selection');
|
|
106
|
+
if (h) triggerHaptic(h);
|
|
107
|
+
setCurrent(segment.value);
|
|
108
|
+
}, [disabled, setCurrent, current, haptic]);
|
|
98
109
|
const thumbBg = tone === 'primary' ? theme.colors.background.elevated : theme.colors.background.elevated;
|
|
99
110
|
const activeTextColor = tone === 'primary' ? theme.colors.text.primary : theme.colors.text.primary;
|
|
100
111
|
return /*#__PURE__*/_jsxs(View, {
|
|
@@ -125,7 +136,7 @@ const SegmentedControl = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
125
136
|
...theme.shadows.sm
|
|
126
137
|
}, selectedIndicatorStyle]
|
|
127
138
|
}) : null, segments.map(segment => {
|
|
128
|
-
const isActive = segment.value ===
|
|
139
|
+
const isActive = segment.value === current;
|
|
129
140
|
return /*#__PURE__*/_jsxs(Pressable, {
|
|
130
141
|
onPress: () => handlePress(segment),
|
|
131
142
|
disabled: disabled,
|
|
@@ -12,7 +12,7 @@ import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState }
|
|
|
12
12
|
import { Animated, Dimensions, Easing, FlatList, Modal, Pressable, StyleSheet, Text, TextInput, View } from 'react-native';
|
|
13
13
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
14
14
|
import { useTheme, createAnimatedValue } from "../../theme/index.js";
|
|
15
|
-
import { triggerHaptic } from "../../utils/index.js";
|
|
15
|
+
import { resolveHaptic, triggerHaptic } from "../../utils/index.js";
|
|
16
16
|
import { FieldBase, resolveFieldSize, resolveFieldTextStyle, resolveVariantColors } from "../FieldBase/FieldBase.js";
|
|
17
17
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
18
18
|
// `unknown` here keeps the forwardRef signature non-generic — consumers who
|
|
@@ -27,6 +27,7 @@ const Select = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
27
27
|
getOptionDescription,
|
|
28
28
|
placeholder,
|
|
29
29
|
searchable = false,
|
|
30
|
+
haptic,
|
|
30
31
|
label,
|
|
31
32
|
error,
|
|
32
33
|
disabled = false,
|
|
@@ -61,34 +62,32 @@ const Select = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
61
62
|
const backdropAnim = useRef(createAnimatedValue(0)).current;
|
|
62
63
|
const sheetAnim = useRef(createAnimatedValue(sheetMaxHeight)).current;
|
|
63
64
|
useEffect(() => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
})]).start();
|
|
91
|
-
}
|
|
65
|
+
const anim = open ? Animated.parallel([
|
|
66
|
+
// Backdrop opacity uses JS driver — see Modal.tsx for the Fabric reason.
|
|
67
|
+
Animated.timing(backdropAnim, {
|
|
68
|
+
toValue: 1,
|
|
69
|
+
duration: theme.motion.duration.fast,
|
|
70
|
+
easing: Easing.bezier(...theme.motion.easing.standard),
|
|
71
|
+
useNativeDriver: false
|
|
72
|
+
}), Animated.spring(sheetAnim, {
|
|
73
|
+
toValue: 0,
|
|
74
|
+
damping: theme.motion.spring.snappy.damping,
|
|
75
|
+
stiffness: theme.motion.spring.snappy.stiffness,
|
|
76
|
+
mass: theme.motion.spring.snappy.mass,
|
|
77
|
+
useNativeDriver: true
|
|
78
|
+
})]) : Animated.parallel([Animated.timing(backdropAnim, {
|
|
79
|
+
toValue: 0,
|
|
80
|
+
duration: theme.motion.duration.fast,
|
|
81
|
+
easing: Easing.bezier(...theme.motion.easing.standard),
|
|
82
|
+
useNativeDriver: false
|
|
83
|
+
}), Animated.timing(sheetAnim, {
|
|
84
|
+
toValue: sheetMaxHeight,
|
|
85
|
+
duration: theme.motion.duration.fast,
|
|
86
|
+
easing: Easing.bezier(...theme.motion.easing.accelerate),
|
|
87
|
+
useNativeDriver: true
|
|
88
|
+
})]);
|
|
89
|
+
anim.start();
|
|
90
|
+
return () => anim.stop();
|
|
92
91
|
}, [open, backdropAnim, sheetAnim, sheetMaxHeight, theme.motion.duration.fast, theme.motion.easing.standard, theme.motion.easing.accelerate, theme.motion.spring.snappy]);
|
|
93
92
|
|
|
94
93
|
// Selection helpers — typed to keep callbacks fully type-safe.
|
|
@@ -116,9 +115,10 @@ const Select = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
116
115
|
}, [searchable, query, options, labelOf, descriptionOf]);
|
|
117
116
|
const handleOpen = useCallback(() => {
|
|
118
117
|
if (disabled) return;
|
|
119
|
-
|
|
118
|
+
const h = resolveHaptic(haptic, 'selection');
|
|
119
|
+
if (h) triggerHaptic(h);
|
|
120
120
|
setOpen(true);
|
|
121
|
-
}, [disabled]);
|
|
121
|
+
}, [disabled, haptic]);
|
|
122
122
|
const handleClose = useCallback(() => {
|
|
123
123
|
setOpen(false);
|
|
124
124
|
setQuery('');
|
|
@@ -126,7 +126,8 @@ const Select = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
126
126
|
const handleSelect = useCallback(option => {
|
|
127
127
|
if (disabledOf(option)) return;
|
|
128
128
|
const optValue = valueOf(option);
|
|
129
|
-
|
|
129
|
+
const h = resolveHaptic(haptic, 'selection');
|
|
130
|
+
if (h) triggerHaptic(h);
|
|
130
131
|
if (multi) {
|
|
131
132
|
const current = props.value ?? [];
|
|
132
133
|
const next = current.includes(optValue) ? current.filter(v => v !== optValue) : [...current, optValue];
|
|
@@ -135,7 +136,7 @@ const Select = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
135
136
|
}
|
|
136
137
|
props.onChange(optValue);
|
|
137
138
|
handleClose();
|
|
138
|
-
}, [multi, props, handleClose, disabledOf, valueOf]);
|
|
139
|
+
}, [multi, props, handleClose, disabledOf, valueOf, haptic]);
|
|
139
140
|
|
|
140
141
|
// Trigger label / placeholder text.
|
|
141
142
|
const triggerText = selectedOptions.length === 0 ? placeholder ?? 'Select…' : selectedOptions.map(o => labelOf(o)).join(', ');
|
|
@@ -386,14 +387,17 @@ const Chevron = ({
|
|
|
386
387
|
size,
|
|
387
388
|
open
|
|
388
389
|
}) => {
|
|
390
|
+
const theme = useTheme();
|
|
389
391
|
const rotate = useRef(createAnimatedValue(open ? 1 : 0)).current;
|
|
390
392
|
useEffect(() => {
|
|
391
|
-
Animated.timing(rotate, {
|
|
393
|
+
const anim = Animated.timing(rotate, {
|
|
392
394
|
toValue: open ? 1 : 0,
|
|
393
|
-
duration:
|
|
395
|
+
duration: theme.motion.duration.fast,
|
|
394
396
|
useNativeDriver: true
|
|
395
|
-
})
|
|
396
|
-
|
|
397
|
+
});
|
|
398
|
+
anim.start();
|
|
399
|
+
return () => anim.stop();
|
|
400
|
+
}, [open, rotate, theme.motion.duration.fast]);
|
|
397
401
|
const rotation = rotate.interpolate({
|
|
398
402
|
inputRange: [0, 1],
|
|
399
403
|
outputRange: ['0deg', '180deg']
|
|
@@ -216,19 +216,22 @@ const SkeletonContent = ({
|
|
|
216
216
|
const wasLoadingRef = useRef(loading);
|
|
217
217
|
const fadeAnim = useRef(createAnimatedValue(loading ? 0 : 1)).current;
|
|
218
218
|
useEffect(() => {
|
|
219
|
+
let anim;
|
|
219
220
|
if (wasLoadingRef.current && !loading) {
|
|
220
221
|
fadeAnim.setValue(0);
|
|
221
|
-
Animated.timing(fadeAnim, {
|
|
222
|
+
anim = Animated.timing(fadeAnim, {
|
|
222
223
|
toValue: 1,
|
|
223
224
|
duration: resolvedFadeDuration,
|
|
224
225
|
useNativeDriver: true
|
|
225
|
-
})
|
|
226
|
+
});
|
|
227
|
+
anim.start();
|
|
226
228
|
} else if (!wasLoadingRef.current && loading) {
|
|
227
229
|
// Going back into loading from loaded — snap opacity so the
|
|
228
230
|
// placeholder appears instantly when data invalidates.
|
|
229
231
|
fadeAnim.setValue(1);
|
|
230
232
|
}
|
|
231
233
|
wasLoadingRef.current = loading;
|
|
234
|
+
return () => anim?.stop();
|
|
232
235
|
}, [loading, fadeAnim, resolvedFadeDuration]);
|
|
233
236
|
if (!loading) {
|
|
234
237
|
if (resolvedFadeDuration <= 0) {
|