@webority-technologies/mobile 0.0.14 → 0.0.20
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 +85 -50
- 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 +306 -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 +84 -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 +87 -52
- 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 +297 -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 +84 -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 +141 -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 +6 -0
- 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 +553 -11
- 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 +141 -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 +6 -0
- 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 +553 -11
- 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
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
import React, { forwardRef, useCallback, useEffect, useMemo, useRef } from 'react';
|
|
4
4
|
import { AccessibilityInfo, Animated, Dimensions, findNodeHandle, Modal as RNModal, Pressable, StyleSheet, View } from 'react-native';
|
|
5
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
5
6
|
import { useTheme, createAnimatedValue, setNativeValue } from "../../theme/index.js";
|
|
6
7
|
import { triggerHaptic } from "../../utils/hapticUtils.js";
|
|
8
|
+
|
|
9
|
+
// Local shape mirror — see types.ts ModalTokens for the canonical definition.
|
|
7
10
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
11
|
const Modal = /*#__PURE__*/forwardRef((props, ref) => {
|
|
9
12
|
const {
|
|
@@ -22,10 +25,14 @@ const Modal = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
22
25
|
testID
|
|
23
26
|
} = props;
|
|
24
27
|
const theme = useTheme();
|
|
28
|
+
const insets = useSafeAreaInsets();
|
|
25
29
|
const duration = animationDuration ?? theme.motion.duration.normal;
|
|
26
30
|
const screenHeight = Dimensions.get('window').height;
|
|
31
|
+
const modalTokens = theme.components.modal;
|
|
32
|
+
const scaleStartValue = modalTokens?.scaleStartValue ?? 0.9;
|
|
33
|
+
const backdropHaptic = modalTokens?.backdropHaptic ?? false;
|
|
27
34
|
const backdropAnim = useRef(createAnimatedValue(0)).current;
|
|
28
|
-
const scaleAnim = useRef(createAnimatedValue(
|
|
35
|
+
const scaleAnim = useRef(createAnimatedValue(scaleStartValue)).current;
|
|
29
36
|
const opacityAnim = useRef(createAnimatedValue(0)).current;
|
|
30
37
|
const translateYAnim = useRef(createAnimatedValue(screenHeight)).current;
|
|
31
38
|
const containerRef = useRef(null);
|
|
@@ -33,10 +40,16 @@ const Modal = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
33
40
|
const styles = useMemo(() => buildStyles(theme), [theme]);
|
|
34
41
|
useEffect(() => {
|
|
35
42
|
if (visible) {
|
|
43
|
+
// Two Fabric quirks force this combination: backdrop View needs
|
|
44
|
+
// `collapsable={false}` (see render) so flattening doesn't drop it,
|
|
45
|
+
// AND opacity must run on the JS driver — native-driver opacity on an
|
|
46
|
+
// Animated.View that also sets backgroundColor inline renders as 0 on
|
|
47
|
+
// RN 0.85 / Fabric / iOS, leaving the dim layer invisible. Single
|
|
48
|
+
// 240ms tween — JS thread cost is negligible.
|
|
36
49
|
Animated.timing(backdropAnim, {
|
|
37
50
|
toValue: 1,
|
|
38
51
|
duration,
|
|
39
|
-
useNativeDriver:
|
|
52
|
+
useNativeDriver: false
|
|
40
53
|
}).start();
|
|
41
54
|
if (presentation === 'bottom') {
|
|
42
55
|
Animated.spring(translateYAnim, {
|
|
@@ -60,12 +73,14 @@ const Modal = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
60
73
|
})]).start();
|
|
61
74
|
}
|
|
62
75
|
} else {
|
|
63
|
-
|
|
64
|
-
|
|
76
|
+
// backdropAnim is JS-driven (see note above) — reset directly so a
|
|
77
|
+
// zero-duration native timing doesn't re-claim the value.
|
|
78
|
+
backdropAnim.setValue(0);
|
|
79
|
+
setNativeValue(scaleAnim, scaleStartValue);
|
|
65
80
|
setNativeValue(opacityAnim, 0);
|
|
66
81
|
setNativeValue(translateYAnim, screenHeight);
|
|
67
82
|
}
|
|
68
|
-
}, [visible, presentation, duration, backdropAnim, scaleAnim, opacityAnim, translateYAnim, screenHeight, theme.motion.spring.gentle.damping, theme.motion.spring.gentle.stiffness, theme.motion.spring.gentle.mass]);
|
|
83
|
+
}, [visible, presentation, duration, backdropAnim, scaleAnim, opacityAnim, translateYAnim, screenHeight, theme.motion.spring.gentle.damping, theme.motion.spring.gentle.stiffness, theme.motion.spring.gentle.mass, scaleStartValue]);
|
|
69
84
|
|
|
70
85
|
// Accessibility focus trap: when the modal opens, push screen-reader focus
|
|
71
86
|
// into the dialog container after the mount animation; when it closes,
|
|
@@ -100,9 +115,9 @@ const Modal = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
100
115
|
}, [visible, duration, restoreFocusRef]);
|
|
101
116
|
const handleBackdropPress = useCallback(() => {
|
|
102
117
|
if (!backdropPressClose) return;
|
|
103
|
-
triggerHaptic('selection');
|
|
118
|
+
if (backdropHaptic) triggerHaptic('selection');
|
|
104
119
|
onRequestClose();
|
|
105
|
-
}, [backdropPressClose, onRequestClose]);
|
|
120
|
+
}, [backdropPressClose, onRequestClose, backdropHaptic]);
|
|
106
121
|
const handleHardwareBack = useCallback(() => {
|
|
107
122
|
if (hardwareBackPress) {
|
|
108
123
|
onRequestClose();
|
|
@@ -115,11 +130,15 @@ const Modal = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
115
130
|
}];
|
|
116
131
|
}
|
|
117
132
|
if (presentation === 'bottom') {
|
|
133
|
+
// paddingBottom includes the Android navigation-bar / iOS home-indicator
|
|
134
|
+
// inset so sheet content never lands behind the system chrome.
|
|
118
135
|
return [styles.bottomContent, {
|
|
119
136
|
backgroundColor: theme.colors.background.elevated,
|
|
120
137
|
borderTopLeftRadius: theme.radius.xl,
|
|
121
138
|
borderTopRightRadius: theme.radius.xl,
|
|
122
|
-
|
|
139
|
+
paddingTop: theme.spacing.lg,
|
|
140
|
+
paddingHorizontal: theme.spacing.lg,
|
|
141
|
+
paddingBottom: theme.spacing.lg + insets.bottom
|
|
123
142
|
}, theme.shadows.xl];
|
|
124
143
|
}
|
|
125
144
|
return [styles.centeredContent, {
|
|
@@ -127,7 +146,7 @@ const Modal = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
127
146
|
borderRadius: theme.radius.xl,
|
|
128
147
|
padding: theme.spacing.xl
|
|
129
148
|
}, theme.shadows.xl];
|
|
130
|
-
}, [presentation, theme, styles]);
|
|
149
|
+
}, [presentation, theme, styles, insets.bottom]);
|
|
131
150
|
const wrapperStyle = useMemo(() => {
|
|
132
151
|
if (presentation === 'bottom') return styles.bottomWrapper;
|
|
133
152
|
if (presentation === 'fullScreen') return styles.fullScreenWrapper;
|
|
@@ -156,11 +175,19 @@ const Modal = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
156
175
|
return /*#__PURE__*/_jsx(RNModal, {
|
|
157
176
|
visible: visible,
|
|
158
177
|
transparent: true,
|
|
159
|
-
statusBarTranslucent: true
|
|
178
|
+
statusBarTranslucent: true
|
|
179
|
+
// Android-only API (RN 0.71+). On iOS the prop is harmless / ignored.
|
|
180
|
+
// Lets the backdrop and sheet extend behind the system navigation bar
|
|
181
|
+
// so the dim layer covers the entire screen and gesture-nav devices
|
|
182
|
+
// don't show a gap under the sheet.
|
|
183
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
184
|
+
// @ts-ignore — typed in RN 0.73+, the runtime prop exists on 0.71+
|
|
185
|
+
,
|
|
186
|
+
navigationBarTranslucent: true,
|
|
160
187
|
animationType: "none",
|
|
161
188
|
onRequestClose: handleHardwareBack,
|
|
162
189
|
testID: testID,
|
|
163
|
-
children: /*#__PURE__*/
|
|
190
|
+
children: /*#__PURE__*/_jsx(View, {
|
|
164
191
|
ref: node => {
|
|
165
192
|
containerRef.current = node;
|
|
166
193
|
if (typeof ref === 'function') {
|
|
@@ -172,32 +199,33 @@ const Modal = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
172
199
|
style: styles.root,
|
|
173
200
|
accessible: true,
|
|
174
201
|
accessibilityViewIsModal: true,
|
|
175
|
-
accessibilityRole:
|
|
202
|
+
accessibilityRole: "alert",
|
|
176
203
|
accessibilityLabel: accessibilityLabel,
|
|
177
|
-
children:
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
204
|
+
children: /*#__PURE__*/_jsxs(View
|
|
205
|
+
// Backdrop wraps the dialog content (not sibling) — empirically,
|
|
206
|
+
// on RN 0.85 / Fabric / iOS a sibling backdrop is occluded by the
|
|
207
|
+
// wrapper View regardless of render order or pointerEvents.
|
|
208
|
+
// collapsable={false} prevents the backdrop View from being
|
|
209
|
+
// flattened out of the native tree. The backdrop uses a pre-baked
|
|
210
|
+
// rgba colour (no animated opacity) because Animated.View opacity
|
|
211
|
+
// on a coloured View doesn't update visibly on Fabric.
|
|
212
|
+
, {
|
|
213
|
+
collapsable: false,
|
|
214
|
+
style: [StyleSheet.absoluteFillObject, wrapperStyle, {
|
|
215
|
+
backgroundColor: theme.colors.background.overlay
|
|
184
216
|
}, backdropStyle],
|
|
185
|
-
children: /*#__PURE__*/_jsx(Pressable, {
|
|
217
|
+
children: [/*#__PURE__*/_jsx(Pressable, {
|
|
186
218
|
style: StyleSheet.absoluteFillObject,
|
|
187
219
|
onPress: handleBackdropPress,
|
|
188
220
|
accessibilityRole: "button",
|
|
189
221
|
accessibilityLabel: "Close modal",
|
|
190
222
|
disabled: !backdropPressClose
|
|
191
|
-
})
|
|
192
|
-
}), /*#__PURE__*/_jsx(View, {
|
|
193
|
-
style: wrapperStyle,
|
|
194
|
-
pointerEvents: "box-none",
|
|
195
|
-
children: /*#__PURE__*/_jsx(Animated.View, {
|
|
223
|
+
}), /*#__PURE__*/_jsx(Animated.View, {
|
|
196
224
|
style: [containerStyle, animatedTransform, contentStyle],
|
|
197
225
|
pointerEvents: "auto",
|
|
198
226
|
children: children
|
|
199
|
-
})
|
|
200
|
-
})
|
|
227
|
+
})]
|
|
228
|
+
})
|
|
201
229
|
})
|
|
202
230
|
});
|
|
203
231
|
});
|
|
@@ -4,41 +4,10 @@ import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState }
|
|
|
4
4
|
import { Pressable, StyleSheet, Text, TextInput, View } from 'react-native';
|
|
5
5
|
import { useTheme } from "../../theme/index.js";
|
|
6
6
|
import { triggerHaptic } from "../../utils/index.js";
|
|
7
|
+
import { FieldBase, resolveFieldSize, resolveFieldTextStyle } from "../FieldBase/FieldBase.js";
|
|
7
8
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
buttonSize: 30,
|
|
11
|
-
inputMinWidth: 44,
|
|
12
|
-
inputHeight: 30,
|
|
13
|
-
fontSize: 13,
|
|
14
|
-
iconFontSize: 16,
|
|
15
|
-
borderRadius: 8,
|
|
16
|
-
paddingHorizontal: 8,
|
|
17
|
-
gap: 6
|
|
18
|
-
},
|
|
19
|
-
md: {
|
|
20
|
-
buttonSize: 38,
|
|
21
|
-
inputMinWidth: 56,
|
|
22
|
-
inputHeight: 38,
|
|
23
|
-
fontSize: 15,
|
|
24
|
-
iconFontSize: 18,
|
|
25
|
-
borderRadius: 10,
|
|
26
|
-
paddingHorizontal: 10,
|
|
27
|
-
gap: 8
|
|
28
|
-
},
|
|
29
|
-
lg: {
|
|
30
|
-
buttonSize: 46,
|
|
31
|
-
inputMinWidth: 68,
|
|
32
|
-
inputHeight: 46,
|
|
33
|
-
fontSize: 17,
|
|
34
|
-
iconFontSize: 20,
|
|
35
|
-
borderRadius: 12,
|
|
36
|
-
paddingHorizontal: 12,
|
|
37
|
-
gap: 10
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
const LONG_PRESS_DELAY = 500;
|
|
41
|
-
const LONG_PRESS_INTERVAL = 80;
|
|
9
|
+
const DEFAULT_LONG_PRESS_DELAY = 500;
|
|
10
|
+
const DEFAULT_LONG_PRESS_INTERVAL = 80;
|
|
42
11
|
const roundToPrecision = (value, precision) => {
|
|
43
12
|
if (!Number.isFinite(value)) return 0;
|
|
44
13
|
if (precision <= 0) return Math.round(value);
|
|
@@ -60,12 +29,10 @@ const formatValue = (value, allowDecimal, precision) => {
|
|
|
60
29
|
const sanitizeTyped = (raw, allowDecimal) => {
|
|
61
30
|
if (!raw) return '';
|
|
62
31
|
let cleaned = raw.replace(/[^0-9.-]/g, '');
|
|
63
|
-
// Allow only one leading minus.
|
|
64
32
|
cleaned = cleaned.replace(/(?!^)-/g, '');
|
|
65
33
|
if (!allowDecimal) {
|
|
66
34
|
cleaned = cleaned.replace(/\./g, '');
|
|
67
35
|
} else {
|
|
68
|
-
// Allow at most one decimal point.
|
|
69
36
|
const firstDot = cleaned.indexOf('.');
|
|
70
37
|
if (firstDot !== -1) {
|
|
71
38
|
cleaned = cleaned.slice(0, firstDot + 1) + cleaned.slice(firstDot + 1).replace(/\./g, '');
|
|
@@ -91,17 +58,27 @@ const NumberInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
91
58
|
unit,
|
|
92
59
|
accessibilityLabel,
|
|
93
60
|
style,
|
|
61
|
+
containerStyle,
|
|
94
62
|
inputStyle,
|
|
63
|
+
buttonStyle,
|
|
64
|
+
unitStyle,
|
|
95
65
|
labelStyle,
|
|
96
66
|
textInputProps,
|
|
97
67
|
testID
|
|
98
68
|
} = props;
|
|
99
69
|
const theme = useTheme();
|
|
100
|
-
const
|
|
70
|
+
const sizeTokens = resolveFieldSize(theme, size);
|
|
71
|
+
const fieldText = resolveFieldTextStyle(theme, {
|
|
72
|
+
disabled
|
|
73
|
+
});
|
|
101
74
|
const styles = useMemo(() => buildStyles(theme), [theme]);
|
|
75
|
+
const longPressDelay = theme.components.numberInput?.longPressDelayMs ?? DEFAULT_LONG_PRESS_DELAY;
|
|
76
|
+
const longPressInterval = theme.components.numberInput?.longPressIntervalMs ?? DEFAULT_LONG_PRESS_INTERVAL;
|
|
77
|
+
const pressHaptic = theme.components.numberInput?.pressHaptic ?? false;
|
|
102
78
|
const inputRef = useRef(null);
|
|
103
79
|
const [draft, setDraft] = useState(formatValue(value, allowDecimal, precision));
|
|
104
80
|
const [editing, setEditing] = useState(false);
|
|
81
|
+
const [focused, setFocused] = useState(false);
|
|
105
82
|
const repeatTimeoutRef = useRef(null);
|
|
106
83
|
const repeatIntervalRef = useRef(null);
|
|
107
84
|
const clearRepeat = useCallback(() => {
|
|
@@ -115,8 +92,6 @@ const NumberInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
115
92
|
}
|
|
116
93
|
}, []);
|
|
117
94
|
useEffect(() => clearRepeat, [clearRepeat]);
|
|
118
|
-
|
|
119
|
-
// Keep draft in sync with value when not actively editing.
|
|
120
95
|
useEffect(() => {
|
|
121
96
|
if (!editing) {
|
|
122
97
|
setDraft(formatValue(value, allowDecimal, precision));
|
|
@@ -134,16 +109,14 @@ const NumberInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
134
109
|
}, [allowDecimal, max, min, onChange, precision, value]);
|
|
135
110
|
const decrement = useCallback(() => {
|
|
136
111
|
if (!interactive || atMin) return;
|
|
137
|
-
triggerHaptic('impactLight');
|
|
112
|
+
if (pressHaptic) triggerHaptic('impactLight');
|
|
138
113
|
setExternal(value - step);
|
|
139
|
-
}, [atMin, interactive, setExternal, step, value]);
|
|
114
|
+
}, [atMin, interactive, pressHaptic, setExternal, step, value]);
|
|
140
115
|
const increment = useCallback(() => {
|
|
141
116
|
if (!interactive || atMax) return;
|
|
142
|
-
triggerHaptic('impactLight');
|
|
117
|
+
if (pressHaptic) triggerHaptic('impactLight');
|
|
143
118
|
setExternal(value + step);
|
|
144
|
-
}, [atMax, interactive, setExternal, step, value]);
|
|
145
|
-
|
|
146
|
-
// Imperative ref.
|
|
119
|
+
}, [atMax, interactive, pressHaptic, setExternal, step, value]);
|
|
147
120
|
React.useImperativeHandle(ref, () => ({
|
|
148
121
|
focus: () => inputRef.current?.focus(),
|
|
149
122
|
blur: () => inputRef.current?.blur(),
|
|
@@ -162,23 +135,18 @@ const NumberInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
162
135
|
repeatTimeoutRef.current = setTimeout(() => {
|
|
163
136
|
repeatIntervalRef.current = setInterval(() => {
|
|
164
137
|
if (direction === 'inc') {
|
|
165
|
-
// Read latest via closure of state via setExternal.
|
|
166
|
-
// Compute next by reading current value from props on each tick.
|
|
167
|
-
// Since closures capture, we rely on state updating between intervals.
|
|
168
138
|
increment();
|
|
169
139
|
} else {
|
|
170
140
|
decrement();
|
|
171
141
|
}
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
}, [clearRepeat, decrement, increment, interactive]);
|
|
142
|
+
}, longPressInterval);
|
|
143
|
+
}, longPressDelay);
|
|
144
|
+
}, [clearRepeat, decrement, increment, interactive, longPressDelay, longPressInterval]);
|
|
175
145
|
const handleChangeText = useCallback(text => {
|
|
176
146
|
const cleaned = sanitizeTyped(text, allowDecimal);
|
|
177
147
|
setDraft(cleaned);
|
|
178
|
-
// Don't push numeric value while user types intermediate states (e.g., '-' or '1.').
|
|
179
148
|
const parsed = Number(cleaned);
|
|
180
149
|
if (cleaned !== '' && cleaned !== '-' && cleaned !== '.' && Number.isFinite(parsed)) {
|
|
181
|
-
// Only push if within bounds; otherwise wait for blur to clamp.
|
|
182
150
|
if (parsed >= min && parsed <= max) {
|
|
183
151
|
if (parsed !== value) {
|
|
184
152
|
onChange(roundToPrecision(parsed, allowDecimal ? precision : 0));
|
|
@@ -188,9 +156,9 @@ const NumberInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
188
156
|
}, [allowDecimal, max, min, onChange, precision, value]);
|
|
189
157
|
const handleBlur = useCallback(() => {
|
|
190
158
|
setEditing(false);
|
|
159
|
+
setFocused(false);
|
|
191
160
|
const parsed = Number(draft);
|
|
192
161
|
if (draft === '' || !Number.isFinite(parsed)) {
|
|
193
|
-
// Reset to the last known good value.
|
|
194
162
|
setDraft(formatValue(value, allowDecimal, precision));
|
|
195
163
|
return;
|
|
196
164
|
}
|
|
@@ -203,27 +171,16 @@ const NumberInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
203
171
|
}, [allowDecimal, draft, max, min, onChange, precision, value]);
|
|
204
172
|
const handleFocus = useCallback(() => {
|
|
205
173
|
setEditing(true);
|
|
174
|
+
setFocused(true);
|
|
206
175
|
}, []);
|
|
207
|
-
const minusBg = atMin || disabled ? theme.colors.surface.disabled : theme.colors.background.secondary;
|
|
208
|
-
const plusBg = atMax || disabled ? theme.colors.surface.disabled : theme.colors.background.secondary;
|
|
209
176
|
const minusFg = atMin || disabled ? theme.colors.text.disabled : theme.colors.text.primary;
|
|
210
177
|
const plusFg = atMax || disabled ? theme.colors.text.disabled : theme.colors.text.primary;
|
|
211
|
-
const
|
|
212
|
-
const
|
|
213
|
-
const buttonBaseStyle = {
|
|
214
|
-
width: sizeStyles.buttonSize,
|
|
215
|
-
height: sizeStyles.buttonSize,
|
|
216
|
-
borderRadius: sizeStyles.borderRadius,
|
|
217
|
-
borderWidth: 1,
|
|
218
|
-
borderColor,
|
|
219
|
-
alignItems: 'center',
|
|
220
|
-
justifyContent: 'center'
|
|
221
|
-
};
|
|
222
|
-
const renderButton = (type, onPress, disabledLocal, a11yLabel, bg, fg, glyph) => /*#__PURE__*/_jsx(Pressable, {
|
|
178
|
+
const stepperSize = sizeTokens.iconSize + 8;
|
|
179
|
+
const renderStepper = (type, onPress, disabledLocal, a11yLabel, fg, glyph) => /*#__PURE__*/_jsx(Pressable, {
|
|
223
180
|
onPress: onPress,
|
|
224
181
|
onLongPress: () => startRepeating(type),
|
|
225
182
|
onPressOut: clearRepeat,
|
|
226
|
-
delayLongPress:
|
|
183
|
+
delayLongPress: longPressDelay,
|
|
227
184
|
disabled: disabledLocal,
|
|
228
185
|
accessibilityRole: "button",
|
|
229
186
|
accessibilityLabel: a11yLabel,
|
|
@@ -232,36 +189,37 @@ const NumberInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
232
189
|
},
|
|
233
190
|
style: ({
|
|
234
191
|
pressed
|
|
235
|
-
}) => [
|
|
236
|
-
|
|
192
|
+
}) => [{
|
|
193
|
+
width: stepperSize,
|
|
194
|
+
height: stepperSize,
|
|
195
|
+
alignItems: 'center',
|
|
196
|
+
justifyContent: 'center',
|
|
197
|
+
borderRadius: stepperSize / 2
|
|
237
198
|
}, pressed && !disabledLocal ? {
|
|
238
199
|
backgroundColor: theme.colors.surface.pressed
|
|
239
|
-
} : null],
|
|
200
|
+
} : null, buttonStyle],
|
|
240
201
|
testID: `${testID ?? 'numberinput'}-${type === 'inc' ? 'plus' : 'minus'}`,
|
|
241
202
|
children: /*#__PURE__*/_jsx(Text, {
|
|
242
203
|
style: {
|
|
243
|
-
fontSize:
|
|
204
|
+
fontSize: sizeTokens.iconSize,
|
|
244
205
|
color: fg,
|
|
245
206
|
fontWeight: theme.typography.fontWeight.semibold,
|
|
246
|
-
lineHeight:
|
|
207
|
+
lineHeight: sizeTokens.iconSize + 2
|
|
247
208
|
},
|
|
248
209
|
children: glyph
|
|
249
210
|
})
|
|
250
211
|
});
|
|
251
212
|
const accessibleInputLabel = accessibilityLabel ?? label ?? 'Number input';
|
|
252
|
-
const inputBlock = /*#__PURE__*/
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
backgroundColor: inputBg,
|
|
263
|
-
borderColor
|
|
264
|
-
}],
|
|
213
|
+
const inputBlock = /*#__PURE__*/_jsx(FieldBase, {
|
|
214
|
+
size: size,
|
|
215
|
+
disabled: disabled,
|
|
216
|
+
error: Boolean(error),
|
|
217
|
+
filled: true,
|
|
218
|
+
focused: focused,
|
|
219
|
+
leading: renderStepper('dec', decrement, atMin || disabled, 'Decrease', minusFg, '−'),
|
|
220
|
+
trailing: renderStepper('inc', increment, atMax || disabled, 'Increase', plusFg, '+'),
|
|
221
|
+
children: /*#__PURE__*/_jsxs(View, {
|
|
222
|
+
style: styles.inputRow,
|
|
265
223
|
children: [/*#__PURE__*/_jsx(TextInput, {
|
|
266
224
|
ref: inputRef,
|
|
267
225
|
value: draft,
|
|
@@ -291,19 +249,19 @@ const NumberInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
291
249
|
if (e.nativeEvent.actionName === 'decrement') decrement();
|
|
292
250
|
},
|
|
293
251
|
style: [styles.input, {
|
|
294
|
-
fontSize:
|
|
295
|
-
color:
|
|
296
|
-
|
|
252
|
+
fontSize: sizeTokens.fontSize,
|
|
253
|
+
color: fieldText.color,
|
|
254
|
+
...fieldText.weightStyle
|
|
297
255
|
}, inputStyle],
|
|
298
256
|
...textInputProps
|
|
299
257
|
}), unit ? /*#__PURE__*/_jsx(Text, {
|
|
300
258
|
style: [styles.unit, {
|
|
301
|
-
fontSize:
|
|
302
|
-
color:
|
|
303
|
-
}],
|
|
259
|
+
fontSize: sizeTokens.fontSize,
|
|
260
|
+
color: fieldText.placeholderColor
|
|
261
|
+
}, unitStyle],
|
|
304
262
|
children: unit
|
|
305
263
|
}) : null]
|
|
306
|
-
})
|
|
264
|
+
})
|
|
307
265
|
});
|
|
308
266
|
const labelEl = label ? /*#__PURE__*/_jsx(Text, {
|
|
309
267
|
style: [styles.label, {
|
|
@@ -323,7 +281,7 @@ const NumberInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
323
281
|
}) : null;
|
|
324
282
|
if (variant === 'horizontal') {
|
|
325
283
|
return /*#__PURE__*/_jsxs(View, {
|
|
326
|
-
style: [styles.horizontalContainer, style],
|
|
284
|
+
style: [styles.horizontalContainer, style, containerStyle],
|
|
327
285
|
testID: testID,
|
|
328
286
|
accessibilityState: {
|
|
329
287
|
disabled
|
|
@@ -336,10 +294,8 @@ const NumberInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
336
294
|
})]
|
|
337
295
|
});
|
|
338
296
|
}
|
|
339
|
-
|
|
340
|
-
// 'inline' and 'stacked' both stack label above the spinner row.
|
|
341
297
|
return /*#__PURE__*/_jsxs(View, {
|
|
342
|
-
style: style,
|
|
298
|
+
style: [style, containerStyle],
|
|
343
299
|
testID: testID,
|
|
344
300
|
accessibilityState: {
|
|
345
301
|
disabled
|
|
@@ -349,20 +305,18 @@ const NumberInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
349
305
|
});
|
|
350
306
|
NumberInput.displayName = 'NumberInput';
|
|
351
307
|
const buildStyles = _theme => StyleSheet.create({
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
alignItems: 'center'
|
|
355
|
-
},
|
|
356
|
-
inputContainer: {
|
|
308
|
+
inputRow: {
|
|
309
|
+
flex: 1,
|
|
357
310
|
flexDirection: 'row',
|
|
358
311
|
alignItems: 'center',
|
|
359
|
-
|
|
360
|
-
flex: 0
|
|
312
|
+
justifyContent: 'center'
|
|
361
313
|
},
|
|
362
314
|
input: {
|
|
363
315
|
flex: 1,
|
|
364
316
|
textAlign: 'center',
|
|
365
|
-
padding: 0
|
|
317
|
+
padding: 0,
|
|
318
|
+
margin: 0,
|
|
319
|
+
includeFontPadding: false
|
|
366
320
|
},
|
|
367
321
|
unit: {
|
|
368
322
|
marginLeft: 4
|
|
@@ -4,6 +4,7 @@ import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo
|
|
|
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
6
|
import { triggerHaptic } from "../../utils/index.js";
|
|
7
|
+
import { FieldBase } from "../FieldBase/FieldBase.js";
|
|
7
8
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
9
|
const sizeMap = {
|
|
9
10
|
sm: {
|
|
@@ -60,21 +61,28 @@ const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
60
61
|
const [focusedIndex, setFocusedIndex] = useState(-1);
|
|
61
62
|
const previousErrorRef = useRef(null);
|
|
62
63
|
const shake = useRef(createAnimatedValue(0)).current;
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
underlines.push(new Animated.Value(0));
|
|
64
|
+
const underlinesMapRef = useRef(new Map());
|
|
65
|
+
const underlines = useMemo(() => {
|
|
66
|
+
const map = underlinesMapRef.current;
|
|
67
|
+
const out = [];
|
|
68
|
+
for (let i = 0; i < length; i += 1) {
|
|
69
|
+
let v = map.get(i);
|
|
70
|
+
if (!v) {
|
|
71
|
+
v = new Animated.Value(0);
|
|
72
|
+
map.set(i, v);
|
|
73
73
|
}
|
|
74
|
-
|
|
75
|
-
underlines.length = length;
|
|
74
|
+
out.push(v);
|
|
76
75
|
}
|
|
77
|
-
|
|
76
|
+
// Trim stale entries beyond current length.
|
|
77
|
+
for (const key of Array.from(map.keys())) {
|
|
78
|
+
if (key >= length) map.delete(key);
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}, [length]);
|
|
82
|
+
const previousFocusedIndexRef = useRef(-1);
|
|
83
|
+
const shakeOnError = theme.components.otpInput?.shakeOnError ?? false;
|
|
84
|
+
const errorHaptic = theme.components.otpInput?.errorHaptic ?? false;
|
|
85
|
+
const selectionHaptic = theme.components.otpInput?.selectionHaptic ?? false;
|
|
78
86
|
const hasError = Boolean(error);
|
|
79
87
|
const errorMessage = typeof error === 'string' ? error : undefined;
|
|
80
88
|
|
|
@@ -83,32 +91,34 @@ const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
83
91
|
useEffect(() => {
|
|
84
92
|
const isFirstRun = previousErrorRef.current === null;
|
|
85
93
|
if (!isFirstRun && hasError && !previousErrorRef.current) {
|
|
86
|
-
triggerHaptic('notificationError');
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
94
|
+
if (errorHaptic) triggerHaptic('notificationError');
|
|
95
|
+
if (shakeOnError) {
|
|
96
|
+
setNativeValue(shake, 0);
|
|
97
|
+
Animated.sequence([Animated.timing(shake, {
|
|
98
|
+
toValue: 1,
|
|
99
|
+
duration: 75,
|
|
100
|
+
easing: Easing.linear,
|
|
101
|
+
useNativeDriver: true
|
|
102
|
+
}), Animated.timing(shake, {
|
|
103
|
+
toValue: -1,
|
|
104
|
+
duration: 75,
|
|
105
|
+
easing: Easing.linear,
|
|
106
|
+
useNativeDriver: true
|
|
107
|
+
}), Animated.timing(shake, {
|
|
108
|
+
toValue: 1,
|
|
109
|
+
duration: 75,
|
|
110
|
+
easing: Easing.linear,
|
|
111
|
+
useNativeDriver: true
|
|
112
|
+
}), Animated.timing(shake, {
|
|
113
|
+
toValue: 0,
|
|
114
|
+
duration: 75,
|
|
115
|
+
easing: Easing.linear,
|
|
116
|
+
useNativeDriver: true
|
|
117
|
+
})]).start();
|
|
118
|
+
}
|
|
109
119
|
}
|
|
110
120
|
previousErrorRef.current = hasError;
|
|
111
|
-
}, [hasError, shake]);
|
|
121
|
+
}, [hasError, shake, shakeOnError, errorHaptic]);
|
|
112
122
|
|
|
113
123
|
// Animate underline opacity for the focused cell. Skip on first mount
|
|
114
124
|
// (no prior focus state) — values are already at 0 and there's nothing to animate.
|
|
@@ -200,7 +210,7 @@ const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
200
210
|
const next = chars.join('').slice(0, length);
|
|
201
211
|
const previousLength = value.length;
|
|
202
212
|
updateValue(next);
|
|
203
|
-
if (next.length !== previousLength) {
|
|
213
|
+
if (next.length !== previousLength && selectionHaptic) {
|
|
204
214
|
triggerHaptic('selection');
|
|
205
215
|
}
|
|
206
216
|
|
|
@@ -211,7 +221,7 @@ const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
211
221
|
} else {
|
|
212
222
|
focusCell(nextFocus);
|
|
213
223
|
}
|
|
214
|
-
}, [cells, focusCell, keyboardType, length, updateValue, value]);
|
|
224
|
+
}, [cells, focusCell, keyboardType, length, selectionHaptic, updateValue, value]);
|
|
215
225
|
const handleKeyPress = useCallback((index, e) => {
|
|
216
226
|
const key = e.nativeEvent.key;
|
|
217
227
|
if (key !== 'Backspace') return;
|
|
@@ -276,23 +286,26 @@ const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
276
286
|
children: cells.map((char, index) => {
|
|
277
287
|
const isFocused = focusedIndex === index;
|
|
278
288
|
const isFilled = char.length > 0;
|
|
279
|
-
const borderColor = hasError ? theme.colors.border.error : isFocused ? theme.colors.border.focus : theme.colors.border.primary;
|
|
280
|
-
const backgroundColor = disabled ? theme.colors.surface.disabled : theme.colors.background.secondary;
|
|
281
289
|
const underlineColor = hasError ? theme.colors.border.error : theme.colors.border.focus;
|
|
282
|
-
const underlineOpacity = underlines[index]
|
|
290
|
+
const underlineOpacity = underlines[index];
|
|
291
|
+
if (!underlineOpacity) return null;
|
|
283
292
|
const display = secure && isFilled ? '●' : char;
|
|
284
293
|
return /*#__PURE__*/_jsxs(View, {
|
|
285
294
|
style: styles.cellWrapper,
|
|
286
|
-
children: [/*#__PURE__*/_jsxs(
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
295
|
+
children: [/*#__PURE__*/_jsxs(FieldBase, {
|
|
296
|
+
size: size,
|
|
297
|
+
width: sizeStyles.cell,
|
|
298
|
+
height: sizeStyles.cell,
|
|
299
|
+
borderRadius: sizeStyles.borderRadius,
|
|
300
|
+
borderWidth: 1.5,
|
|
301
|
+
focused: isFocused,
|
|
302
|
+
error: hasError,
|
|
303
|
+
filled: isFilled,
|
|
304
|
+
disabled: disabled,
|
|
305
|
+
paddingHorizontal: 0,
|
|
306
|
+
paddingVertical: 0,
|
|
307
|
+
gap: 0,
|
|
308
|
+
style: cellStyle,
|
|
296
309
|
children: [/*#__PURE__*/_jsx(TextInput, {
|
|
297
310
|
ref: node => {
|
|
298
311
|
inputsRef.current[index] = node;
|
|
@@ -362,12 +375,6 @@ const buildStyles = _theme => StyleSheet.create({
|
|
|
362
375
|
cellWrapper: {
|
|
363
376
|
alignItems: 'center'
|
|
364
377
|
},
|
|
365
|
-
cell: {
|
|
366
|
-
borderWidth: 1.5,
|
|
367
|
-
alignItems: 'center',
|
|
368
|
-
justifyContent: 'center',
|
|
369
|
-
overflow: 'hidden'
|
|
370
|
-
},
|
|
371
378
|
input: {
|
|
372
379
|
width: '100%',
|
|
373
380
|
height: '100%',
|