@webority-technologies/mobile 0.0.11 → 0.0.13
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/Badge/Badge.js +1 -1
- package/lib/commonjs/components/BottomNavigation/BottomNavigation.js +11 -3
- package/lib/commonjs/components/DatePicker/DatePicker.js +18 -12
- package/lib/commonjs/components/DateRangePicker/DateRangePicker.js +14 -9
- package/lib/commonjs/components/FloatingActionButton/FloatingActionButton.js +1 -1
- package/lib/commonjs/components/Input/Input.js +1 -1
- package/lib/commonjs/components/Modal/Modal.js +4 -4
- package/lib/commonjs/components/OTPInput/OTPInput.js +29 -9
- package/lib/commonjs/components/ProgressBar/ProgressBar.js +1 -1
- package/lib/commonjs/components/SearchBar/SearchBar.js +6 -1
- package/lib/commonjs/components/SegmentedControl/SegmentedControl.js +23 -28
- package/lib/commonjs/components/Skeleton/Skeleton.js +1 -1
- package/lib/commonjs/components/Slider/Slider.js +11 -11
- package/lib/commonjs/components/Stepper/Stepper.js +10 -4
- package/lib/commonjs/components/Tabs/Tabs.js +7 -5
- package/lib/commonjs/components/TimePicker/TimePicker.js +3 -3
- package/lib/commonjs/components/Toast/Toast.js +2 -2
- package/lib/commonjs/theme/animatedValue.js +20 -1
- package/lib/commonjs/theme/index.js +8 -1
- package/lib/module/components/Badge/Badge.js +2 -2
- package/lib/module/components/BottomNavigation/BottomNavigation.js +11 -3
- package/lib/module/components/DatePicker/DatePicker.js +19 -13
- package/lib/module/components/DateRangePicker/DateRangePicker.js +15 -10
- package/lib/module/components/FloatingActionButton/FloatingActionButton.js +2 -2
- package/lib/module/components/Input/Input.js +2 -2
- package/lib/module/components/Modal/Modal.js +5 -5
- package/lib/module/components/OTPInput/OTPInput.js +30 -10
- package/lib/module/components/ProgressBar/ProgressBar.js +2 -2
- package/lib/module/components/SearchBar/SearchBar.js +6 -1
- package/lib/module/components/SegmentedControl/SegmentedControl.js +24 -29
- package/lib/module/components/Skeleton/Skeleton.js +2 -2
- package/lib/module/components/Slider/Slider.js +12 -12
- package/lib/module/components/Stepper/Stepper.js +10 -4
- package/lib/module/components/Tabs/Tabs.js +7 -5
- package/lib/module/components/TimePicker/TimePicker.js +4 -4
- package/lib/module/components/Toast/Toast.js +3 -3
- package/lib/module/theme/animatedValue.js +18 -0
- package/lib/module/theme/index.js +1 -1
- package/lib/typescript/commonjs/components/BottomNavigation/BottomNavigation.d.ts +7 -0
- package/lib/typescript/commonjs/components/SearchBar/SearchBar.d.ts +2 -1
- package/lib/typescript/commonjs/theme/animatedValue.d.ts +11 -0
- package/lib/typescript/commonjs/theme/index.d.ts +1 -1
- package/lib/typescript/module/components/BottomNavigation/BottomNavigation.d.ts +7 -0
- package/lib/typescript/module/components/SearchBar/SearchBar.d.ts +2 -1
- package/lib/typescript/module/theme/animatedValue.d.ts +11 -0
- package/lib/typescript/module/theme/index.d.ts +1 -1
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.createAnimatedValue = void 0;
|
|
6
|
+
exports.setNativeValue = exports.createAnimatedValue = void 0;
|
|
7
7
|
var _reactNative = require("react-native");
|
|
8
8
|
/**
|
|
9
9
|
* Create an `Animated.Value` that survives RN 0.85's dev-mode prop deepFreeze.
|
|
@@ -24,5 +24,24 @@ var _reactNative = require("react-native");
|
|
|
24
24
|
* Use everywhere the library would otherwise call `new Animated.Value(...)`.
|
|
25
25
|
*/
|
|
26
26
|
const createAnimatedValue = (initial, config) => Object.seal(new _reactNative.Animated.Value(initial, config));
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Set an `Animated.Value` to a target without going through JS.
|
|
30
|
+
*
|
|
31
|
+
* Once a value has been driven by `useNativeDriver: true`, the underlying
|
|
32
|
+
* native node owns it — calling `value.setValue(x)` from JS throws
|
|
33
|
+
* "Attempting to run JS driven animation on animated node that has been moved to native".
|
|
34
|
+
* Use this helper for any value that is *also* used in a native-driven
|
|
35
|
+
* `Animated.timing/spring`. A zero-duration native timing routes the update
|
|
36
|
+
* through the same driver and stays valid across re-mounts and re-runs.
|
|
37
|
+
*/
|
|
27
38
|
exports.createAnimatedValue = createAnimatedValue;
|
|
39
|
+
const setNativeValue = (value, to) => {
|
|
40
|
+
_reactNative.Animated.timing(value, {
|
|
41
|
+
toValue: to,
|
|
42
|
+
duration: 0,
|
|
43
|
+
useNativeDriver: true
|
|
44
|
+
}).start();
|
|
45
|
+
};
|
|
46
|
+
exports.setNativeValue = setNativeValue;
|
|
28
47
|
//# sourceMappingURL=animatedValue.js.map
|
|
@@ -52,7 +52,14 @@ Object.defineProperty(exports, "mergeTheme", {
|
|
|
52
52
|
return _merge.mergeTheme;
|
|
53
53
|
}
|
|
54
54
|
});
|
|
55
|
-
exports.
|
|
55
|
+
exports.setColorMode = exports.resetTheme = void 0;
|
|
56
|
+
Object.defineProperty(exports, "setNativeValue", {
|
|
57
|
+
enumerable: true,
|
|
58
|
+
get: function () {
|
|
59
|
+
return _animatedValue.setNativeValue;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
exports.subscribeTheme = exports.setTheme = void 0;
|
|
56
63
|
Object.defineProperty(exports, "useTheme", {
|
|
57
64
|
enumerable: true,
|
|
58
65
|
get: function () {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import React, { forwardRef, useEffect, useMemo, useRef } from 'react';
|
|
4
4
|
import { Animated, Easing, StyleSheet, Text, View } from 'react-native';
|
|
5
|
-
import { fontFor, useTheme, createAnimatedValue } from "../../theme/index.js";
|
|
5
|
+
import { fontFor, useTheme, createAnimatedValue, setNativeValue } from "../../theme/index.js";
|
|
6
6
|
import { SkeletonContent } from "../Skeleton/index.js";
|
|
7
7
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
8
|
const toneFor = (theme, tone) => {
|
|
@@ -126,7 +126,7 @@ const Badge = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
126
126
|
const pulseScale = useRef(createAnimatedValue(1)).current;
|
|
127
127
|
useEffect(() => {
|
|
128
128
|
if (!pulse || !shouldRender) {
|
|
129
|
-
pulseScale
|
|
129
|
+
setNativeValue(pulseScale, 1);
|
|
130
130
|
return;
|
|
131
131
|
}
|
|
132
132
|
const loop = Animated.loop(Animated.sequence([Animated.timing(pulseScale, {
|
|
@@ -48,6 +48,7 @@ const BottomNavigation = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
48
48
|
haptic = 'selection',
|
|
49
49
|
showLabels = true,
|
|
50
50
|
variant = 'pill',
|
|
51
|
+
indicatorPosition = 'bottom',
|
|
51
52
|
style,
|
|
52
53
|
indicatorStyle,
|
|
53
54
|
labelStyle,
|
|
@@ -193,7 +194,7 @@ const BottomNavigation = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
193
194
|
}, tab.key);
|
|
194
195
|
}), variant === 'underline' && tabWidth > 0 ? /*#__PURE__*/_jsx(Animated.View, {
|
|
195
196
|
pointerEvents: "none",
|
|
196
|
-
style: [styles.underline, {
|
|
197
|
+
style: [styles.underline, indicatorPosition === 'top' ? styles.underlineTop : styles.underlineBottom, {
|
|
197
198
|
width: tabWidth,
|
|
198
199
|
backgroundColor: theme.colors.primary,
|
|
199
200
|
transform: [{
|
|
@@ -243,12 +244,19 @@ const buildStyles = theme => StyleSheet.create({
|
|
|
243
244
|
},
|
|
244
245
|
underline: {
|
|
245
246
|
position: 'absolute',
|
|
246
|
-
bottom: -6,
|
|
247
247
|
left: 0,
|
|
248
|
-
height: UNDERLINE_HEIGHT
|
|
248
|
+
height: UNDERLINE_HEIGHT
|
|
249
|
+
},
|
|
250
|
+
underlineBottom: {
|
|
251
|
+
bottom: -6,
|
|
249
252
|
borderTopLeftRadius: 2,
|
|
250
253
|
borderTopRightRadius: 2
|
|
251
254
|
},
|
|
255
|
+
underlineTop: {
|
|
256
|
+
top: -6,
|
|
257
|
+
borderBottomLeftRadius: 2,
|
|
258
|
+
borderBottomRightRadius: 2
|
|
259
|
+
},
|
|
252
260
|
badgePill: {
|
|
253
261
|
position: 'absolute',
|
|
254
262
|
top: -4,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { AccessibilityInfo, Animated, Easing, Modal, Pressable, StyleSheet, Text, View } from 'react-native';
|
|
5
|
-
import { useTheme, createAnimatedValue } from "../../theme/index.js";
|
|
5
|
+
import { useTheme, createAnimatedValue, setNativeValue } from "../../theme/index.js";
|
|
6
6
|
import { triggerHaptic } from "../../utils/index.js";
|
|
7
7
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
8
8
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
@@ -130,8 +130,8 @@ const DatePicker = props => {
|
|
|
130
130
|
useEffect(() => {
|
|
131
131
|
if (mode !== 'modal') return;
|
|
132
132
|
if (visible) {
|
|
133
|
-
backdrop
|
|
134
|
-
sheet
|
|
133
|
+
setNativeValue(backdrop, 0);
|
|
134
|
+
setNativeValue(sheet, 0);
|
|
135
135
|
Animated.parallel([Animated.timing(backdrop, {
|
|
136
136
|
toValue: 1,
|
|
137
137
|
duration: theme.motion.duration.normal,
|
|
@@ -243,8 +243,8 @@ const DatePicker = props => {
|
|
|
243
243
|
|
|
244
244
|
// View-mode transition: fade + scale (200ms, native driver).
|
|
245
245
|
const animateViewTransition = useCallback(() => {
|
|
246
|
-
viewFade
|
|
247
|
-
viewScale
|
|
246
|
+
setNativeValue(viewFade, 0);
|
|
247
|
+
setNativeValue(viewScale, 0.9);
|
|
248
248
|
Animated.parallel([Animated.timing(viewFade, {
|
|
249
249
|
toValue: 1,
|
|
250
250
|
duration: 200,
|
|
@@ -295,7 +295,7 @@ const DatePicker = props => {
|
|
|
295
295
|
if (!cell.inMonth) {
|
|
296
296
|
setAnchor(new Date(cell.date.getFullYear(), cell.date.getMonth(), 1));
|
|
297
297
|
}
|
|
298
|
-
selectScale
|
|
298
|
+
setNativeValue(selectScale, 0.7);
|
|
299
299
|
Animated.spring(selectScale, {
|
|
300
300
|
toValue: 1,
|
|
301
301
|
damping: theme.motion.spring.bouncy.damping,
|
|
@@ -377,11 +377,16 @@ const DatePicker = props => {
|
|
|
377
377
|
opacity: viewMode === 'days' ? monthFade : 1
|
|
378
378
|
}],
|
|
379
379
|
children: [/*#__PURE__*/_jsx(Animated.Text, {
|
|
380
|
-
style: [styles.headerLabel,
|
|
380
|
+
style: [styles.headerLabel,
|
|
381
|
+
// Always include the transform — passing null/undefined as a
|
|
382
|
+
// transform value confuses Animated's processTransform on Fabric.
|
|
383
|
+
// When the month slide isn't active we still drive translateX
|
|
384
|
+
// through the same value, just with target 0.
|
|
385
|
+
{
|
|
381
386
|
transform: [{
|
|
382
|
-
translateX: monthSlide
|
|
387
|
+
translateX: viewMode === 'days' ? monthSlide : 0
|
|
383
388
|
}]
|
|
384
|
-
}
|
|
389
|
+
}],
|
|
385
390
|
accessibilityLiveRegion: "polite",
|
|
386
391
|
children: headerLabel
|
|
387
392
|
}), /*#__PURE__*/_jsx(Text, {
|
|
@@ -447,11 +452,12 @@ const DatePicker = props => {
|
|
|
447
452
|
backgroundColor: cellBg,
|
|
448
453
|
borderColor,
|
|
449
454
|
borderWidth: todayCell && !selected ? 1.5 : 0,
|
|
450
|
-
opacity
|
|
451
|
-
|
|
455
|
+
opacity
|
|
456
|
+
}, selected ? {
|
|
457
|
+
transform: [{
|
|
452
458
|
scale: selectScale
|
|
453
|
-
}]
|
|
454
|
-
}],
|
|
459
|
+
}]
|
|
460
|
+
} : null],
|
|
455
461
|
children: /*#__PURE__*/_jsx(Text, {
|
|
456
462
|
style: [styles.dayText, {
|
|
457
463
|
color: textColor
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { AccessibilityInfo, Animated, Easing, Modal, Pressable, StyleSheet, Text, View } from 'react-native';
|
|
5
|
-
import { useTheme, createAnimatedValue } from "../../theme/index.js";
|
|
5
|
+
import { useTheme, createAnimatedValue, setNativeValue } from "../../theme/index.js";
|
|
6
6
|
import { triggerHaptic } from "../../utils/index.js";
|
|
7
7
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
8
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
@@ -115,8 +115,8 @@ const DateRangePicker = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
115
115
|
// Modal open / close animation.
|
|
116
116
|
useEffect(() => {
|
|
117
117
|
if (visible) {
|
|
118
|
-
backdrop
|
|
119
|
-
sheet
|
|
118
|
+
setNativeValue(backdrop, 0);
|
|
119
|
+
setNativeValue(sheet, 0);
|
|
120
120
|
Animated.parallel([Animated.timing(backdrop, {
|
|
121
121
|
toValue: 1,
|
|
122
122
|
duration: theme.motion.duration.normal,
|
|
@@ -556,26 +556,31 @@ const buildStyles = theme => {
|
|
|
556
556
|
aspectRatio: 1,
|
|
557
557
|
alignItems: 'center',
|
|
558
558
|
justifyContent: 'center',
|
|
559
|
-
padding
|
|
559
|
+
// No horizontal padding so the connector bars on adjacent cells meet
|
|
560
|
+
// without a gap. Vertical breathing room comes from dayInner's height.
|
|
561
|
+
padding: 0,
|
|
560
562
|
position: 'relative'
|
|
561
563
|
},
|
|
564
|
+
// Bars sit BEHIND dayInner and bridge the start/end circles to mid-range
|
|
565
|
+
// cells. Their height mirrors dayInner (~90% of cell height) so the visual
|
|
566
|
+
// pill is one continuous shape across multiple cells.
|
|
562
567
|
barLeft: {
|
|
563
568
|
position: 'absolute',
|
|
564
569
|
left: 0,
|
|
565
570
|
right: '50%',
|
|
566
|
-
top: '
|
|
567
|
-
bottom: '
|
|
571
|
+
top: '5%',
|
|
572
|
+
bottom: '5%'
|
|
568
573
|
},
|
|
569
574
|
barRight: {
|
|
570
575
|
position: 'absolute',
|
|
571
576
|
left: '50%',
|
|
572
577
|
right: 0,
|
|
573
|
-
top: '
|
|
574
|
-
bottom: '
|
|
578
|
+
top: '5%',
|
|
579
|
+
bottom: '5%'
|
|
575
580
|
},
|
|
576
581
|
dayInner: {
|
|
577
|
-
width: '
|
|
578
|
-
height: '
|
|
582
|
+
width: '90%',
|
|
583
|
+
height: '90%',
|
|
579
584
|
alignItems: 'center',
|
|
580
585
|
justifyContent: 'center'
|
|
581
586
|
},
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { Animated, Easing, Pressable, StyleSheet, Text, View } from 'react-native';
|
|
5
5
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
6
|
-
import { useTheme, createAnimatedValue } from "../../theme/index.js";
|
|
6
|
+
import { useTheme, createAnimatedValue, setNativeValue } from "../../theme/index.js";
|
|
7
7
|
import { usePressAnimation } from "../../hooks/usePressAnimation.js";
|
|
8
8
|
import { triggerHaptic } from "../../utils/hapticUtils.js";
|
|
9
9
|
import { AppIcon } from "../AppIcon/index.js";
|
|
@@ -81,7 +81,7 @@ const FloatingActionButton = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
81
81
|
const hideAnim = useRef(createAnimatedValue(0)).current;
|
|
82
82
|
useEffect(() => {
|
|
83
83
|
if (!hideOnScroll) {
|
|
84
|
-
hideAnim
|
|
84
|
+
setNativeValue(hideAnim, 0);
|
|
85
85
|
return;
|
|
86
86
|
}
|
|
87
87
|
Animated.timing(hideAnim, {
|
|
@@ -5,7 +5,7 @@ import { Animated, Easing, Pressable, StyleSheet, Text, TextInput, View } from '
|
|
|
5
5
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
6
6
|
// @ts-ignore - react-native-vector-icons ships no bundled types in this version
|
|
7
7
|
import Feather from 'react-native-vector-icons/Feather';
|
|
8
|
-
import { createAnimatedValue, fontFor, useTheme } from "../../theme/index.js";
|
|
8
|
+
import { createAnimatedValue, fontFor, setNativeValue, useTheme } from "../../theme/index.js";
|
|
9
9
|
import { triggerHaptic } from "../../utils/index.js";
|
|
10
10
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
11
|
const sizeMap = {
|
|
@@ -140,7 +140,7 @@ const Input = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
140
140
|
const prevErrorRef = useRef(hasError);
|
|
141
141
|
useEffect(() => {
|
|
142
142
|
if (hasError && !prevErrorRef.current) {
|
|
143
|
-
shakeAnim
|
|
143
|
+
setNativeValue(shakeAnim, 0);
|
|
144
144
|
Animated.sequence([Animated.timing(shakeAnim, {
|
|
145
145
|
toValue: 1,
|
|
146
146
|
duration: 50,
|
|
@@ -2,7 +2,7 @@
|
|
|
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 { useTheme, createAnimatedValue } from "../../theme/index.js";
|
|
5
|
+
import { useTheme, createAnimatedValue, setNativeValue } from "../../theme/index.js";
|
|
6
6
|
import { triggerHaptic } from "../../utils/hapticUtils.js";
|
|
7
7
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
8
|
const Modal = /*#__PURE__*/forwardRef((props, ref) => {
|
|
@@ -60,10 +60,10 @@ const Modal = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
60
60
|
})]).start();
|
|
61
61
|
}
|
|
62
62
|
} else {
|
|
63
|
-
backdropAnim
|
|
64
|
-
scaleAnim
|
|
65
|
-
opacityAnim
|
|
66
|
-
translateYAnim
|
|
63
|
+
setNativeValue(backdropAnim, 0);
|
|
64
|
+
setNativeValue(scaleAnim, 0.9);
|
|
65
|
+
setNativeValue(opacityAnim, 0);
|
|
66
|
+
setNativeValue(translateYAnim, screenHeight);
|
|
67
67
|
}
|
|
68
68
|
}, [visible, presentation, duration, backdropAnim, scaleAnim, opacityAnim, translateYAnim, screenHeight, theme.motion.spring.gentle.damping, theme.motion.spring.gentle.stiffness, theme.motion.spring.gentle.mass]);
|
|
69
69
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
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
|
-
import { fontFor, useTheme, createAnimatedValue } from "../../theme/index.js";
|
|
5
|
+
import { fontFor, useTheme, createAnimatedValue, setNativeValue } from "../../theme/index.js";
|
|
6
6
|
import { triggerHaptic } from "../../utils/index.js";
|
|
7
7
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
8
|
const sizeMap = {
|
|
@@ -84,7 +84,7 @@ const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
84
84
|
const isFirstRun = previousErrorRef.current === null;
|
|
85
85
|
if (!isFirstRun && hasError && !previousErrorRef.current) {
|
|
86
86
|
triggerHaptic('notificationError');
|
|
87
|
-
shake
|
|
87
|
+
setNativeValue(shake, 0);
|
|
88
88
|
Animated.sequence([Animated.timing(shake, {
|
|
89
89
|
toValue: 1,
|
|
90
90
|
duration: 75,
|
|
@@ -170,16 +170,27 @@ const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
170
170
|
}
|
|
171
171
|
}, [length, onChange, onComplete, value]);
|
|
172
172
|
const handleChangeText = useCallback((index, raw) => {
|
|
173
|
-
|
|
173
|
+
// Strip the ZWSP placeholder (used so iOS fires onKeyPress/Backspace on otherwise-empty cells).
|
|
174
|
+
const stripped = raw.replace(/\u200B/g, '');
|
|
175
|
+
const sanitized = sanitizeChar(stripped, keyboardType);
|
|
174
176
|
if (!sanitized) {
|
|
175
|
-
//
|
|
177
|
+
// Cell was cleared (delete / cut). Clear this cell and step focus back.
|
|
176
178
|
const chars = cells.slice();
|
|
179
|
+
const wasFilled = (chars[index] ?? '').length > 0;
|
|
177
180
|
chars[index] = '';
|
|
178
181
|
updateValue(chars.join('').slice(0, length));
|
|
182
|
+
if (!wasFilled && index > 0) {
|
|
183
|
+
// Empty cell + backspace → also clear and focus the previous cell.
|
|
184
|
+
const prev = cells.slice();
|
|
185
|
+
prev[index - 1] = '';
|
|
186
|
+
updateValue(prev.join(''));
|
|
187
|
+
focusCell(index - 1);
|
|
188
|
+
}
|
|
179
189
|
return;
|
|
180
190
|
}
|
|
181
191
|
|
|
182
192
|
// Fill cells from current index onward (handles paste of multi-char text).
|
|
193
|
+
// For single-character typing, this overwrites the current cell and advances.
|
|
183
194
|
const chars = cells.slice();
|
|
184
195
|
let writeIndex = index;
|
|
185
196
|
for (let i = 0; i < sanitized.length && writeIndex < length; i += 1) {
|
|
@@ -196,7 +207,6 @@ const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
196
207
|
// Move focus to the next empty cell or last cell.
|
|
197
208
|
const nextFocus = Math.min(writeIndex, length - 1);
|
|
198
209
|
if (writeIndex >= length) {
|
|
199
|
-
// All filled — blur last cell.
|
|
200
210
|
inputsRef.current[length - 1]?.blur();
|
|
201
211
|
} else {
|
|
202
212
|
focusCell(nextFocus);
|
|
@@ -205,9 +215,12 @@ const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
205
215
|
const handleKeyPress = useCallback((index, e) => {
|
|
206
216
|
const key = e.nativeEvent.key;
|
|
207
217
|
if (key !== 'Backspace') return;
|
|
218
|
+
// Backspace on a non-empty cell — clear it. Backspace on an empty cell —
|
|
219
|
+
// step back and clear the previous cell. handleChangeText also covers the
|
|
220
|
+
// empty-cell case via the ZWSP placeholder, so this branch only matters
|
|
221
|
+
// when the platform fires onKeyPress without firing onChangeText.
|
|
208
222
|
const current = cells[index] ?? '';
|
|
209
223
|
if (current.length === 0) {
|
|
210
|
-
// Move back, clear previous cell.
|
|
211
224
|
if (index > 0) {
|
|
212
225
|
const chars = cells.slice();
|
|
213
226
|
chars[index - 1] = '';
|
|
@@ -283,8 +296,11 @@ const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
283
296
|
children: [/*#__PURE__*/_jsx(TextInput, {
|
|
284
297
|
ref: node => {
|
|
285
298
|
inputsRef.current[index] = node;
|
|
286
|
-
}
|
|
287
|
-
|
|
299
|
+
}
|
|
300
|
+
// Always render at least the ZWSP placeholder so iOS keeps
|
|
301
|
+
// firing onChangeText/onKeyPress for Backspace on empty cells.
|
|
302
|
+
,
|
|
303
|
+
value: (secure && isFilled ? '' : char) || '\u200B',
|
|
288
304
|
onChangeText: t => handleChangeText(index, t),
|
|
289
305
|
onKeyPress: e => handleKeyPress(index, e),
|
|
290
306
|
onFocus: () => handleFocus(index),
|
|
@@ -292,8 +308,12 @@ const OTPInput = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
292
308
|
keyboardType: keyboardType,
|
|
293
309
|
editable: !disabled,
|
|
294
310
|
selectTextOnFocus: true,
|
|
295
|
-
caretHidden: isFilled
|
|
296
|
-
|
|
311
|
+
caretHidden: isFilled
|
|
312
|
+
// Only the first cell accepts paste-like multi-char input
|
|
313
|
+
// (e.g., when SMS autofill or clipboard delivers the full code);
|
|
314
|
+
// every other cell is single-char so typing always overwrites.
|
|
315
|
+
,
|
|
316
|
+
maxLength: index === 0 ? length : 2,
|
|
297
317
|
textContentType: index === 0 ? 'oneTimeCode' : 'none',
|
|
298
318
|
autoComplete: index === 0 ? 'sms-otp' : 'off',
|
|
299
319
|
importantForAutofill: index === 0 ? 'yes' : 'no',
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import React, { forwardRef, useEffect, useMemo, useRef } from 'react';
|
|
4
4
|
import { Animated, Easing, StyleSheet, View } from 'react-native';
|
|
5
|
-
import { useTheme, createAnimatedValue } from "../../theme/index.js";
|
|
5
|
+
import { useTheme, createAnimatedValue, setNativeValue } from "../../theme/index.js";
|
|
6
6
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
7
7
|
const toneColor = (theme, tone) => {
|
|
8
8
|
switch (tone) {
|
|
@@ -62,7 +62,7 @@ const ProgressBar = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
62
62
|
}, [animated, clamped, fillAnim, isIndeterminate, theme]);
|
|
63
63
|
useEffect(() => {
|
|
64
64
|
if (!isIndeterminate) return;
|
|
65
|
-
loopAnim
|
|
65
|
+
setNativeValue(loopAnim, 0);
|
|
66
66
|
const animation = Animated.loop(Animated.timing(loopAnim, {
|
|
67
67
|
toValue: 1,
|
|
68
68
|
duration: 1500,
|
|
@@ -33,7 +33,7 @@ const sizeMap = {
|
|
|
33
33
|
const CANCEL_WIDTH = 72;
|
|
34
34
|
const SearchBar = /*#__PURE__*/forwardRef((props, ref) => {
|
|
35
35
|
const {
|
|
36
|
-
value,
|
|
36
|
+
value: rawValue,
|
|
37
37
|
onChangeText,
|
|
38
38
|
onSubmit,
|
|
39
39
|
placeholder = 'Search…',
|
|
@@ -59,6 +59,11 @@ const SearchBar = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
59
59
|
};
|
|
60
60
|
const cancelWidth = theme.components.searchBar?.cancelButtonWidth ?? CANCEL_WIDTH;
|
|
61
61
|
const styles = useMemo(() => buildStyles(theme), [theme]);
|
|
62
|
+
|
|
63
|
+
// Coerce nullable inputs to '' once at the top so every downstream read is
|
|
64
|
+
// a guaranteed string. Required when consumers pass `value={state}` where
|
|
65
|
+
// `state` may be `undefined` / `null` before being initialised.
|
|
66
|
+
const value = typeof rawValue === 'string' ? rawValue : '';
|
|
62
67
|
const [isFocused, setIsFocused] = useState(false);
|
|
63
68
|
const [internalValue, setInternalValue] = useState(value);
|
|
64
69
|
const debouncedValue = useDebounce(internalValue, debounceMs ?? 0);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* fully accessible (radiogroup / radio roles + selected state).
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import React, { forwardRef, useCallback, useEffect, useMemo, useRef } from 'react';
|
|
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
14
|
import { triggerHaptic } from "../../utils/index.js";
|
|
@@ -45,44 +45,39 @@ const SegmentedControl = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
45
45
|
const styles = useMemo(() => buildStyles(theme), [theme]);
|
|
46
46
|
const sizeStyles = sizeMap[size];
|
|
47
47
|
|
|
48
|
-
//
|
|
49
|
-
|
|
48
|
+
// Track width is measured from onLayout. Thumb width is a regular number (not
|
|
49
|
+
// animated) — `width` cannot be driven by the native animated module, and mixing
|
|
50
|
+
// a JS-driven width with a native-driven translateX inside `Animated.parallel`
|
|
51
|
+
// sends the parallel composite into the native driver and JS setValue/timing
|
|
52
|
+
// calls on the same value then throw "moved to native". Keep width as state and
|
|
53
|
+
// only animate translateX via the native driver.
|
|
54
|
+
const [trackWidth, setTrackWidth] = useState(0);
|
|
50
55
|
const thumbTranslateX = useRef(createAnimatedValue(0)).current;
|
|
51
|
-
const thumbWidth = useRef(createAnimatedValue(0)).current;
|
|
52
56
|
const activeIndex = Math.max(0, segments.findIndex(s => s.value === value));
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
const segmentWidth = trackWidth > 0 ? (trackWidth - TRACK_PADDING * 2) / Math.max(segments.length, 1) : 0;
|
|
58
|
+
const animateThumb = useCallback((index, segWidth) => {
|
|
59
|
+
if (segWidth <= 0) return;
|
|
56
60
|
const targetX = TRACK_PADDING + segWidth * index;
|
|
57
61
|
const spring = theme.motion.spring.snappy;
|
|
58
|
-
Animated.
|
|
62
|
+
Animated.spring(thumbTranslateX, {
|
|
59
63
|
toValue: targetX,
|
|
60
64
|
damping: spring.damping,
|
|
61
65
|
stiffness: spring.stiffness,
|
|
62
66
|
mass: spring.mass,
|
|
63
67
|
useNativeDriver: true
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
damping: spring.damping,
|
|
67
|
-
stiffness: spring.stiffness,
|
|
68
|
-
mass: spring.mass,
|
|
69
|
-
useNativeDriver: false
|
|
70
|
-
})]).start();
|
|
71
|
-
}, [segments.length, theme.motion.spring.snappy, thumbTranslateX, thumbWidth]);
|
|
68
|
+
}).start();
|
|
69
|
+
}, [theme.motion.spring.snappy, thumbTranslateX]);
|
|
72
70
|
useEffect(() => {
|
|
73
|
-
if (
|
|
74
|
-
animateThumb(activeIndex,
|
|
71
|
+
if (segmentWidth > 0) {
|
|
72
|
+
animateThumb(activeIndex, segmentWidth);
|
|
75
73
|
}
|
|
76
|
-
}, [activeIndex, animateThumb]);
|
|
74
|
+
}, [activeIndex, segmentWidth, animateThumb]);
|
|
77
75
|
const handleTrackLayout = useCallback(e => {
|
|
78
76
|
const width = e.nativeEvent.layout.width;
|
|
79
|
-
if (width
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
thumbWidth.setValue(segWidth);
|
|
84
|
-
thumbTranslateX.setValue(TRACK_PADDING + segWidth * activeIndex);
|
|
85
|
-
}, [activeIndex, segments.length, thumbTranslateX, thumbWidth]);
|
|
77
|
+
if (width !== trackWidth) {
|
|
78
|
+
setTrackWidth(width);
|
|
79
|
+
}
|
|
80
|
+
}, [trackWidth]);
|
|
86
81
|
const handlePress = useCallback(segment => {
|
|
87
82
|
if (disabled) return;
|
|
88
83
|
if (segment.value === value) return;
|
|
@@ -105,10 +100,10 @@ const SegmentedControl = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
105
100
|
alignSelf: fullWidth ? 'stretch' : 'flex-start'
|
|
106
101
|
}, style],
|
|
107
102
|
onLayout: handleTrackLayout,
|
|
108
|
-
children: [/*#__PURE__*/_jsx(Animated.View, {
|
|
103
|
+
children: [segmentWidth > 0 ? /*#__PURE__*/_jsx(Animated.View, {
|
|
109
104
|
pointerEvents: "none",
|
|
110
105
|
style: [styles.thumb, {
|
|
111
|
-
width:
|
|
106
|
+
width: segmentWidth,
|
|
112
107
|
height: sizeStyles.height - TRACK_PADDING * 2,
|
|
113
108
|
borderRadius: theme.radius.sm,
|
|
114
109
|
backgroundColor: thumbBg,
|
|
@@ -117,7 +112,7 @@ const SegmentedControl = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
117
112
|
}],
|
|
118
113
|
...theme.shadows.sm
|
|
119
114
|
}]
|
|
120
|
-
}), segments.map(segment => {
|
|
115
|
+
}) : null, segments.map(segment => {
|
|
121
116
|
const isActive = segment.value === value;
|
|
122
117
|
return /*#__PURE__*/_jsxs(Pressable, {
|
|
123
118
|
onPress: () => handlePress(segment),
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { Animated, Easing, StyleSheet, View } from 'react-native';
|
|
5
|
-
import { useTheme, createAnimatedValue } from "../../theme/index.js";
|
|
5
|
+
import { useTheme, createAnimatedValue, setNativeValue } from "../../theme/index.js";
|
|
6
6
|
import { Responsive } from "../../utils/index.js";
|
|
7
7
|
import { useSkeletonDefaults } from "./SkeletonProvider.js";
|
|
8
8
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
@@ -50,7 +50,7 @@ const Skeleton = ({
|
|
|
50
50
|
const progress = useRef(createAnimatedValue(0)).current;
|
|
51
51
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
52
52
|
useEffect(() => {
|
|
53
|
-
progress
|
|
53
|
+
setNativeValue(progress, 0);
|
|
54
54
|
const duration = resolvedVariant === 'pulse' ? PULSE_DURATION : SPEED_DURATION[resolvedSpeed];
|
|
55
55
|
const animation = resolvedVariant === 'pulse' ? Animated.loop(Animated.sequence([Animated.timing(progress, {
|
|
56
56
|
toValue: 1,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { Animated, Easing, PanResponder, Platform, Pressable, StyleSheet, Text, View } from 'react-native';
|
|
5
|
-
import { useTheme, createAnimatedValue } from "../../theme/index.js";
|
|
5
|
+
import { useTheme, createAnimatedValue, setNativeValue } from "../../theme/index.js";
|
|
6
6
|
import { triggerHaptic } from "../../utils/index.js";
|
|
7
7
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
8
|
const SIZE_MAP = {
|
|
@@ -105,14 +105,14 @@ const Slider = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
105
105
|
highRef.current = hi;
|
|
106
106
|
lastReportedLow.current = lo;
|
|
107
107
|
lastReportedHigh.current = hi;
|
|
108
|
-
lowX
|
|
109
|
-
highX
|
|
108
|
+
setNativeValue(lowX, valueToPx(lo, trackWidth));
|
|
109
|
+
setNativeValue(highX, valueToPx(hi, trackWidth));
|
|
110
110
|
} else {
|
|
111
111
|
const v = props.value;
|
|
112
112
|
lowRef.current = v;
|
|
113
113
|
highRef.current = v;
|
|
114
114
|
lastReportedLow.current = v;
|
|
115
|
-
lowX
|
|
115
|
+
setNativeValue(lowX, valueToPx(v, trackWidth));
|
|
116
116
|
}
|
|
117
117
|
// We intentionally listen to props.value across both shapes via JSON; ESLint is fine.
|
|
118
118
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -125,10 +125,10 @@ const Slider = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
125
125
|
const [v0, v1] = props.value;
|
|
126
126
|
const lo = Math.min(v0, v1);
|
|
127
127
|
const hi = Math.max(v0, v1);
|
|
128
|
-
lowX
|
|
129
|
-
highX
|
|
128
|
+
setNativeValue(lowX, valueToPx(lo, w));
|
|
129
|
+
setNativeValue(highX, valueToPx(hi, w));
|
|
130
130
|
} else {
|
|
131
|
-
lowX
|
|
131
|
+
setNativeValue(lowX, valueToPx(props.value, w));
|
|
132
132
|
}
|
|
133
133
|
},
|
|
134
134
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -179,7 +179,7 @@ const Slider = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
179
179
|
lastReportedLow.current = nextValue;
|
|
180
180
|
}
|
|
181
181
|
lowRef.current = nextValue;
|
|
182
|
-
lowX
|
|
182
|
+
setNativeValue(lowX, valueToPx(nextValue, trackWidth));
|
|
183
183
|
fireChange(nextValue, highRef.current);
|
|
184
184
|
} else {
|
|
185
185
|
if (isRange && nextValue < lowRef.current) nextValue = lowRef.current;
|
|
@@ -188,7 +188,7 @@ const Slider = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
188
188
|
lastReportedHigh.current = nextValue;
|
|
189
189
|
}
|
|
190
190
|
highRef.current = nextValue;
|
|
191
|
-
highX
|
|
191
|
+
setNativeValue(highX, valueToPx(nextValue, trackWidth));
|
|
192
192
|
fireChange(lowRef.current, nextValue);
|
|
193
193
|
}
|
|
194
194
|
},
|
|
@@ -218,7 +218,7 @@ const Slider = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
218
218
|
toValue: valueToPx(next, trackWidth),
|
|
219
219
|
duration: theme.motion.duration.fast,
|
|
220
220
|
easing: Easing.out(Easing.cubic),
|
|
221
|
-
useNativeDriver:
|
|
221
|
+
useNativeDriver: true
|
|
222
222
|
}).start();
|
|
223
223
|
fireChange(next, next);
|
|
224
224
|
}
|
|
@@ -233,7 +233,7 @@ const Slider = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
233
233
|
if (nextLow !== lowRef.current) {
|
|
234
234
|
lowRef.current = nextLow;
|
|
235
235
|
lastReportedLow.current = nextLow;
|
|
236
|
-
lowX
|
|
236
|
+
setNativeValue(lowX, valueToPx(nextLow, trackWidth));
|
|
237
237
|
triggerHaptic('selection');
|
|
238
238
|
fireChange(nextLow, highRef.current);
|
|
239
239
|
}
|
|
@@ -242,7 +242,7 @@ const Slider = /*#__PURE__*/forwardRef((props, ref) => {
|
|
|
242
242
|
if (next !== lowRef.current) {
|
|
243
243
|
lowRef.current = next;
|
|
244
244
|
lastReportedLow.current = next;
|
|
245
|
-
lowX
|
|
245
|
+
setNativeValue(lowX, valueToPx(next, trackWidth));
|
|
246
246
|
triggerHaptic('selection');
|
|
247
247
|
fireChange(next, next);
|
|
248
248
|
}
|