@webority-technologies/mobile 0.0.22 → 0.0.23

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 (181) hide show
  1. package/lib/commonjs/components/Accordion/Accordion.js +4 -2
  2. package/lib/commonjs/components/Avatar/Avatar.js +4 -2
  3. package/lib/commonjs/components/Badge/Badge.js +5 -5
  4. package/lib/commonjs/components/Banner/Banner.js +8 -4
  5. package/lib/commonjs/components/BottomNavigation/BottomNavigation.js +6 -4
  6. package/lib/commonjs/components/BottomSheet/BottomSheet.js +8 -9
  7. package/lib/commonjs/components/Box/Box.js +162 -0
  8. package/lib/commonjs/components/Box/index.js +37 -0
  9. package/lib/commonjs/components/Button/Button.js +7 -7
  10. package/lib/commonjs/components/Carousel/Carousel.js +4 -2
  11. package/lib/commonjs/components/Checkbox/Checkbox.js +14 -5
  12. package/lib/commonjs/components/DatePicker/DatePicker.js +9 -7
  13. package/lib/commonjs/components/DateRangePicker/DateRangePicker.js +5 -2
  14. package/lib/commonjs/components/Dialog/Dialog.js +2 -2
  15. package/lib/commonjs/components/FieldBase/FieldBase.js +8 -4
  16. package/lib/commonjs/components/FloatingActionButton/FloatingActionButton.js +13 -5
  17. package/lib/commonjs/components/FormField/FormField.js +61 -25
  18. package/lib/commonjs/components/Input/Input.js +41 -29
  19. package/lib/commonjs/components/KeyboardAwareScrollView/KeyboardAwareScrollView.js +102 -0
  20. package/lib/commonjs/components/KeyboardAwareScrollView/index.js +13 -0
  21. package/lib/commonjs/components/KeyboardToolbar/KeyboardToolbar.js +130 -0
  22. package/lib/commonjs/components/KeyboardToolbar/index.js +13 -0
  23. package/lib/commonjs/components/Modal/Modal.js +17 -6
  24. package/lib/commonjs/components/NumberInput/NumberInput.js +35 -28
  25. package/lib/commonjs/components/OTPInput/OTPInput.js +33 -18
  26. package/lib/commonjs/components/Radio/Radio.js +7 -5
  27. package/lib/commonjs/components/Radio/RadioGroup.js +10 -3
  28. package/lib/commonjs/components/SearchBar/SearchBar.js +4 -2
  29. package/lib/commonjs/components/SegmentedControl/SegmentedControl.js +20 -10
  30. package/lib/commonjs/components/Select/Select.js +33 -32
  31. package/lib/commonjs/components/Skeleton/SkeletonContent.js +5 -2
  32. package/lib/commonjs/components/Slider/Slider.js +42 -26
  33. package/lib/commonjs/components/Spinner/Spinner.js +5 -5
  34. package/lib/commonjs/components/Switch/Switch.js +29 -16
  35. package/lib/commonjs/components/Tabs/Tabs.js +4 -2
  36. package/lib/commonjs/components/Text/Text.js +142 -0
  37. package/lib/commonjs/components/Text/index.js +13 -0
  38. package/lib/commonjs/components/TimePicker/TimePicker.js +10 -7
  39. package/lib/commonjs/components/Toast/Toast.js +22 -10
  40. package/lib/commonjs/components/Tooltip/Tooltip.js +6 -2
  41. package/lib/commonjs/components/index.js +135 -89
  42. package/lib/commonjs/form/FormContext.js +40 -0
  43. package/lib/commonjs/form/index.js +68 -0
  44. package/lib/commonjs/form/path.js +79 -0
  45. package/lib/commonjs/form/rules.js +67 -0
  46. package/lib/commonjs/form/types.js +2 -0
  47. package/lib/commonjs/form/useField.js +54 -0
  48. package/lib/commonjs/form/useForm.js +316 -0
  49. package/lib/commonjs/hooks/index.js +14 -0
  50. package/lib/commonjs/hooks/useControllableState.js +30 -0
  51. package/lib/commonjs/hooks/useReducedMotion.js +31 -0
  52. package/lib/commonjs/index.js +96 -11
  53. package/lib/commonjs/theme/ThemeContext.js +30 -2
  54. package/lib/commonjs/theme/tokens.js +12 -0
  55. package/lib/module/components/Accordion/Accordion.js +4 -2
  56. package/lib/module/components/Avatar/Avatar.js +4 -2
  57. package/lib/module/components/Badge/Badge.js +5 -5
  58. package/lib/module/components/Banner/Banner.js +8 -4
  59. package/lib/module/components/BottomNavigation/BottomNavigation.js +6 -4
  60. package/lib/module/components/BottomSheet/BottomSheet.js +8 -9
  61. package/lib/module/components/Box/Box.js +156 -0
  62. package/lib/module/components/Box/index.js +4 -0
  63. package/lib/module/components/Button/Button.js +7 -7
  64. package/lib/module/components/Carousel/Carousel.js +4 -2
  65. package/lib/module/components/Checkbox/Checkbox.js +14 -5
  66. package/lib/module/components/DatePicker/DatePicker.js +9 -7
  67. package/lib/module/components/DateRangePicker/DateRangePicker.js +5 -2
  68. package/lib/module/components/Dialog/Dialog.js +2 -2
  69. package/lib/module/components/FieldBase/FieldBase.js +8 -4
  70. package/lib/module/components/FloatingActionButton/FloatingActionButton.js +13 -5
  71. package/lib/module/components/FormField/FormField.js +62 -26
  72. package/lib/module/components/Input/Input.js +41 -29
  73. package/lib/module/components/KeyboardAwareScrollView/KeyboardAwareScrollView.js +98 -0
  74. package/lib/module/components/KeyboardAwareScrollView/index.js +4 -0
  75. package/lib/module/components/KeyboardToolbar/KeyboardToolbar.js +125 -0
  76. package/lib/module/components/KeyboardToolbar/index.js +4 -0
  77. package/lib/module/components/Modal/Modal.js +17 -6
  78. package/lib/module/components/NumberInput/NumberInput.js +30 -23
  79. package/lib/module/components/OTPInput/OTPInput.js +30 -15
  80. package/lib/module/components/Radio/Radio.js +7 -5
  81. package/lib/module/components/Radio/RadioGroup.js +10 -3
  82. package/lib/module/components/SearchBar/SearchBar.js +4 -2
  83. package/lib/module/components/SegmentedControl/SegmentedControl.js +20 -10
  84. package/lib/module/components/Select/Select.js +33 -32
  85. package/lib/module/components/Skeleton/SkeletonContent.js +5 -2
  86. package/lib/module/components/Slider/Slider.js +42 -26
  87. package/lib/module/components/Spinner/Spinner.js +5 -5
  88. package/lib/module/components/Switch/Switch.js +29 -16
  89. package/lib/module/components/Tabs/Tabs.js +4 -2
  90. package/lib/module/components/Text/Text.js +138 -0
  91. package/lib/module/components/Text/index.js +4 -0
  92. package/lib/module/components/TimePicker/TimePicker.js +10 -7
  93. package/lib/module/components/Toast/Toast.js +22 -10
  94. package/lib/module/components/Tooltip/Tooltip.js +6 -2
  95. package/lib/module/components/index.js +4 -0
  96. package/lib/module/form/FormContext.js +32 -0
  97. package/lib/module/form/index.js +12 -0
  98. package/lib/module/form/path.js +72 -0
  99. package/lib/module/form/rules.js +52 -0
  100. package/lib/module/form/types.js +2 -0
  101. package/lib/module/form/useField.js +49 -0
  102. package/lib/module/form/useForm.js +312 -0
  103. package/lib/module/hooks/index.js +2 -0
  104. package/lib/module/hooks/useControllableState.js +26 -0
  105. package/lib/module/hooks/useReducedMotion.js +27 -0
  106. package/lib/module/index.js +3 -1
  107. package/lib/module/theme/ThemeContext.js +30 -2
  108. package/lib/module/theme/tokens.js +12 -0
  109. package/lib/typescript/commonjs/components/BottomNavigation/BottomNavigation.d.ts +1 -1
  110. package/lib/typescript/commonjs/components/Box/Box.d.ts +60 -0
  111. package/lib/typescript/commonjs/components/Box/index.d.ts +3 -0
  112. package/lib/typescript/commonjs/components/Button/Button.d.ts +1 -1
  113. package/lib/typescript/commonjs/components/Checkbox/Checkbox.d.ts +3 -2
  114. package/lib/typescript/commonjs/components/DatePicker/DatePicker.d.ts +3 -3
  115. package/lib/typescript/commonjs/components/Dialog/Dialog.d.ts +2 -2
  116. package/lib/typescript/commonjs/components/FormField/FormField.d.ts +13 -2
  117. package/lib/typescript/commonjs/components/KeyboardAwareScrollView/KeyboardAwareScrollView.d.ts +20 -0
  118. package/lib/typescript/commonjs/components/KeyboardAwareScrollView/index.d.ts +3 -0
  119. package/lib/typescript/commonjs/components/KeyboardToolbar/KeyboardToolbar.d.ts +29 -0
  120. package/lib/typescript/commonjs/components/KeyboardToolbar/index.d.ts +3 -0
  121. package/lib/typescript/commonjs/components/NumberInput/NumberInput.d.ts +3 -2
  122. package/lib/typescript/commonjs/components/OTPInput/OTPInput.d.ts +3 -2
  123. package/lib/typescript/commonjs/components/Radio/Radio.d.ts +2 -2
  124. package/lib/typescript/commonjs/components/Radio/RadioGroup.d.ts +3 -2
  125. package/lib/typescript/commonjs/components/SegmentedControl/SegmentedControl.d.ts +3 -2
  126. package/lib/typescript/commonjs/components/Slider/Slider.d.ts +6 -4
  127. package/lib/typescript/commonjs/components/Spinner/Spinner.d.ts +1 -1
  128. package/lib/typescript/commonjs/components/Switch/Switch.d.ts +3 -2
  129. package/lib/typescript/commonjs/components/Text/Text.d.ts +25 -0
  130. package/lib/typescript/commonjs/components/Text/index.d.ts +3 -0
  131. package/lib/typescript/commonjs/components/TimePicker/TimePicker.d.ts +3 -3
  132. package/lib/typescript/commonjs/components/index.d.ts +8 -0
  133. package/lib/typescript/commonjs/form/FormContext.d.ts +17 -0
  134. package/lib/typescript/commonjs/form/index.d.ts +9 -0
  135. package/lib/typescript/commonjs/form/path.d.ts +10 -0
  136. package/lib/typescript/commonjs/form/rules.d.ts +31 -0
  137. package/lib/typescript/commonjs/form/types.d.ts +94 -0
  138. package/lib/typescript/commonjs/form/useField.d.ts +27 -0
  139. package/lib/typescript/commonjs/form/useForm.d.ts +10 -0
  140. package/lib/typescript/commonjs/hooks/index.d.ts +3 -0
  141. package/lib/typescript/commonjs/hooks/useControllableState.d.ts +17 -0
  142. package/lib/typescript/commonjs/hooks/useReducedMotion.d.ts +8 -0
  143. package/lib/typescript/commonjs/index.d.ts +4 -2
  144. package/lib/typescript/commonjs/theme/types.d.ts +15 -0
  145. package/lib/typescript/module/components/BottomNavigation/BottomNavigation.d.ts +1 -1
  146. package/lib/typescript/module/components/Box/Box.d.ts +60 -0
  147. package/lib/typescript/module/components/Box/index.d.ts +3 -0
  148. package/lib/typescript/module/components/Button/Button.d.ts +1 -1
  149. package/lib/typescript/module/components/Checkbox/Checkbox.d.ts +3 -2
  150. package/lib/typescript/module/components/DatePicker/DatePicker.d.ts +3 -3
  151. package/lib/typescript/module/components/Dialog/Dialog.d.ts +2 -2
  152. package/lib/typescript/module/components/FormField/FormField.d.ts +13 -2
  153. package/lib/typescript/module/components/KeyboardAwareScrollView/KeyboardAwareScrollView.d.ts +20 -0
  154. package/lib/typescript/module/components/KeyboardAwareScrollView/index.d.ts +3 -0
  155. package/lib/typescript/module/components/KeyboardToolbar/KeyboardToolbar.d.ts +29 -0
  156. package/lib/typescript/module/components/KeyboardToolbar/index.d.ts +3 -0
  157. package/lib/typescript/module/components/NumberInput/NumberInput.d.ts +3 -2
  158. package/lib/typescript/module/components/OTPInput/OTPInput.d.ts +3 -2
  159. package/lib/typescript/module/components/Radio/Radio.d.ts +2 -2
  160. package/lib/typescript/module/components/Radio/RadioGroup.d.ts +3 -2
  161. package/lib/typescript/module/components/SegmentedControl/SegmentedControl.d.ts +3 -2
  162. package/lib/typescript/module/components/Slider/Slider.d.ts +6 -4
  163. package/lib/typescript/module/components/Spinner/Spinner.d.ts +1 -1
  164. package/lib/typescript/module/components/Switch/Switch.d.ts +3 -2
  165. package/lib/typescript/module/components/Text/Text.d.ts +25 -0
  166. package/lib/typescript/module/components/Text/index.d.ts +3 -0
  167. package/lib/typescript/module/components/TimePicker/TimePicker.d.ts +3 -3
  168. package/lib/typescript/module/components/index.d.ts +8 -0
  169. package/lib/typescript/module/form/FormContext.d.ts +17 -0
  170. package/lib/typescript/module/form/index.d.ts +9 -0
  171. package/lib/typescript/module/form/path.d.ts +10 -0
  172. package/lib/typescript/module/form/rules.d.ts +31 -0
  173. package/lib/typescript/module/form/types.d.ts +94 -0
  174. package/lib/typescript/module/form/useField.d.ts +27 -0
  175. package/lib/typescript/module/form/useForm.d.ts +10 -0
  176. package/lib/typescript/module/hooks/index.d.ts +3 -0
  177. package/lib/typescript/module/hooks/useControllableState.d.ts +17 -0
  178. package/lib/typescript/module/hooks/useReducedMotion.d.ts +8 -0
  179. package/lib/typescript/module/index.d.ts +4 -2
  180. package/lib/typescript/module/theme/types.d.ts +15 -0
  181. package/package.json +1 -1
