@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.
Files changed (47) hide show
  1. package/lib/commonjs/components/Badge/Badge.js +1 -1
  2. package/lib/commonjs/components/BottomNavigation/BottomNavigation.js +11 -3
  3. package/lib/commonjs/components/DatePicker/DatePicker.js +18 -12
  4. package/lib/commonjs/components/DateRangePicker/DateRangePicker.js +14 -9
  5. package/lib/commonjs/components/FloatingActionButton/FloatingActionButton.js +1 -1
  6. package/lib/commonjs/components/Input/Input.js +1 -1
  7. package/lib/commonjs/components/Modal/Modal.js +4 -4
  8. package/lib/commonjs/components/OTPInput/OTPInput.js +29 -9
  9. package/lib/commonjs/components/ProgressBar/ProgressBar.js +1 -1
  10. package/lib/commonjs/components/SearchBar/SearchBar.js +6 -1
  11. package/lib/commonjs/components/SegmentedControl/SegmentedControl.js +23 -28
  12. package/lib/commonjs/components/Skeleton/Skeleton.js +1 -1
  13. package/lib/commonjs/components/Slider/Slider.js +11 -11
  14. package/lib/commonjs/components/Stepper/Stepper.js +10 -4
  15. package/lib/commonjs/components/Tabs/Tabs.js +7 -5
  16. package/lib/commonjs/components/TimePicker/TimePicker.js +3 -3
  17. package/lib/commonjs/components/Toast/Toast.js +2 -2
  18. package/lib/commonjs/theme/animatedValue.js +20 -1
  19. package/lib/commonjs/theme/index.js +8 -1
  20. package/lib/module/components/Badge/Badge.js +2 -2
  21. package/lib/module/components/BottomNavigation/BottomNavigation.js +11 -3
  22. package/lib/module/components/DatePicker/DatePicker.js +19 -13
  23. package/lib/module/components/DateRangePicker/DateRangePicker.js +15 -10
  24. package/lib/module/components/FloatingActionButton/FloatingActionButton.js +2 -2
  25. package/lib/module/components/Input/Input.js +2 -2
  26. package/lib/module/components/Modal/Modal.js +5 -5
  27. package/lib/module/components/OTPInput/OTPInput.js +30 -10
  28. package/lib/module/components/ProgressBar/ProgressBar.js +2 -2
  29. package/lib/module/components/SearchBar/SearchBar.js +6 -1
  30. package/lib/module/components/SegmentedControl/SegmentedControl.js +24 -29
  31. package/lib/module/components/Skeleton/Skeleton.js +2 -2
  32. package/lib/module/components/Slider/Slider.js +12 -12
  33. package/lib/module/components/Stepper/Stepper.js +10 -4
  34. package/lib/module/components/Tabs/Tabs.js +7 -5
  35. package/lib/module/components/TimePicker/TimePicker.js +4 -4
  36. package/lib/module/components/Toast/Toast.js +3 -3
  37. package/lib/module/theme/animatedValue.js +18 -0
  38. package/lib/module/theme/index.js +1 -1
  39. package/lib/typescript/commonjs/components/BottomNavigation/BottomNavigation.d.ts +7 -0
  40. package/lib/typescript/commonjs/components/SearchBar/SearchBar.d.ts +2 -1
  41. package/lib/typescript/commonjs/theme/animatedValue.d.ts +11 -0
  42. package/lib/typescript/commonjs/theme/index.d.ts +1 -1
  43. package/lib/typescript/module/components/BottomNavigation/BottomNavigation.d.ts +7 -0
  44. package/lib/typescript/module/components/SearchBar/SearchBar.d.ts +2 -1
  45. package/lib/typescript/module/theme/animatedValue.d.ts +11 -0
  46. package/lib/typescript/module/theme/index.d.ts +1 -1
  47. package/package.json +1 -1
@@ -131,7 +131,7 @@ const Badge = exports.Badge = /*#__PURE__*/(0, _react.forwardRef)((props, ref) =
131
131
  const pulseScale = (0, _react.useRef)((0, _index.createAnimatedValue)(1)).current;
132
132
  (0, _react.useEffect)(() => {
133
133
  if (!pulse || !shouldRender) {
134
- pulseScale.setValue(1);
134
+ (0, _index.setNativeValue)(pulseScale, 1);
135
135
  return;
136
136
  }
137
137
  const loop = _reactNative.Animated.loop(_reactNative.Animated.sequence([_reactNative.Animated.timing(pulseScale, {
@@ -53,6 +53,7 @@ const BottomNavigation = exports.BottomNavigation = /*#__PURE__*/(0, _react.forw
53
53
  haptic = 'selection',
54
54
  showLabels = true,
55
55
  variant = 'pill',
56
+ indicatorPosition = 'bottom',
56
57
  style,
57
58
  indicatorStyle,
58
59
  labelStyle,
@@ -198,7 +199,7 @@ const BottomNavigation = exports.BottomNavigation = /*#__PURE__*/(0, _react.forw
198
199
  }, tab.key);
199
200
  }), variant === 'underline' && tabWidth > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
200
201
  pointerEvents: "none",
201
- style: [styles.underline, {
202
+ style: [styles.underline, indicatorPosition === 'top' ? styles.underlineTop : styles.underlineBottom, {
202
203
  width: tabWidth,
203
204
  backgroundColor: theme.colors.primary,
204
205
  transform: [{
@@ -248,12 +249,19 @@ const buildStyles = theme => _reactNative.StyleSheet.create({
248
249
  },
249
250
  underline: {
250
251
  position: 'absolute',
251
- bottom: -6,
252
252
  left: 0,
253
- height: UNDERLINE_HEIGHT,
253
+ height: UNDERLINE_HEIGHT
254
+ },
255
+ underlineBottom: {
256
+ bottom: -6,
254
257
  borderTopLeftRadius: 2,
255
258
  borderTopRightRadius: 2
256
259
  },
260
+ underlineTop: {
261
+ top: -6,
262
+ borderBottomLeftRadius: 2,
263
+ borderBottomRightRadius: 2
264
+ },
257
265
  badgePill: {
258
266
  position: 'absolute',
259
267
  top: -4,
@@ -135,8 +135,8 @@ const DatePicker = props => {
135
135
  (0, _react.useEffect)(() => {
136
136
  if (mode !== 'modal') return;
137
137
  if (visible) {
138
- backdrop.setValue(0);
139
- sheet.setValue(0);
138
+ (0, _index.setNativeValue)(backdrop, 0);
139
+ (0, _index.setNativeValue)(sheet, 0);
140
140
  _reactNative.Animated.parallel([_reactNative.Animated.timing(backdrop, {
141
141
  toValue: 1,
142
142
  duration: theme.motion.duration.normal,
@@ -248,8 +248,8 @@ const DatePicker = props => {
248
248
 
249
249
  // View-mode transition: fade + scale (200ms, native driver).
250
250
  const animateViewTransition = (0, _react.useCallback)(() => {
251
- viewFade.setValue(0);
252
- viewScale.setValue(0.9);
251
+ (0, _index.setNativeValue)(viewFade, 0);
252
+ (0, _index.setNativeValue)(viewScale, 0.9);
253
253
  _reactNative.Animated.parallel([_reactNative.Animated.timing(viewFade, {
254
254
  toValue: 1,
255
255
  duration: 200,
@@ -300,7 +300,7 @@ const DatePicker = props => {
300
300
  if (!cell.inMonth) {
301
301
  setAnchor(new Date(cell.date.getFullYear(), cell.date.getMonth(), 1));
302
302
  }
303
- selectScale.setValue(0.7);
303
+ (0, _index.setNativeValue)(selectScale, 0.7);
304
304
  _reactNative.Animated.spring(selectScale, {
305
305
  toValue: 1,
306
306
  damping: theme.motion.spring.bouncy.damping,
@@ -382,11 +382,16 @@ const DatePicker = props => {
382
382
  opacity: viewMode === 'days' ? monthFade : 1
383
383
  }],
384
384
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.Text, {
385
- style: [styles.headerLabel, viewMode === 'days' ? {
385
+ style: [styles.headerLabel,
386
+ // Always include the transform — passing null/undefined as a
387
+ // transform value confuses Animated's processTransform on Fabric.
388
+ // When the month slide isn't active we still drive translateX
389
+ // through the same value, just with target 0.
390
+ {
386
391
  transform: [{
387
- translateX: monthSlide
392
+ translateX: viewMode === 'days' ? monthSlide : 0
388
393
  }]
389
- } : null],
394
+ }],
390
395
  accessibilityLiveRegion: "polite",
391
396
  children: headerLabel
392
397
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
@@ -452,11 +457,12 @@ const DatePicker = props => {
452
457
  backgroundColor: cellBg,
453
458
  borderColor,
454
459
  borderWidth: todayCell && !selected ? 1.5 : 0,
455
- opacity,
456
- transform: selected ? [{
460
+ opacity
461
+ }, selected ? {
462
+ transform: [{
457
463
  scale: selectScale
458
- }] : undefined
459
- }],
464
+ }]
465
+ } : null],
460
466
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
461
467
  style: [styles.dayText, {
462
468
  color: textColor
@@ -120,8 +120,8 @@ const DateRangePicker = exports.DateRangePicker = /*#__PURE__*/(0, _react.forwar
120
120
  // Modal open / close animation.
121
121
  (0, _react.useEffect)(() => {
122
122
  if (visible) {
123
- backdrop.setValue(0);
124
- sheet.setValue(0);
123
+ (0, _index.setNativeValue)(backdrop, 0);
124
+ (0, _index.setNativeValue)(sheet, 0);
125
125
  _reactNative.Animated.parallel([_reactNative.Animated.timing(backdrop, {
126
126
  toValue: 1,
127
127
  duration: theme.motion.duration.normal,
@@ -561,26 +561,31 @@ const buildStyles = theme => {
561
561
  aspectRatio: 1,
562
562
  alignItems: 'center',
563
563
  justifyContent: 'center',
564
- padding: 2,
564
+ // No horizontal padding so the connector bars on adjacent cells meet
565
+ // without a gap. Vertical breathing room comes from dayInner's height.
566
+ padding: 0,
565
567
  position: 'relative'
566
568
  },
569
+ // Bars sit BEHIND dayInner and bridge the start/end circles to mid-range
570
+ // cells. Their height mirrors dayInner (~90% of cell height) so the visual
571
+ // pill is one continuous shape across multiple cells.
567
572
  barLeft: {
568
573
  position: 'absolute',
569
574
  left: 0,
570
575
  right: '50%',
571
- top: '15%',
572
- bottom: '15%'
576
+ top: '5%',
577
+ bottom: '5%'
573
578
  },
574
579
  barRight: {
575
580
  position: 'absolute',
576
581
  left: '50%',
577
582
  right: 0,
578
- top: '15%',
579
- bottom: '15%'
583
+ top: '5%',
584
+ bottom: '5%'
580
585
  },
581
586
  dayInner: {
582
- width: '100%',
583
- height: '100%',
587
+ width: '90%',
588
+ height: '90%',
584
589
  alignItems: 'center',
585
590
  justifyContent: 'center'
586
591
  },
@@ -86,7 +86,7 @@ const FloatingActionButton = exports.FloatingActionButton = /*#__PURE__*/(0, _re
86
86
  const hideAnim = (0, _react.useRef)((0, _index.createAnimatedValue)(0)).current;
87
87
  (0, _react.useEffect)(() => {
88
88
  if (!hideOnScroll) {
89
- hideAnim.setValue(0);
89
+ (0, _index.setNativeValue)(hideAnim, 0);
90
90
  return;
91
91
  }
92
92
  _reactNative.Animated.timing(hideAnim, {
@@ -147,7 +147,7 @@ const Input = exports.Input = /*#__PURE__*/(0, _react.forwardRef)((props, ref) =
147
147
  const prevErrorRef = (0, _react.useRef)(hasError);
148
148
  (0, _react.useEffect)(() => {
149
149
  if (hasError && !prevErrorRef.current) {
150
- shakeAnim.setValue(0);
150
+ (0, _index.setNativeValue)(shakeAnim, 0);
151
151
  _reactNative.Animated.sequence([_reactNative.Animated.timing(shakeAnim, {
152
152
  toValue: 1,
153
153
  duration: 50,
@@ -65,10 +65,10 @@ const Modal = exports.Modal = /*#__PURE__*/(0, _react.forwardRef)((props, ref) =
65
65
  })]).start();
66
66
  }
67
67
  } else {
68
- backdropAnim.setValue(0);
69
- scaleAnim.setValue(0.9);
70
- opacityAnim.setValue(0);
71
- translateYAnim.setValue(screenHeight);
68
+ (0, _index.setNativeValue)(backdropAnim, 0);
69
+ (0, _index.setNativeValue)(scaleAnim, 0.9);
70
+ (0, _index.setNativeValue)(opacityAnim, 0);
71
+ (0, _index.setNativeValue)(translateYAnim, screenHeight);
72
72
  }
73
73
  }, [visible, presentation, duration, backdropAnim, scaleAnim, opacityAnim, translateYAnim, screenHeight, theme.motion.spring.gentle.damping, theme.motion.spring.gentle.stiffness, theme.motion.spring.gentle.mass]);
74
74
 
@@ -89,7 +89,7 @@ const OTPInput = exports.OTPInput = /*#__PURE__*/(0, _react.forwardRef)((props,
89
89
  const isFirstRun = previousErrorRef.current === null;
90
90
  if (!isFirstRun && hasError && !previousErrorRef.current) {
91
91
  (0, _index2.triggerHaptic)('notificationError');
92
- shake.setValue(0);
92
+ (0, _index.setNativeValue)(shake, 0);
93
93
  _reactNative.Animated.sequence([_reactNative.Animated.timing(shake, {
94
94
  toValue: 1,
95
95
  duration: 75,
@@ -175,16 +175,27 @@ const OTPInput = exports.OTPInput = /*#__PURE__*/(0, _react.forwardRef)((props,
175
175
  }
176
176
  }, [length, onChange, onComplete, value]);
177
177
  const handleChangeText = (0, _react.useCallback)((index, raw) => {
178
- const sanitized = sanitizeChar(raw, keyboardType);
178
+ // Strip the ZWSP placeholder (used so iOS fires onKeyPress/Backspace on otherwise-empty cells).
179
+ const stripped = raw.replace(/\u200B/g, '');
180
+ const sanitized = sanitizeChar(stripped, keyboardType);
179
181
  if (!sanitized) {
180
- // User cleared this cell explicitly via change (rare since we manage backspace via key-press).
182
+ // Cell was cleared (delete / cut). Clear this cell and step focus back.
181
183
  const chars = cells.slice();
184
+ const wasFilled = (chars[index] ?? '').length > 0;
182
185
  chars[index] = '';
183
186
  updateValue(chars.join('').slice(0, length));
187
+ if (!wasFilled && index > 0) {
188
+ // Empty cell + backspace → also clear and focus the previous cell.
189
+ const prev = cells.slice();
190
+ prev[index - 1] = '';
191
+ updateValue(prev.join(''));
192
+ focusCell(index - 1);
193
+ }
184
194
  return;
185
195
  }
186
196
 
187
197
  // Fill cells from current index onward (handles paste of multi-char text).
198
+ // For single-character typing, this overwrites the current cell and advances.
188
199
  const chars = cells.slice();
189
200
  let writeIndex = index;
190
201
  for (let i = 0; i < sanitized.length && writeIndex < length; i += 1) {
@@ -201,7 +212,6 @@ const OTPInput = exports.OTPInput = /*#__PURE__*/(0, _react.forwardRef)((props,
201
212
  // Move focus to the next empty cell or last cell.
202
213
  const nextFocus = Math.min(writeIndex, length - 1);
203
214
  if (writeIndex >= length) {
204
- // All filled — blur last cell.
205
215
  inputsRef.current[length - 1]?.blur();
206
216
  } else {
207
217
  focusCell(nextFocus);
@@ -210,9 +220,12 @@ const OTPInput = exports.OTPInput = /*#__PURE__*/(0, _react.forwardRef)((props,
210
220
  const handleKeyPress = (0, _react.useCallback)((index, e) => {
211
221
  const key = e.nativeEvent.key;
212
222
  if (key !== 'Backspace') return;
223
+ // Backspace on a non-empty cell — clear it. Backspace on an empty cell —
224
+ // step back and clear the previous cell. handleChangeText also covers the
225
+ // empty-cell case via the ZWSP placeholder, so this branch only matters
226
+ // when the platform fires onKeyPress without firing onChangeText.
213
227
  const current = cells[index] ?? '';
214
228
  if (current.length === 0) {
215
- // Move back, clear previous cell.
216
229
  if (index > 0) {
217
230
  const chars = cells.slice();
218
231
  chars[index - 1] = '';
@@ -288,8 +301,11 @@ const OTPInput = exports.OTPInput = /*#__PURE__*/(0, _react.forwardRef)((props,
288
301
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
289
302
  ref: node => {
290
303
  inputsRef.current[index] = node;
291
- },
292
- value: secure && isFilled ? '' : char,
304
+ }
305
+ // Always render at least the ZWSP placeholder so iOS keeps
306
+ // firing onChangeText/onKeyPress for Backspace on empty cells.
307
+ ,
308
+ value: (secure && isFilled ? '' : char) || '\u200B',
293
309
  onChangeText: t => handleChangeText(index, t),
294
310
  onKeyPress: e => handleKeyPress(index, e),
295
311
  onFocus: () => handleFocus(index),
@@ -297,8 +313,12 @@ const OTPInput = exports.OTPInput = /*#__PURE__*/(0, _react.forwardRef)((props,
297
313
  keyboardType: keyboardType,
298
314
  editable: !disabled,
299
315
  selectTextOnFocus: true,
300
- caretHidden: isFilled,
301
- maxLength: length,
316
+ caretHidden: isFilled
317
+ // Only the first cell accepts paste-like multi-char input
318
+ // (e.g., when SMS autofill or clipboard delivers the full code);
319
+ // every other cell is single-char so typing always overwrites.
320
+ ,
321
+ maxLength: index === 0 ? length : 2,
302
322
  textContentType: index === 0 ? 'oneTimeCode' : 'none',
303
323
  autoComplete: index === 0 ? 'sms-otp' : 'off',
304
324
  importantForAutofill: index === 0 ? 'yes' : 'no',
@@ -67,7 +67,7 @@ const ProgressBar = exports.ProgressBar = /*#__PURE__*/(0, _react.forwardRef)((p
67
67
  }, [animated, clamped, fillAnim, isIndeterminate, theme]);
68
68
  (0, _react.useEffect)(() => {
69
69
  if (!isIndeterminate) return;
70
- loopAnim.setValue(0);
70
+ (0, _index.setNativeValue)(loopAnim, 0);
71
71
  const animation = _reactNative.Animated.loop(_reactNative.Animated.timing(loopAnim, {
72
72
  toValue: 1,
73
73
  duration: 1500,
@@ -38,7 +38,7 @@ const sizeMap = {
38
38
  const CANCEL_WIDTH = 72;
39
39
  const SearchBar = exports.SearchBar = /*#__PURE__*/(0, _react.forwardRef)((props, ref) => {
40
40
  const {
41
- value,
41
+ value: rawValue,
42
42
  onChangeText,
43
43
  onSubmit,
44
44
  placeholder = 'Search…',
@@ -64,6 +64,11 @@ const SearchBar = exports.SearchBar = /*#__PURE__*/(0, _react.forwardRef)((props
64
64
  };
65
65
  const cancelWidth = theme.components.searchBar?.cancelButtonWidth ?? CANCEL_WIDTH;
66
66
  const styles = (0, _react.useMemo)(() => buildStyles(theme), [theme]);
67
+
68
+ // Coerce nullable inputs to '' once at the top so every downstream read is
69
+ // a guaranteed string. Required when consumers pass `value={state}` where
70
+ // `state` may be `undefined` / `null` before being initialised.
71
+ const value = typeof rawValue === 'string' ? rawValue : '';
67
72
  const [isFocused, setIsFocused] = (0, _react.useState)(false);
68
73
  const [internalValue, setInternalValue] = (0, _react.useState)(value);
69
74
  const debouncedValue = (0, _useDebounce.useDebounce)(internalValue, debounceMs ?? 0);
@@ -50,44 +50,39 @@ const SegmentedControl = exports.SegmentedControl = /*#__PURE__*/(0, _react.forw
50
50
  const styles = (0, _react.useMemo)(() => buildStyles(theme), [theme]);
51
51
  const sizeStyles = sizeMap[size];
52
52
 
53
- // Total track width measured from onLayout; thumb width = trackWidth / segments.length.
54
- const trackWidthRef = (0, _react.useRef)(0);
53
+ // Track width is measured from onLayout. Thumb width is a regular number (not
54
+ // animated) `width` cannot be driven by the native animated module, and mixing
55
+ // a JS-driven width with a native-driven translateX inside `Animated.parallel`
56
+ // sends the parallel composite into the native driver and JS setValue/timing
57
+ // calls on the same value then throw "moved to native". Keep width as state and
58
+ // only animate translateX via the native driver.
59
+ const [trackWidth, setTrackWidth] = (0, _react.useState)(0);
55
60
  const thumbTranslateX = (0, _react.useRef)((0, _index.createAnimatedValue)(0)).current;
56
- const thumbWidth = (0, _react.useRef)((0, _index.createAnimatedValue)(0)).current;
57
61
  const activeIndex = Math.max(0, segments.findIndex(s => s.value === value));
58
- const animateThumb = (0, _react.useCallback)((index, totalWidth) => {
59
- if (totalWidth <= 0 || segments.length === 0) return;
60
- const segWidth = (totalWidth - TRACK_PADDING * 2) / segments.length;
62
+ const segmentWidth = trackWidth > 0 ? (trackWidth - TRACK_PADDING * 2) / Math.max(segments.length, 1) : 0;
63
+ const animateThumb = (0, _react.useCallback)((index, segWidth) => {
64
+ if (segWidth <= 0) return;
61
65
  const targetX = TRACK_PADDING + segWidth * index;
62
66
  const spring = theme.motion.spring.snappy;
63
- _reactNative.Animated.parallel([_reactNative.Animated.spring(thumbTranslateX, {
67
+ _reactNative.Animated.spring(thumbTranslateX, {
64
68
  toValue: targetX,
65
69
  damping: spring.damping,
66
70
  stiffness: spring.stiffness,
67
71
  mass: spring.mass,
68
72
  useNativeDriver: true
69
- }), _reactNative.Animated.spring(thumbWidth, {
70
- toValue: segWidth,
71
- damping: spring.damping,
72
- stiffness: spring.stiffness,
73
- mass: spring.mass,
74
- useNativeDriver: false
75
- })]).start();
76
- }, [segments.length, theme.motion.spring.snappy, thumbTranslateX, thumbWidth]);
73
+ }).start();
74
+ }, [theme.motion.spring.snappy, thumbTranslateX]);
77
75
  (0, _react.useEffect)(() => {
78
- if (trackWidthRef.current > 0) {
79
- animateThumb(activeIndex, trackWidthRef.current);
76
+ if (segmentWidth > 0) {
77
+ animateThumb(activeIndex, segmentWidth);
80
78
  }
81
- }, [activeIndex, animateThumb]);
79
+ }, [activeIndex, segmentWidth, animateThumb]);
82
80
  const handleTrackLayout = (0, _react.useCallback)(e => {
83
81
  const width = e.nativeEvent.layout.width;
84
- if (width === trackWidthRef.current) return;
85
- trackWidthRef.current = width;
86
- // Initial-position the thumb without animation jitter — set then animate to active.
87
- const segWidth = (width - TRACK_PADDING * 2) / Math.max(segments.length, 1);
88
- thumbWidth.setValue(segWidth);
89
- thumbTranslateX.setValue(TRACK_PADDING + segWidth * activeIndex);
90
- }, [activeIndex, segments.length, thumbTranslateX, thumbWidth]);
82
+ if (width !== trackWidth) {
83
+ setTrackWidth(width);
84
+ }
85
+ }, [trackWidth]);
91
86
  const handlePress = (0, _react.useCallback)(segment => {
92
87
  if (disabled) return;
93
88
  if (segment.value === value) return;
@@ -110,10 +105,10 @@ const SegmentedControl = exports.SegmentedControl = /*#__PURE__*/(0, _react.forw
110
105
  alignSelf: fullWidth ? 'stretch' : 'flex-start'
111
106
  }, style],
112
107
  onLayout: handleTrackLayout,
113
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
108
+ children: [segmentWidth > 0 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Animated.View, {
114
109
  pointerEvents: "none",
115
110
  style: [styles.thumb, {
116
- width: thumbWidth,
111
+ width: segmentWidth,
117
112
  height: sizeStyles.height - TRACK_PADDING * 2,
118
113
  borderRadius: theme.radius.sm,
119
114
  backgroundColor: thumbBg,
@@ -122,7 +117,7 @@ const SegmentedControl = exports.SegmentedControl = /*#__PURE__*/(0, _react.forw
122
117
  }],
123
118
  ...theme.shadows.sm
124
119
  }]
125
- }), segments.map(segment => {
120
+ }) : null, segments.map(segment => {
126
121
  const isActive = segment.value === value;
127
122
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Pressable, {
128
123
  onPress: () => handlePress(segment),
@@ -55,7 +55,7 @@ const Skeleton = ({
55
55
  const progress = (0, _react.useRef)((0, _index.createAnimatedValue)(0)).current;
56
56
  const [containerWidth, setContainerWidth] = (0, _react.useState)(0);
57
57
  (0, _react.useEffect)(() => {
58
- progress.setValue(0);
58
+ (0, _index.setNativeValue)(progress, 0);
59
59
  const duration = resolvedVariant === 'pulse' ? PULSE_DURATION : SPEED_DURATION[resolvedSpeed];
60
60
  const animation = resolvedVariant === 'pulse' ? _reactNative.Animated.loop(_reactNative.Animated.sequence([_reactNative.Animated.timing(progress, {
61
61
  toValue: 1,
@@ -110,14 +110,14 @@ const Slider = exports.Slider = /*#__PURE__*/(0, _react.forwardRef)((props, ref)
110
110
  highRef.current = hi;
111
111
  lastReportedLow.current = lo;
112
112
  lastReportedHigh.current = hi;
113
- lowX.setValue(valueToPx(lo, trackWidth));
114
- highX.setValue(valueToPx(hi, trackWidth));
113
+ (0, _index.setNativeValue)(lowX, valueToPx(lo, trackWidth));
114
+ (0, _index.setNativeValue)(highX, valueToPx(hi, trackWidth));
115
115
  } else {
116
116
  const v = props.value;
117
117
  lowRef.current = v;
118
118
  highRef.current = v;
119
119
  lastReportedLow.current = v;
120
- lowX.setValue(valueToPx(v, trackWidth));
120
+ (0, _index.setNativeValue)(lowX, valueToPx(v, trackWidth));
121
121
  }
122
122
  // We intentionally listen to props.value across both shapes via JSON; ESLint is fine.
123
123
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -130,10 +130,10 @@ const Slider = exports.Slider = /*#__PURE__*/(0, _react.forwardRef)((props, ref)
130
130
  const [v0, v1] = props.value;
131
131
  const lo = Math.min(v0, v1);
132
132
  const hi = Math.max(v0, v1);
133
- lowX.setValue(valueToPx(lo, w));
134
- highX.setValue(valueToPx(hi, w));
133
+ (0, _index.setNativeValue)(lowX, valueToPx(lo, w));
134
+ (0, _index.setNativeValue)(highX, valueToPx(hi, w));
135
135
  } else {
136
- lowX.setValue(valueToPx(props.value, w));
136
+ (0, _index.setNativeValue)(lowX, valueToPx(props.value, w));
137
137
  }
138
138
  },
139
139
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -184,7 +184,7 @@ const Slider = exports.Slider = /*#__PURE__*/(0, _react.forwardRef)((props, ref)
184
184
  lastReportedLow.current = nextValue;
185
185
  }
186
186
  lowRef.current = nextValue;
187
- lowX.setValue(valueToPx(nextValue, trackWidth));
187
+ (0, _index.setNativeValue)(lowX, valueToPx(nextValue, trackWidth));
188
188
  fireChange(nextValue, highRef.current);
189
189
  } else {
190
190
  if (isRange && nextValue < lowRef.current) nextValue = lowRef.current;
@@ -193,7 +193,7 @@ const Slider = exports.Slider = /*#__PURE__*/(0, _react.forwardRef)((props, ref)
193
193
  lastReportedHigh.current = nextValue;
194
194
  }
195
195
  highRef.current = nextValue;
196
- highX.setValue(valueToPx(nextValue, trackWidth));
196
+ (0, _index.setNativeValue)(highX, valueToPx(nextValue, trackWidth));
197
197
  fireChange(lowRef.current, nextValue);
198
198
  }
199
199
  },
@@ -223,7 +223,7 @@ const Slider = exports.Slider = /*#__PURE__*/(0, _react.forwardRef)((props, ref)
223
223
  toValue: valueToPx(next, trackWidth),
224
224
  duration: theme.motion.duration.fast,
225
225
  easing: _reactNative.Easing.out(_reactNative.Easing.cubic),
226
- useNativeDriver: false
226
+ useNativeDriver: true
227
227
  }).start();
228
228
  fireChange(next, next);
229
229
  }
@@ -238,7 +238,7 @@ const Slider = exports.Slider = /*#__PURE__*/(0, _react.forwardRef)((props, ref)
238
238
  if (nextLow !== lowRef.current) {
239
239
  lowRef.current = nextLow;
240
240
  lastReportedLow.current = nextLow;
241
- lowX.setValue(valueToPx(nextLow, trackWidth));
241
+ (0, _index.setNativeValue)(lowX, valueToPx(nextLow, trackWidth));
242
242
  (0, _index2.triggerHaptic)('selection');
243
243
  fireChange(nextLow, highRef.current);
244
244
  }
@@ -247,7 +247,7 @@ const Slider = exports.Slider = /*#__PURE__*/(0, _react.forwardRef)((props, ref)
247
247
  if (next !== lowRef.current) {
248
248
  lowRef.current = next;
249
249
  lastReportedLow.current = next;
250
- lowX.setValue(valueToPx(next, trackWidth));
250
+ (0, _index.setNativeValue)(lowX, valueToPx(next, trackWidth));
251
251
  (0, _index2.triggerHaptic)('selection');
252
252
  fireChange(next, next);
253
253
  }
@@ -28,6 +28,7 @@ const StepCircle = ({
28
28
  status,
29
29
  toneColor,
30
30
  upcomingColor,
31
+ upcomingFillColor,
31
32
  textOnColor,
32
33
  upcomingTextColor,
33
34
  interactive,
@@ -70,7 +71,7 @@ const StepCircle = ({
70
71
  loop.start();
71
72
  return () => loop.stop();
72
73
  }, [isActive, pulseScale, pulseOpacity]);
73
- const bgColor = status === 'upcoming' ? 'transparent' : toneColor;
74
+ const bgColor = status === 'upcoming' ? upcomingFillColor : toneColor;
74
75
  const borderColor = status === 'upcoming' ? upcomingColor : toneColor;
75
76
  const textColor = status === 'upcoming' ? upcomingTextColor : textOnColor;
76
77
  const isComplete = status === 'complete';
@@ -174,6 +175,7 @@ const Stepper = exports.Stepper = /*#__PURE__*/(0, _react.forwardRef)((props, re
174
175
  const styles = (0, _react.useMemo)(() => buildStyles(theme), [theme]);
175
176
  const toneColor = toneColorFor(theme, tone);
176
177
  const upcomingColor = theme.colors.border.primary;
178
+ const upcomingFillColor = theme.colors.background.primary;
177
179
  const upcomingTextColor = theme.colors.text.secondary;
178
180
  const textOnColor = theme.colors.text.inverse;
179
181
  const interactive = typeof onStepPress === 'function';
@@ -203,11 +205,15 @@ const Stepper = exports.Stepper = /*#__PURE__*/(0, _react.forwardRef)((props, re
203
205
  outputRange: ['0%', '100%']
204
206
  });
205
207
  const a11yLabel = accessibilityLabel ?? `Progress: step ${activeStep + 1} of ${total}`;
208
+ const halfStepPct = total > 0 ? `${50 / total}%` : `0%`;
206
209
  const renderHorizontal = () => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
207
210
  style: styles.hRow,
208
211
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
209
212
  pointerEvents: "none",
210
- style: styles.hConnectorContainer,
213
+ style: [styles.hConnectorContainer, {
214
+ left: halfStepPct,
215
+ right: halfStepPct
216
+ }],
211
217
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
212
218
  style: [styles.hConnectorTrack, {
213
219
  backgroundColor: upcomingColor
@@ -228,6 +234,7 @@ const Stepper = exports.Stepper = /*#__PURE__*/(0, _react.forwardRef)((props, re
228
234
  status: status,
229
235
  toneColor: toneColor,
230
236
  upcomingColor: upcomingColor,
237
+ upcomingFillColor: upcomingFillColor,
231
238
  onPressColor: toneColor,
232
239
  textOnColor: textOnColor,
233
240
  upcomingTextColor: upcomingTextColor,
@@ -260,6 +267,7 @@ const Stepper = exports.Stepper = /*#__PURE__*/(0, _react.forwardRef)((props, re
260
267
  status: status,
261
268
  toneColor: toneColor,
262
269
  upcomingColor: upcomingColor,
270
+ upcomingFillColor: upcomingFillColor,
263
271
  onPressColor: toneColor,
264
272
  textOnColor: textOnColor,
265
273
  upcomingTextColor: upcomingTextColor,
@@ -338,8 +346,6 @@ const buildStyles = theme => _reactNative.StyleSheet.create({
338
346
  hConnectorContainer: {
339
347
  position: 'absolute',
340
348
  top: CIRCLE_SIZE / 2 - 1,
341
- left: CIRCLE_SIZE / 2,
342
- right: CIRCLE_SIZE / 2,
343
349
  height: 2,
344
350
  flexDirection: 'row',
345
351
  alignItems: 'center'
@@ -47,15 +47,17 @@ const Tabs = exports.Tabs = /*#__PURE__*/(0, _react.forwardRef)((props, ref) =>
47
47
  (0, _react.useEffect)(() => {
48
48
  if (!activeLayout) return;
49
49
  const spring = theme.motion.spring.snappy;
50
- _reactNative.Animated.parallel([_reactNative.Animated.spring(indicatorTranslateX, {
50
+ _reactNative.Animated.parallel([
51
+ // Both must use the JS driver: width can't run on native, and mixing
52
+ // drivers on the same view (transform native + width JS) trips RN's
53
+ // "node already moved to native" guard under the new architecture.
54
+ _reactNative.Animated.spring(indicatorTranslateX, {
51
55
  toValue: activeLayout.x,
52
56
  damping: spring.damping,
53
57
  stiffness: spring.stiffness,
54
58
  mass: spring.mass,
55
- useNativeDriver: true
56
- }),
57
- // width is a layout prop — must run on JS thread (useNativeDriver: false).
58
- _reactNative.Animated.spring(indicatorWidth, {
59
+ useNativeDriver: false
60
+ }), _reactNative.Animated.spring(indicatorWidth, {
59
61
  toValue: activeLayout.width,
60
62
  damping: spring.damping,
61
63
  stiffness: spring.stiffness,
@@ -74,7 +74,7 @@ const Wheel = ({
74
74
  if (lastIndexRef.current !== selectedIndex) {
75
75
  lastIndexRef.current = selectedIndex;
76
76
  const offset = selectedIndex * ITEM_HEIGHT;
77
- scrollY.setValue(offset);
77
+ (0, _index.setNativeValue)(scrollY, offset);
78
78
  // Defer to next frame so FlatList has measured.
79
79
  requestAnimationFrame(() => {
80
80
  listRef.current?.scrollToOffset({
@@ -281,8 +281,8 @@ const TimePicker = ({
281
281
  useNativeDriver: true
282
282
  })]).start();
283
283
  } else {
284
- opacity.setValue(0);
285
- translateY.setValue(40);
284
+ (0, _index.setNativeValue)(opacity, 0);
285
+ (0, _index.setNativeValue)(translateY, 40);
286
286
  }
287
287
  }, [visible, opacity, translateY, theme.motion]);
288
288
  const announce = (0, _react.useCallback)(msg => {
@@ -73,9 +73,9 @@ const Toast = ({
73
73
  const panResponder = (0, _react.useMemo)(() => _reactNative.PanResponder.create({
74
74
  onMoveShouldSetPanResponder: (_evt, gesture) => Math.abs(gesture.dx) > Math.abs(gesture.dy) && Math.abs(gesture.dx) > 6,
75
75
  onPanResponderMove: (_evt, gesture) => {
76
- translateX.setValue(gesture.dx);
76
+ (0, _index.setNativeValue)(translateX, gesture.dx);
77
77
  const fade = 1 - Math.min(Math.abs(gesture.dx) / 200, 1) * 0.7;
78
- opacity.setValue(fade);
78
+ (0, _index.setNativeValue)(opacity, fade);
79
79
  },
80
80
  onPanResponderRelease: (_evt, gesture) => {
81
81
  if (Math.abs(gesture.dx) > SWIPE_DISMISS_THRESHOLD || Math.abs(gesture.vx) > SWIPE_VELOCITY_THRESHOLD) {