@@ -61,34 +61,32 @@ const Select = /*#__PURE__*/forwardRef((props, ref) => {
61
61
  const backdropAnim = useRef(createAnimatedValue(0)).current;
62
62
  const sheetAnim = useRef(createAnimatedValue(sheetMaxHeight)).current;
63
63
  useEffect(() => {
64
- if (open) {
65
- Animated.parallel([
66
- // Backdrop opacity uses JS driver — see Modal.tsx for the Fabric reason.
67
- Animated.timing(backdropAnim, {
68
- toValue: 1,
69
- duration: theme.motion.duration.fast,
70
- easing: Easing.bezier(...theme.motion.easing.standard),
71
- useNativeDriver: false
72
- }), Animated.spring(sheetAnim, {
73
- toValue: 0,
74
- damping: theme.motion.spring.snappy.damping,
75
- stiffness: theme.motion.spring.snappy.stiffness,
76
- mass: theme.motion.spring.snappy.mass,
77
- useNativeDriver: true
78
- })]).start();
79
- } else {
80
- Animated.parallel([Animated.timing(backdropAnim, {
81
- toValue: 0,
82
- duration: theme.motion.duration.fast,
83
- easing: Easing.bezier(...theme.motion.easing.standard),
84
- useNativeDriver: false
85
- }), Animated.timing(sheetAnim, {
86
- toValue: sheetMaxHeight,
87
- duration: theme.motion.duration.fast,
88
- easing: Easing.bezier(...theme.motion.easing.accelerate),
89
- useNativeDriver: true
90
- })]).start();
91
- }
64
+ const anim = open ? Animated.parallel([
65
+ // Backdrop opacity uses JS driver — see Modal.tsx for the Fabric reason.
66
+ Animated.timing(backdropAnim, {
67
+ toValue: 1,
68
+ duration: theme.motion.duration.fast,
69
+ easing: Easing.bezier(...theme.motion.easing.standard),
70
+ useNativeDriver: false
71
+ }), Animated.spring(sheetAnim, {
72
+ toValue: 0,
73
+ damping: theme.motion.spring.snappy.damping,
74
+ stiffness: theme.motion.spring.snappy.stiffness,
75
+ mass: theme.motion.spring.snappy.mass,
76
+ useNativeDriver: true
77
+ })]) : Animated.parallel([Animated.timing(backdropAnim, {
78
+ toValue: 0,
79
+ duration: theme.motion.duration.fast,
80
+ easing: Easing.bezier(...theme.motion.easing.standard),
81
+ useNativeDriver: false
82
+ }), Animated.timing(sheetAnim, {
83
+ toValue: sheetMaxHeight,
84
+ duration: theme.motion.duration.fast,
85
+ easing: Easing.bezier(...theme.motion.easing.accelerate),
86
+ useNativeDriver: true
87
+ })]);
88
+ anim.start();
89
+ return () => anim.stop();
92
90
  }, [open, backdropAnim, sheetAnim, sheetMaxHeight, theme.motion.duration.fast, theme.motion.easing.standard, theme.motion.easing.accelerate, theme.motion.spring.snappy]);
93
91
 
94
92
  // Selection helpers — typed to keep callbacks fully type-safe.
@@ -386,14 +384,17 @@ const Chevron = ({
386
384
  size,
387
385
  open
388
386
  }) => {
387
+ const theme = useTheme();
389
388
  const rotate = useRef(createAnimatedValue(open ? 1 : 0)).current;
390
389
  useEffect(() => {
391
- Animated.timing(rotate, {
390
+ const anim = Animated.timing(rotate, {
392
391
  toValue: open ? 1 : 0,
393
- duration: 160,
392
+ duration: theme.motion.duration.fast,
394
393
  useNativeDriver: true
395
- }).start();
396
- }, [open, rotate]);
394
+ });
395
+ anim.start();
396
+ return () => anim.stop();
397
+ }, [open, rotate, theme.motion.duration.fast]);
397
398
  const rotation = rotate.interpolate({
398
399
  inputRange: [0, 1],
399
400
  outputRange: ['0deg', '180deg']
@@ -216,19 +216,22 @@ const SkeletonContent = ({
216
216
  const wasLoadingRef = useRef(loading);
217
217
  const fadeAnim = useRef(createAnimatedValue(loading ? 0 : 1)).current;
218
218
  useEffect(() => {
219
+ let anim;
219
220
  if (wasLoadingRef.current && !loading) {
220
221
  fadeAnim.setValue(0);
221
- Animated.timing(fadeAnim, {
222
+ anim = Animated.timing(fadeAnim, {
222
223
  toValue: 1,
223
224
  duration: resolvedFadeDuration,
224
225
  useNativeDriver: true
225
- }).start();
226
+ });
227
+ anim.start();
226
228
  } else if (!wasLoadingRef.current && loading) {
227
229
  // Going back into loading from loaded — snap opacity so the
228
230
  // placeholder appears instantly when data invalidates.
229
231
  fadeAnim.setValue(1);
230
232
  }
231
233
  wasLoadingRef.current = loading;
234
+ return () => anim?.stop();
232
235
  }, [loading, fadeAnim, resolvedFadeDuration]);
233
236
  if (!loading) {
234
237
  if (resolvedFadeDuration <= 0) {
@@ -4,6 +4,7 @@ import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState }
4
4
  import { Animated, Easing, PanResponder, Platform, Pressable, StyleSheet, Text, View } from 'react-native';
5
5
  import { useTheme, createAnimatedValue, setNativeValue } from "../../theme/index.js";
6
6
  import { triggerHaptic } from "../../utils/index.js";
7
+ import { useControllableState } from "../../hooks/index.js";
7
8
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
9
  const SIZE_MAP = {
9
10
  sm: {
@@ -74,10 +75,15 @@ const Slider = /*#__PURE__*/forwardRef((props, ref) => {
74
75
  const [trackWidth, setTrackWidth] = useState(0);
75
76
  const fillColor = theme.colors[TONE_TO_COLOR_KEY[tone]];
76
77
  const isRange = props.range === true;
78
+ const [currentValue, setCurrentValue] = useControllableState({
79
+ value: props.value,
80
+ defaultValue: props.defaultValue ?? (isRange ? [min, max] : min),
81
+ onChange: props.onChange
82
+ });
77
83
 
78
84
  // Current value is mirrored in refs so PanResponder closures get fresh values.
79
- const lowRef = useRef(isRange ? props.value[0] : props.value);
80
- const highRef = useRef(isRange ? props.value[1] : props.value);
85
+ const lowRef = useRef(isRange ? currentValue[0] : currentValue);
86
+ const highRef = useRef(isRange ? currentValue[1] : currentValue);
81
87
  const lastReportedLow = useRef(lowRef.current);
82
88
  const lastReportedHigh = useRef(highRef.current);
83
89
 
@@ -115,7 +121,7 @@ const Slider = /*#__PURE__*/forwardRef((props, ref) => {
115
121
  useEffect(() => {
116
122
  if (trackWidth <= 0) return;
117
123
  if (isRange) {
118
- const [v0, v1] = props.value;
124
+ const [v0, v1] = currentValue;
119
125
  const lo = Math.min(v0, v1);
120
126
  const hi = Math.max(v0, v1);
121
127
  lowRef.current = lo;
@@ -125,38 +131,34 @@ const Slider = /*#__PURE__*/forwardRef((props, ref) => {
125
131
  setNativeValue(lowX, valueToPx(lo, trackWidth));
126
132
  setNativeValue(highX, valueToPx(hi, trackWidth));
127
133
  } else {
128
- const v = props.value;
134
+ const v = currentValue;
129
135
  lowRef.current = v;
130
136
  highRef.current = v;
131
137
  lastReportedLow.current = v;
132
138
  setNativeValue(lowX, valueToPx(v, trackWidth));
133
139
  }
134
- // We intentionally listen to props.value across both shapes via JSON; ESLint is fine.
140
+ // We intentionally listen to the value across both shapes via JSON; ESLint is fine.
135
141
  // eslint-disable-next-line react-hooks/exhaustive-deps
136
- }, [JSON.stringify(props.value), trackWidth, isRange, valueToPx]);
142
+ }, [JSON.stringify(currentValue), trackWidth, isRange, valueToPx]);
137
143
  const onTrackLayout = useCallback(e => {
138
144
  const w = e.nativeEvent.layout.width;
139
145
  setTrackWidth(w);
140
146
  // Initialize positions immediately.
141
147
  if (isRange) {
142
- const [v0, v1] = props.value;
148
+ const [v0, v1] = currentValue;
143
149
  const lo = Math.min(v0, v1);
144
150
  const hi = Math.max(v0, v1);
145
151
  setNativeValue(lowX, valueToPx(lo, w));
146
152
  setNativeValue(highX, valueToPx(hi, w));
147
153
  } else {
148
- setNativeValue(lowX, valueToPx(props.value, w));
154
+ setNativeValue(lowX, valueToPx(currentValue, w));
149
155
  }
150
156
  },
151
157
  // eslint-disable-next-line react-hooks/exhaustive-deps
152
158
  [isRange, valueToPx]);
153
159
  const fireChange = useCallback((lo, hi) => {
154
- if (isRange) {
155
- props.onChange([lo, hi]);
156
- } else {
157
- props.onChange(lo);
158
- }
159
- }, [isRange, props]);
160
+ setCurrentValue(isRange ? [lo, hi] : lo);
161
+ }, [isRange, setCurrentValue]);
160
162
  const animateThumbPress = useCallback((target, opacity, pressed) => {
161
163
  if (thumbPressAnimationEnabled) {
162
164
  Animated.spring(target, {
@@ -191,24 +193,29 @@ const Slider = /*#__PURE__*/forwardRef((props, ref) => {
191
193
  if (trackWidth <= 0) return;
192
194
  const nextPx = clamp(dragOffsetRef.current + gesture.dx, 0, trackWidth);
193
195
  let nextValue = pxToValue(nextPx, trackWidth);
196
+
197
+ // Only touch the native driver + fire onChange when the snapped value
198
+ // actually changes — between step boundaries the drag is a visual
199
+ // no-op (px is derived from the snapped value), so this drops the
200
+ // per-frame native round-trip and per-frame consumer re-render.
194
201
  if (which === 'low') {
195
202
  if (isRange && nextValue > highRef.current) nextValue = highRef.current;
196
203
  if (nextValue !== lastReportedLow.current) {
197
204
  if (sliderTheme?.stepHaptic ?? false) triggerHaptic('selection');
198
205
  lastReportedLow.current = nextValue;
206
+ lowRef.current = nextValue;
207
+ setNativeValue(lowX, valueToPx(nextValue, trackWidth));
208
+ fireChange(nextValue, highRef.current);
199
209
  }
200
- lowRef.current = nextValue;
201
- setNativeValue(lowX, valueToPx(nextValue, trackWidth));
202
- fireChange(nextValue, highRef.current);
203
210
  } else {
204
211
  if (isRange && nextValue < lowRef.current) nextValue = lowRef.current;
205
212
  if (nextValue !== lastReportedHigh.current) {
206
213
  if (sliderTheme?.stepHaptic ?? false) triggerHaptic('selection');
207
214
  lastReportedHigh.current = nextValue;
215
+ highRef.current = nextValue;
216
+ setNativeValue(highX, valueToPx(nextValue, trackWidth));
217
+ fireChange(lowRef.current, nextValue);
208
218
  }
209
- highRef.current = nextValue;
210
- setNativeValue(highX, valueToPx(nextValue, trackWidth));
211
- fireChange(lowRef.current, nextValue);
212
219
  }
213
220
  },
214
221
  onPanResponderRelease: () => {
@@ -267,12 +274,21 @@ const Slider = /*#__PURE__*/forwardRef((props, ref) => {
267
274
  }
268
275
  }
269
276
  }, [disabled, step, isRange, min, max, valueToPx, trackWidth, lowX, fireChange, sliderTheme]);
270
-
271
- // Computed values for fill bar.
272
- const fillLeft = isRange ? lowX : new Animated.Value(0);
273
- const fillWidth = isRange ? Animated.subtract(highX, lowX) : lowX;
274
277
  const thumbRadius = sizeCfg.thumbDiameter / 2;
275
278
 
279
+ // Stable Animated nodes for the fill bar and thumb offsets — created once and
280
+ // memoised so a render doesn't rebuild `new Animated.Value(...)` /
281
+ // Animated.subtract nodes and re-attach the animation graph every pass.
282
+ const zeroX = useRef(createAnimatedValue(0)).current;
283
+ const thumbRadiusX = useRef(createAnimatedValue(thumbRadius)).current;
284
+ useEffect(() => {
285
+ setNativeValue(thumbRadiusX, thumbRadius);
286
+ }, [thumbRadius, thumbRadiusX]);
287
+ const fillLeft = isRange ? lowX : zeroX;
288
+ const fillWidth = useMemo(() => isRange ? Animated.subtract(highX, lowX) : lowX, [isRange, highX, lowX]);
289
+ const lowTranslateX = useMemo(() => Animated.subtract(lowX, thumbRadiusX), [lowX, thumbRadiusX]);
290
+ const highTranslateX = useMemo(() => Animated.subtract(highX, thumbRadiusX), [highX, thumbRadiusX]);
291
+
276
292
  // Display values for labels.
277
293
  const lowDisplayValue = isRange ? lowRef.current : lowRef.current;
278
294
  const highDisplayValue = highRef.current;
@@ -325,7 +341,7 @@ const Slider = /*#__PURE__*/forwardRef((props, ref) => {
325
341
  backgroundColor: theme.colors.background.elevated,
326
342
  borderColor: disabled ? theme.colors.surface.disabled : fillColor,
327
343
  transform: [{
328
- translateX: Animated.subtract(lowX, new Animated.Value(thumbRadius))
344
+ translateX: lowTranslateX
329
345
  }, {
330
346
  scale: lowScale
331
347
  }],
@@ -353,7 +369,7 @@ const Slider = /*#__PURE__*/forwardRef((props, ref) => {
353
369
  backgroundColor: theme.colors.background.elevated,
354
370
  borderColor: disabled ? theme.colors.surface.disabled : fillColor,
355
371
  transform: [{
356
- translateX: Animated.subtract(highX, new Animated.Value(thumbRadius))
372
+ translateX: highTranslateX
357
373
  }, {
358
374
  scale: highScale
359
375
  }],
@@ -5,9 +5,9 @@ import { ActivityIndicator, Animated, Easing, StyleSheet, Text, View } from 'rea
5
5
  import { useTheme, createAnimatedValue } from "../../theme/index.js";
6
6
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
7
  const sizeMap = {
8
- small: 20,
9
- medium: 32,
10
- large: 48
8
+ sm: 20,
9
+ md: 32,
10
+ lg: 48
11
11
  };
12
12
  const DOT_COUNT = 3;
13
13
  const DOT_STAGGER_MS = 150;
@@ -50,7 +50,7 @@ const Dot = ({
50
50
  };
51
51
  const Spinner = /*#__PURE__*/forwardRef((props, ref) => {
52
52
  const {
53
- size = 'medium',
53
+ size = 'md',
54
54
  color,
55
55
  message,
56
56
  overlay = false,
@@ -93,7 +93,7 @@ const Spinner = /*#__PURE__*/forwardRef((props, ref) => {
93
93
  }
94
94
  return /*#__PURE__*/_jsx(ActivityIndicator, {
95
95
  color: resolvedColor,
96
- size: size === 'small' ? 'small' : 'large',
96
+ size: size === 'sm' ? 'small' : 'large',
97
97
  style: {
98
98
  width: dimension,
99
99
  height: dimension
@@ -3,6 +3,7 @@
3
3
  import React, { forwardRef, useEffect, useMemo, useRef } from 'react';
4
4
  import { Animated, Easing, Pressable, StyleSheet, Text, View } from 'react-native';
5
5
  import { useTheme, createAnimatedValue } from "../../theme/index.js";
6
+ import { useControllableState } from "../../hooks/index.js";
6
7
  import { triggerHaptic } from "../../utils/hapticUtils.js";
7
8
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
9
  const sizeMap = {
@@ -41,7 +42,8 @@ const toneColor = (theme, tone) => {
41
42
  const Switch = /*#__PURE__*/forwardRef((props, ref) => {
42
43
  const {
43
44
  value,
44
- onValueChange,
45
+ defaultValue,
46
+ onChange,
45
47
  disabled = false,
46
48
  size = 'md',
47
49
  tone = 'primary',
@@ -56,6 +58,11 @@ const Switch = /*#__PURE__*/forwardRef((props, ref) => {
56
58
  testID,
57
59
  ...rest
58
60
  } = props;
61
+ const [current, setCurrent] = useControllableState({
62
+ value,
63
+ defaultValue: defaultValue ?? false,
64
+ onChange
65
+ });
59
66
  const theme = useTheme();
60
67
  const sizeStyles = {
61
68
  ...sizeMap[size],
@@ -65,25 +72,29 @@ const Switch = /*#__PURE__*/forwardRef((props, ref) => {
65
72
  const styles = useMemo(() => buildStyles(theme), [theme]);
66
73
  const offTrackColor = theme.mode === 'dark' ? theme.colors.secondary : theme.colors.surface.disabled;
67
74
  const onTrackColor = toneColor(theme, tone);
68
- const progress = useRef(createAnimatedValue(value ? 1 : 0)).current;
75
+ const progress = useRef(createAnimatedValue(current ? 1 : 0)).current;
69
76
  useEffect(() => {
70
- Animated.spring(progress, {
71
- toValue: value ? 1 : 0,
77
+ const anim = Animated.spring(progress, {
78
+ toValue: current ? 1 : 0,
72
79
  damping: theme.motion.spring.snappy.damping,
73
80
  stiffness: theme.motion.spring.snappy.stiffness,
74
81
  mass: theme.motion.spring.snappy.mass,
75
82
  useNativeDriver: true
76
- }).start();
77
- }, [value, progress, theme.motion.spring.snappy]);
78
- const trackBgAnim = useRef(createAnimatedValue(value ? 1 : 0)).current;
83
+ });
84
+ anim.start();
85
+ return () => anim.stop();
86
+ }, [current, progress, theme.motion.spring.snappy]);
87
+ const trackBgAnim = useRef(createAnimatedValue(current ? 1 : 0)).current;
79
88
  useEffect(() => {
80
- Animated.timing(trackBgAnim, {
81
- toValue: value ? 1 : 0,
89
+ const anim = Animated.timing(trackBgAnim, {
90
+ toValue: current ? 1 : 0,
82
91
  duration: theme.motion.duration.fast,
83
92
  easing: Easing.out(Easing.ease),
84
93
  useNativeDriver: false
85
- }).start();
86
- }, [value, trackBgAnim, theme.motion.duration.fast]);
94
+ });
95
+ anim.start();
96
+ return () => anim.stop();
97
+ }, [current, trackBgAnim, theme.motion.duration.fast]);
87
98
  const offX = sizeStyles.padding;
88
99
  const onX = sizeStyles.trackWidth - sizeStyles.thumbSize - sizeStyles.padding;
89
100
  const translateX = progress.interpolate({
@@ -102,7 +113,7 @@ const Switch = /*#__PURE__*/forwardRef((props, ref) => {
102
113
  isFirstRender.current = false;
103
114
  return;
104
115
  }
105
- Animated.sequence([Animated.spring(bounceScale, {
116
+ const anim = Animated.sequence([Animated.spring(bounceScale, {
106
117
  toValue: 1.15,
107
118
  damping: 10,
108
119
  stiffness: 220,
@@ -114,12 +125,14 @@ const Switch = /*#__PURE__*/forwardRef((props, ref) => {
114
125
  stiffness: 240,
115
126
  mass: 1,
116
127
  useNativeDriver: true
117
- })]).start();
118
- }, [value, bounce, bounceScale]);
128
+ })]);
129
+ anim.start();
130
+ return () => anim.stop();
131
+ }, [current, bounce, bounceScale]);
119
132
  const handlePress = event => {
120
133
  if (disabled) return;
121
134
  if (haptic !== false) triggerHaptic(haptic);
122
- onValueChange(!value);
135
+ setCurrent(!current);
123
136
  rest.onPressOut?.(event);
124
137
  };
125
138
  const accessibleLabel = accessibilityLabel ?? label;
@@ -130,7 +143,7 @@ const Switch = /*#__PURE__*/forwardRef((props, ref) => {
130
143
  accessibilityRole: "switch",
131
144
  accessibilityLabel: accessibleLabel,
132
145
  accessibilityState: {
133
- checked: value,
146
+ checked: current,
134
147
  disabled
135
148
  },
136
149
  testID: testID,
@@ -82,7 +82,7 @@ const Tabs = /*#__PURE__*/forwardRef((props, ref) => {
82
82
  if (!activeLayout) return;
83
83
  if (useProgress) return;
84
84
  const spring = theme.motion.spring.snappy;
85
- Animated.parallel([
85
+ const anim = Animated.parallel([
86
86
  // Both must use the JS driver: width can't run on native, and mixing
87
87
  // drivers on the same view (transform native + width JS) trips RN's
88
88
  // "node already moved to native" guard under the new architecture.
@@ -98,7 +98,9 @@ const Tabs = /*#__PURE__*/forwardRef((props, ref) => {
98
98
  stiffness: spring.stiffness,
99
99
  mass: spring.mass,
100
100
  useNativeDriver: false
101
- })]).start();
101
+ })]);
102
+ anim.start();
103
+ return () => anim.stop();
102
104
  }, [activeLayout, activeKey, indicatorTranslateX, indicatorWidth, theme.motion.spring.snappy, useProgress]);
103
105
  const handleLayout = useCallback(key => e => {
104
106
  const {
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+
3
+ import React, { forwardRef, useMemo } from 'react';
4
+ import { Text as RNText } from 'react-native';
5
+ import { fontFor, useTheme } from "../../theme/index.js";
6
+ import { jsx as _jsx } from "react/jsx-runtime";
7
+ const VARIANTS = {
8
+ display: {
9
+ size: '4xl',
10
+ weight: 'bold',
11
+ line: 'tight',
12
+ color: 'primary',
13
+ heading: true
14
+ },
15
+ h1: {
16
+ size: '3xl',
17
+ weight: 'bold',
18
+ line: 'tight',
19
+ color: 'primary',
20
+ heading: true
21
+ },
22
+ h2: {
23
+ size: '2xl',
24
+ weight: 'semibold',
25
+ line: 'tight',
26
+ color: 'primary',
27
+ heading: true
28
+ },
29
+ h3: {
30
+ size: 'xl',
31
+ weight: 'semibold',
32
+ line: 'tight',
33
+ color: 'primary',
34
+ heading: true
35
+ },
36
+ title: {
37
+ size: 'lg',
38
+ weight: 'semibold',
39
+ line: 'normal',
40
+ color: 'primary'
41
+ },
42
+ body: {
43
+ size: 'base',
44
+ weight: 'normal',
45
+ line: 'normal',
46
+ color: 'primary'
47
+ },
48
+ bodySmall: {
49
+ size: 'sm',
50
+ weight: 'normal',
51
+ line: 'normal',
52
+ color: 'secondary'
53
+ },
54
+ caption: {
55
+ size: 'xs',
56
+ weight: 'normal',
57
+ line: 'normal',
58
+ color: 'tertiary'
59
+ },
60
+ label: {
61
+ size: 'sm',
62
+ weight: 'medium',
63
+ line: 'normal',
64
+ color: 'secondary'
65
+ },
66
+ overline: {
67
+ size: 'xs',
68
+ weight: 'semibold',
69
+ line: 'normal',
70
+ color: 'secondary',
71
+ uppercase: true,
72
+ letterSpacing: 'wide'
73
+ }
74
+ };
75
+ const TEXT_ROLES = {
76
+ primary: true,
77
+ secondary: true,
78
+ tertiary: true,
79
+ inverse: true,
80
+ disabled: true,
81
+ link: true
82
+ };
83
+ const SEMANTIC = {
84
+ success: true,
85
+ warning: true,
86
+ error: true,
87
+ info: true
88
+ };
89
+ const resolveColor = (theme, color) => {
90
+ // Text roles win over the same-named brand colour ('primary' -> text.primary),
91
+ // which is the intent the vast majority of the time for text.
92
+ if (TEXT_ROLES[color]) return theme.colors.text[color];
93
+ if (SEMANTIC[color]) return theme.colors[color];
94
+ return color; // raw colour string
95
+ };
96
+ const Text = /*#__PURE__*/forwardRef((props, ref) => {
97
+ const {
98
+ variant = 'body',
99
+ color,
100
+ weight,
101
+ size,
102
+ align,
103
+ italic,
104
+ underline,
105
+ style,
106
+ children,
107
+ ...rest
108
+ } = props;
109
+ const theme = useTheme();
110
+ const textStyle = useMemo(() => {
111
+ const spec = VARIANTS[variant];
112
+ const fontSize = typeof size === 'number' ? size : theme.typography.fontSize[size ?? spec.size];
113
+ const lineHeight = Math.round(fontSize * theme.typography.lineHeight[spec.line]);
114
+ return {
115
+ fontSize,
116
+ lineHeight,
117
+ color: resolveColor(theme, color ?? spec.color),
118
+ textAlign: align,
119
+ textTransform: spec.uppercase ? 'uppercase' : undefined,
120
+ letterSpacing: spec.letterSpacing ? theme.typography.letterSpacing[spec.letterSpacing] : undefined,
121
+ fontStyle: italic ? 'italic' : undefined,
122
+ textDecorationLine: underline ? 'underline' : undefined,
123
+ ...fontFor(theme, weight ?? spec.weight)
124
+ };
125
+ }, [theme, variant, color, weight, size, align, italic, underline]);
126
+ const heading = VARIANTS[variant].heading;
127
+ return /*#__PURE__*/_jsx(RNText, {
128
+ ref: ref,
129
+ style: [textStyle, style],
130
+ accessibilityRole: heading ? 'header' : rest.accessibilityRole,
131
+ ...rest,
132
+ children: children
133
+ });
134
+ });
135
+ Text.displayName = 'Text';
136
+ export { Text };
137
+ export default Text;
138
+ //# sourceMappingURL=Text.js.map
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+
3
+ export { Text } from "./Text.js";
4
+ //# sourceMappingURL=index.js.map
@@ -11,12 +11,12 @@ import { PickerTrigger } from "../PickerTrigger/PickerTrigger.js";
11
11
  /**
12
12
  * TimePicker supports two modes:
13
13
  *
14
- * 1. Controlled-modal mode — pass `visible`, `onSelect`, `onClose`. The
14
+ * 1. Controlled-modal mode — pass `visible`, `onChange`, `onClose`. The
15
15
  * component renders only the modal sheet and the caller owns open/close
16
16
  * state plus its own trigger UI.
17
17
  * 2. Trigger mode — omit `visible`. The component renders a PickerTrigger
18
18
  * field (label / value / placeholder / chevron / clear / helper / error)
19
- * and manages its own modal open state. `onSelect` is still called on
19
+ * and manages its own modal open state. `onChange` is still called on
20
20
  * confirm.
21
21
  */
22
22
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
@@ -217,7 +217,7 @@ const TimePicker = props => {
217
217
  const {
218
218
  visible,
219
219
  value,
220
- onSelect,
220
+ onChange,
221
221
  onClose,
222
222
  format = '12h',
223
223
  minuteStep = 1,
@@ -304,8 +304,9 @@ const TimePicker = props => {
304
304
  const opacity = useRef(createAnimatedValue(0)).current;
305
305
  const translateY = useRef(createAnimatedValue(40)).current;
306
306
  useEffect(() => {
307
+ let anim;
307
308
  if (open) {
308
- Animated.parallel([
309
+ anim = Animated.parallel([
309
310
  // Backdrop opacity uses JS driver — see Modal.tsx for the Fabric reason.
310
311
  Animated.timing(opacity, {
311
312
  toValue: 1,
@@ -318,11 +319,13 @@ const TimePicker = props => {
318
319
  stiffness: theme.motion.spring.snappy.stiffness,
319
320
  mass: theme.motion.spring.snappy.mass,
320
321
  useNativeDriver: true
321
- })]).start();
322
+ })]);
323
+ anim.start();
322
324
  } else {
323
325
  opacity.setValue(0);
324
326
  setNativeValue(translateY, 40);
325
327
  }
328
+ return () => anim?.stop();
326
329
  }, [open, opacity, translateY, theme.motion]);
327
330
  const announce = useCallback(msg => {
328
331
  if (Platform.OS === 'ios' || Platform.OS === 'android') {
@@ -351,12 +354,12 @@ const TimePicker = props => {
351
354
  const hour24 = to24h(displayHour, period, format);
352
355
  const minute = minutes[minuteIndex] ?? 0;
353
356
  if (theme.components.timePicker?.haptic) triggerHaptic('notificationSuccess');
354
- onSelect?.({
357
+ onChange?.({
355
358
  hour: hour24,
356
359
  minute
357
360
  });
358
361
  handleCloseModal();
359
- }, [hours, hourIndex, periods, periodIndex, minutes, minuteIndex, format, onSelect, handleCloseModal, theme.components.timePicker]);
362
+ }, [hours, hourIndex, periods, periodIndex, minutes, minuteIndex, format, onChange, handleCloseModal, theme.components.timePicker]);
360
363
  const summary = useMemo(() => {
361
364
  const displayHour = hours[hourIndex] ?? 0;
362
365
  const minute = minutes[minuteIndex] ?? 0;