@expokit/cli 0.4.4 → 0.5.0

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 (239) hide show
  1. package/README.md +30 -3
  2. package/bin/expo-kit.js +749 -288
  3. package/bin/lib.mjs +310 -3
  4. package/package.json +1 -1
  5. package/registry/_docs/action-sheet.json +1 -1
  6. package/registry/_docs/alert.json +6 -0
  7. package/registry/_docs/bottom-sheet.json +6 -0
  8. package/registry/_docs/bullet.json +6 -0
  9. package/registry/_docs/dialog.json +1 -1
  10. package/registry/_docs/drawer.json +3 -3
  11. package/registry/_docs/glass-search-bar.json +1 -1
  12. package/registry/_docs/grain.json +6 -1
  13. package/registry/_docs/isometric-bar.json +6 -0
  14. package/registry/_docs/liquidglass-tab.json +1 -1
  15. package/registry/_docs/password-input.json +1 -1
  16. package/registry/_docs/pixel-bar.json +6 -0
  17. package/registry/_docs/polar-area.json +6 -0
  18. package/registry/_docs/qr-code-button.json +9 -3
  19. package/registry/_docs/scratch-card.json +7 -1
  20. package/registry/_docs/search-bar.json +1 -1
  21. package/registry/_docs/select.json +2 -2
  22. package/registry/_docs/slide-to-confirm.json +2 -2
  23. package/registry/_docs/spinner.json +1 -1
  24. package/registry/_docs/stateful-button.json +2 -2
  25. package/registry/_docs/swipe-deck.json +1 -1
  26. package/registry/_docs/time-picker.json +2 -2
  27. package/registry/_docs/timeline-chart.json +6 -0
  28. package/registry/_docs/toast.json +15 -3
  29. package/registry/_docs/treemap.json +6 -0
  30. package/registry/_docs/waterfall.json +6 -0
  31. package/registry/accordion/files/src/components/ui/accordion.tsx +6 -4
  32. package/registry/accordion/files/src/lib/kit.tsx +363 -11
  33. package/registry/accordion.json +1 -0
  34. package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +43 -48
  35. package/registry/action-sheet/files/src/lib/kit.tsx +363 -11
  36. package/registry/action-sheet.json +1 -0
  37. package/registry/alert/files/src/components/ui/alert.tsx +19 -12
  38. package/registry/alert/files/src/lib/kit.tsx +363 -11
  39. package/registry/alert.json +3 -4
  40. package/registry/assistant-app.json +1 -0
  41. package/registry/avatar/files/src/components/ui/avatar.tsx +84 -66
  42. package/registry/avatar/files/src/lib/kit.tsx +363 -11
  43. package/registry/avatar.json +1 -0
  44. package/registry/badge/files/src/components/ui/badge.tsx +34 -34
  45. package/registry/badge/files/src/lib/kit.tsx +363 -11
  46. package/registry/badge.json +1 -0
  47. package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +28 -27
  48. package/registry/bottom-nav/files/src/lib/kit.tsx +363 -11
  49. package/registry/bottom-nav.json +5 -0
  50. package/registry/bottom-sheet/files/src/components/ui/bottom-sheet.tsx +353 -289
  51. package/registry/bottom-sheet/files/src/lib/kit.tsx +363 -11
  52. package/registry/bottom-sheet.json +5 -1
  53. package/registry/bullet.json +1 -0
  54. package/registry/button/files/src/components/ui/button.tsx +18 -16
  55. package/registry/button/files/src/lib/kit.tsx +363 -11
  56. package/registry/button-3d/files/src/components/ui/button-3d.tsx +2 -2
  57. package/registry/button-3d/files/src/lib/kit.tsx +363 -11
  58. package/registry/button-3d.json +1 -0
  59. package/registry/button.json +1 -0
  60. package/registry/calendar/files/src/components/ui/calendar.tsx +74 -76
  61. package/registry/calendar/files/src/lib/kit.tsx +363 -11
  62. package/registry/calendar.json +1 -0
  63. package/registry/card/files/src/components/ui/card.tsx +3 -3
  64. package/registry/card/files/src/lib/kit.tsx +363 -11
  65. package/registry/card-stack.json +1 -0
  66. package/registry/card.json +1 -0
  67. package/registry/carousel.json +1 -0
  68. package/registry/chat.json +7 -0
  69. package/registry/checkbox/files/src/components/ui/checkbox.tsx +4 -4
  70. package/registry/checkbox/files/src/lib/kit.tsx +363 -11
  71. package/registry/checkbox.json +1 -0
  72. package/registry/checkout.json +1 -0
  73. package/registry/chip-group/files/src/components/ui/chip-group.tsx +22 -22
  74. package/registry/chip-group/files/src/lib/kit.tsx +363 -11
  75. package/registry/chip-group.json +1 -0
  76. package/registry/confetti/files/src/components/ui/confetti.tsx +15 -14
  77. package/registry/confetti.json +3 -6
  78. package/registry/coupon/files/src/components/ui/coupon.tsx +42 -42
  79. package/registry/coupon/files/src/components/ui/flip-card.tsx +6 -5
  80. package/registry/coupon/files/src/components/ui/tilt-card.tsx +76 -91
  81. package/registry/coupon/files/src/lib/kit.tsx +363 -11
  82. package/registry/coupon/files/src/lib/ticket-shape.tsx +54 -44
  83. package/registry/coupon.json +1 -0
  84. package/registry/cube-pager.json +1 -0
  85. package/registry/dashboard.json +1 -0
  86. package/registry/date-picker/files/src/components/ui/calendar.tsx +74 -76
  87. package/registry/date-picker/files/src/components/ui/date-picker.tsx +55 -56
  88. package/registry/date-picker/files/src/lib/kit.tsx +363 -11
  89. package/registry/date-picker.json +1 -0
  90. package/registry/dialog/files/src/components/ui/dialog.tsx +60 -56
  91. package/registry/dialog/files/src/lib/kit.tsx +363 -11
  92. package/registry/dialog.json +1 -0
  93. package/registry/donut-shop.json +1 -0
  94. package/registry/drawer/files/src/components/ui/drawer.tsx +73 -69
  95. package/registry/drawer/files/src/lib/kit.tsx +363 -11
  96. package/registry/drawer.json +1 -0
  97. package/registry/fab.json +1 -0
  98. package/registry/fintech-app.json +1 -0
  99. package/registry/flip-calendar/files/src/components/ui/flip-calendar.tsx +92 -86
  100. package/registry/flip-calendar/files/src/lib/kit.tsx +363 -11
  101. package/registry/flip-calendar.json +7 -0
  102. package/registry/flip-card/files/src/components/ui/flip-card.tsx +6 -5
  103. package/registry/flip-card/files/src/lib/kit.tsx +363 -11
  104. package/registry/flip-card.json +1 -0
  105. package/registry/floating-input/files/src/components/ui/floating-input.tsx +33 -33
  106. package/registry/floating-input/files/src/lib/kit.tsx +363 -11
  107. package/registry/floating-input.json +1 -0
  108. package/registry/food-app.json +1 -0
  109. package/registry/gift-card.json +1 -0
  110. package/registry/glass-button.json +5 -0
  111. package/registry/glass-nav-bar.json +1 -0
  112. package/registry/glass-search-bar.json +1 -0
  113. package/registry/glass-segmented.json +1 -0
  114. package/registry/glass-sheet.json +1 -0
  115. package/registry/glass-tab-bar.json +5 -0
  116. package/registry/glass.json +1 -0
  117. package/registry/grain/files/src/components/ui/grain.tsx +16 -14
  118. package/registry/grain.json +8 -6
  119. package/registry/inbox.json +1 -0
  120. package/registry/isometric-bar.json +1 -0
  121. package/registry/kit/files/src/lib/kit.tsx +363 -11
  122. package/registry/kit.json +1 -0
  123. package/registry/liquidglass-tab/files/src/app/_layout.tsx +0 -1
  124. package/registry/liquidglass-tab/files/src/components/GlassTabBar.tsx +23 -9
  125. package/registry/liquidglass-tab/files/src/components/TabScreen.tsx +2 -2
  126. package/registry/liquidglass-tab/files/src/lib/tab-icons.ts +1 -1
  127. package/registry/liquidglass-tab.json +1 -0
  128. package/registry/list-item/files/src/components/ui/list-item.tsx +42 -42
  129. package/registry/list-item/files/src/lib/kit.tsx +363 -11
  130. package/registry/list-item.json +1 -0
  131. package/registry/loyalty-card.json +1 -0
  132. package/registry/morphing-button-group.json +1 -0
  133. package/registry/number-ticker.json +1 -0
  134. package/registry/offers.json +1 -0
  135. package/registry/onboarding.json +1 -0
  136. package/registry/orb-reveal.json +1 -0
  137. package/registry/otp-input/files/src/components/ui/otp-input.tsx +48 -29
  138. package/registry/otp-input/files/src/lib/kit.tsx +363 -11
  139. package/registry/otp-input.json +1 -0
  140. package/registry/parallax-layers.json +1 -0
  141. package/registry/parallax-scroll.json +1 -0
  142. package/registry/password-input/files/src/components/ui/floating-input.tsx +33 -33
  143. package/registry/password-input/files/src/components/ui/password-input.tsx +29 -25
  144. package/registry/password-input/files/src/lib/kit.tsx +363 -11
  145. package/registry/password-input.json +1 -0
  146. package/registry/payment-card/files/src/components/ui/flip-card.tsx +6 -5
  147. package/registry/payment-card/files/src/components/ui/payment-card.tsx +17 -17
  148. package/registry/payment-card/files/src/components/ui/tilt-card.tsx +76 -91
  149. package/registry/payment-card/files/src/lib/kit.tsx +363 -11
  150. package/registry/payment-card.json +1 -0
  151. package/registry/paywall.json +1 -0
  152. package/registry/pixel-bar.json +1 -0
  153. package/registry/polar-area.json +1 -0
  154. package/registry/profile.json +1 -0
  155. package/registry/progress/files/src/components/ui/progress.tsx +29 -29
  156. package/registry/progress/files/src/lib/kit.tsx +363 -11
  157. package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +20 -20
  158. package/registry/progress-steps/files/src/lib/kit.tsx +363 -11
  159. package/registry/progress-steps.json +1 -0
  160. package/registry/progress.json +1 -0
  161. package/registry/qr-code-button.json +1 -0
  162. package/registry/radio-group/files/src/components/ui/radio-group.tsx +19 -19
  163. package/registry/radio-group/files/src/lib/kit.tsx +363 -11
  164. package/registry/radio-group.json +1 -0
  165. package/registry/rating.json +1 -0
  166. package/registry/ring-carousel.json +1 -0
  167. package/registry/ruler-time-picker/files/src/components/ui/ruler-time-picker.tsx +98 -82
  168. package/registry/ruler-time-picker/files/src/lib/kit.tsx +363 -11
  169. package/registry/ruler-time-picker.json +7 -0
  170. package/registry/scratch-card.json +1 -0
  171. package/registry/search-bar/files/src/components/ui/search-bar.tsx +25 -21
  172. package/registry/search-bar/files/src/lib/kit.tsx +363 -11
  173. package/registry/search-bar.json +1 -0
  174. package/registry/search-results.json +4 -0
  175. package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +8 -7
  176. package/registry/segmented-control/files/src/lib/kit.tsx +363 -11
  177. package/registry/segmented-control.json +1 -0
  178. package/registry/select/files/src/components/ui/select.tsx +93 -88
  179. package/registry/select/files/src/lib/kit.tsx +363 -11
  180. package/registry/select.json +6 -0
  181. package/registry/settings.json +1 -0
  182. package/registry/sign-in.json +1 -0
  183. package/registry/skeleton/files/src/components/ui/skeleton.tsx +149 -80
  184. package/registry/skeleton/files/src/lib/kit.tsx +363 -11
  185. package/registry/skeleton.json +1 -0
  186. package/registry/slide-to-confirm.json +1 -0
  187. package/registry/slider/files/src/components/ui/slider.tsx +103 -96
  188. package/registry/slider/files/src/lib/kit.tsx +363 -11
  189. package/registry/slider.json +5 -0
  190. package/registry/spin-wheel.json +1 -0
  191. package/registry/spinner/files/src/components/ui/spinner.tsx +39 -29
  192. package/registry/spinner/files/src/lib/kit.tsx +363 -11
  193. package/registry/spinner.json +6 -0
  194. package/registry/stateful-button/files/src/components/ui/stateful-button.tsx +39 -32
  195. package/registry/stateful-button/files/src/lib/kit.tsx +363 -11
  196. package/registry/stateful-button.json +5 -0
  197. package/registry/stepper/files/src/components/ui/stepper.tsx +38 -36
  198. package/registry/stepper/files/src/lib/kit.tsx +363 -11
  199. package/registry/stepper.json +1 -0
  200. package/registry/swipe-deck.json +1 -0
  201. package/registry/swipeable-row.json +4 -0
  202. package/registry/switch/files/src/components/ui/switch.tsx +39 -42
  203. package/registry/switch/files/src/lib/kit.tsx +363 -11
  204. package/registry/switch.json +1 -0
  205. package/registry/tabs/files/src/components/ui/tabs.tsx +59 -62
  206. package/registry/tabs/files/src/lib/kit.tsx +363 -11
  207. package/registry/tabs.json +1 -0
  208. package/registry/text-animation/files/src/components/ui/text-animation.tsx +55 -42
  209. package/registry/text-animation/files/src/lib/kit.tsx +363 -11
  210. package/registry/text-animation.json +7 -0
  211. package/registry/textarea/files/src/components/ui/textarea.tsx +10 -10
  212. package/registry/textarea/files/src/lib/kit.tsx +363 -11
  213. package/registry/textarea.json +1 -0
  214. package/registry/ticket-card.json +1 -0
  215. package/registry/ticket-shape/files/src/lib/kit.tsx +363 -11
  216. package/registry/ticket-shape/files/src/lib/ticket-shape.tsx +54 -44
  217. package/registry/ticket-shape.json +1 -0
  218. package/registry/tilt-card/files/src/components/ui/tilt-card.tsx +76 -91
  219. package/registry/tilt-card/files/src/lib/kit.tsx +363 -11
  220. package/registry/tilt-card.json +1 -0
  221. package/registry/time-picker/files/src/components/ui/time-picker.tsx +122 -119
  222. package/registry/time-picker/files/src/lib/kit.tsx +363 -11
  223. package/registry/time-picker.json +1 -0
  224. package/registry/timeline/files/src/components/ui/avatar.tsx +84 -66
  225. package/registry/timeline/files/src/components/ui/timeline.tsx +44 -41
  226. package/registry/timeline/files/src/lib/kit.tsx +363 -11
  227. package/registry/timeline-chart.json +1 -0
  228. package/registry/timeline.json +1 -0
  229. package/registry/toast/files/src/components/ui/toast.tsx +541 -463
  230. package/registry/toast/files/src/lib/kit.tsx +363 -11
  231. package/registry/toast.json +7 -5
  232. package/registry/treemap.json +1 -0
  233. package/registry/waterfall.json +1 -0
  234. package/registry/wheel-list.json +1 -0
  235. package/schema/components.schema.json +1 -0
  236. package/registry/alert/files/src/components/ui/button.tsx +0 -290
  237. package/registry/confetti/files/src/lib/kit.tsx +0 -320
  238. package/registry/grain/files/src/lib/kit.tsx +0 -320
  239. package/registry/toast/files/src/components/ui/button.tsx +0 -290
@@ -1,9 +1,8 @@
1
- import React, { useEffect, useMemo, useRef, useState } from 'react';
1
+ import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
2
2
  import {
3
3
  AccessibilityInfo,
4
4
  Animated,
5
5
  Easing,
6
- PanResponder,
7
6
  Pressable,
8
7
  StyleSheet,
9
8
  Text,
@@ -17,7 +16,7 @@ import {
17
16
  } from 'react-native';
18
17
  import Svg, { Circle, Path } from 'react-native-svg';
19
18
 
20
- import { alpha, motion, readableOn, useKit } from '@/lib/kit';
19
+ import { alpha, motion, readableOn, useKit, usePanResponder } from '@/lib/kit';
21
20
 
22
21
  /**
23
22
  * - `default` — thin track with a round thumb (iOS / Material).
@@ -130,6 +129,10 @@ const glide = (v: Animated.Value, toValue: number, reduce: boolean) =>
130
129
  ? Animated.timing(v, { toValue, duration: 150, easing: SMOOTH, useNativeDriver: true })
131
130
  : Animated.spring(v, { toValue, useNativeDriver: true, ...motion.snappy });
132
131
  const decimals = (n: number) => (String(n).split('.')[1] ?? '').length;
132
+ /** Value `i` of a slider's values: index 1 of a single slider (or a short array) falls back to the first. */
133
+ const nth = (arr: readonly number[], i: number) => arr[i] ?? arr[0] ?? 0;
134
+ /** The animated value of thumb `i` (always 0 or 1). */
135
+ const pick = <T,>(pair: readonly [T, T], i: number): T => (i === 1 ? pair[1] : pair[0]);
133
136
 
134
137
  /**
135
138
  * A slider that feels native: the thumb and fill move on the native driver (no re-render per
@@ -173,7 +176,7 @@ export function Slider(props: SliderProps) {
173
176
  const onSlidingStart = props.onSlidingStart as Callback;
174
177
  const onSlidingComplete = props.onSlidingComplete as Callback;
175
178
 
176
- const { colors, radius, dark, font, shadow } = useKit();
179
+ const { colors, radius, dark, font, shadow, labels } = useKit();
177
180
  const ff = font.family ? { fontFamily: font.family } : null;
178
181
  const reduce = useReduceMotion();
179
182
  const tint = accent ?? colors.accent;
@@ -203,7 +206,7 @@ export function Slider(props: SliderProps) {
203
206
  const controlled = props.value !== undefined;
204
207
  const [inner, setInner] = useState<number[]>(() => toArr(props.defaultValue));
205
208
  const vals = (controlled ? toArr(props.value) : inner).map((v) => clamp(v, min, max));
206
- if (range && vals[0] > vals[1]) vals.reverse();
209
+ if (range && nth(vals, 0) > nth(vals, 1)) vals.reverse();
207
210
 
208
211
  const span = max - min || 1;
209
212
  const ratioOf = (v: number) => clamp((v - min) / span, 0, 1);
@@ -213,28 +216,33 @@ export function Slider(props: SliderProps) {
213
216
  return clamp(Number(n.toFixed(decimals(step))), min, max);
214
217
  };
215
218
  const fmt = (v: number) => (formatValue ? formatValue(v) : step ? v.toFixed(decimals(step)) : String(Math.round(v)));
216
- const out = (arr: number[]): SliderValue => (range ? [arr[0], arr[1]] : arr[0]);
219
+ const out = (arr: number[]): SliderValue => (range ? [nth(arr, 0), nth(arr, 1)] : nth(arr, 0));
217
220
 
218
221
  const [w, setW] = useState(0);
219
222
  const [active, setActive] = useState<number | null>(null);
220
223
 
221
224
  // ---- animated values ------------------------------------------------------------------------
222
225
  // All native-driven: they only ever feed transform / opacity.
223
- const ratios = useRef([new Animated.Value(ratioOf(vals[0])), new Animated.Value(ratioOf(vals[1] ?? vals[0]))]).current;
224
- const press = useRef([new Animated.Value(0), new Animated.Value(0)]).current;
225
- const bubble = useRef([new Animated.Value(bubbleShown ? 1 : 0), new Animated.Value(bubbleShown ? 1 : 0)]).current;
226
- const held = useRef(new Animated.Value(0)).current;
226
+ const [ratios] = useState((): readonly [Animated.Value, Animated.Value] => [
227
+ new Animated.Value(ratioOf(nth(vals, 0))),
228
+ new Animated.Value(ratioOf(nth(vals, 1))),
229
+ ]);
230
+ const [press] = useState(() => [new Animated.Value(0), new Animated.Value(0)]);
231
+ const [bubble] = useState(() => [new Animated.Value(bubbleShown ? 1 : 0), new Animated.Value(bubbleShown ? 1 : 0)]);
232
+ const [held] = useState(() => new Animated.Value(0));
227
233
 
228
234
  // Everything the (once-created) PanResponder reads, kept fresh every render.
229
235
  const live = useRef({ vals, w, inset, step, span, min, range, isBar, T, disabled, controlled, reduce, snap, ratioOf, fmt, out, onValueChange, onSlidingStart, onSlidingComplete });
230
- live.current = { vals, w, inset, step, span, min, range, isBar, T, disabled, controlled, reduce, snap, ratioOf, fmt, out, onValueChange, onSlidingStart, onSlidingComplete };
236
+ useLayoutEffect(() => {
237
+ live.current = { vals, w, inset, step, span, min, range, isBar, T, disabled, controlled, reduce, snap, ratioOf, fmt, out, onValueChange, onSlidingStart, onSlidingComplete };
238
+ });
231
239
  const drag = useRef({ dragging: false, thumb: 0, startX: 0, origin: 0, relative: false, moved: false, cur: [] as number[], target: [-1, -1] });
232
240
 
233
241
  // Follow value changes from outside (controlled updates, a11y, icon taps) when nobody is dragging.
234
242
  const key = vals.join(',');
235
243
  useEffect(() => {
236
244
  if (drag.current.dragging) return;
237
- vals.forEach((v, i) => glide(ratios[i], ratioOf(v), reduce).start());
245
+ vals.forEach((v, i) => glide(pick(ratios, i), ratioOf(v), reduce).start());
238
246
  // eslint-disable-next-line react-hooks/exhaustive-deps
239
247
  }, [key, min, max]);
240
248
 
@@ -245,91 +253,90 @@ export function Slider(props: SliderProps) {
245
253
  glide(held, active !== null ? 1 : 0, reduce).start();
246
254
  }, [active, bubbleShown, showValue, press, bubble, held, reduce]);
247
255
 
248
- const pan = useMemo(() => {
249
- const usableOf = () => Math.max(1, live.current.w - 2 * live.current.inset);
250
- const ratioAt = (x: number) => clamp((x - live.current.inset) / usableOf(), 0, 1);
256
+ const usableOf = () => Math.max(1, live.current.w - 2 * live.current.inset);
257
+ const ratioAt = (x: number) => clamp((x - live.current.inset) / usableOf(), 0, 1);
258
+
259
+ const moveTo = (x: number, animate: boolean) => {
260
+ const L = live.current;
261
+ const d = drag.current;
262
+ const i = d.thumb;
263
+ let v = L.min + ratioAt(x) * L.span;
264
+ if (L.range) v = i === 0 ? Math.min(v, nth(d.cur, 1)) : Math.max(v, nth(d.cur, 0));
265
+ const snapped = L.snap(v);
266
+ const coarse = !!L.step && (L.step / L.span) * usableOf() >= SNAP_PX;
267
+ const target = coarse ? L.ratioOf(snapped) : L.ratioOf(v);
268
+ if (animate || coarse) {
269
+ if (target !== d.target[i]) glide(pick(ratios, i), target, L.reduce).start();
270
+ } else {
271
+ pick(ratios, i).setValue(target);
272
+ }
273
+ d.target[i] = target;
274
+ if (snapped === d.cur[i]) return;
275
+ const prev = nth(d.cur, i);
276
+ const next = [...d.cur];
277
+ next[i] = snapped;
278
+ d.cur = next;
279
+ // Uncontrolled continuous sliders only re-render when the shown text changes.
280
+ if (!L.controlled && (L.step || L.fmt(prev) !== L.fmt(snapped))) setInner(next);
281
+ L.onValueChange?.(L.out(next));
282
+ };
251
283
 
252
- const moveTo = (x: number, animate: boolean) => {
253
- const L = live.current;
254
- const d = drag.current;
255
- const i = d.thumb;
256
- let v = L.min + ratioAt(x) * L.span;
257
- if (L.range) v = i === 0 ? Math.min(v, d.cur[1]) : Math.max(v, d.cur[0]);
258
- const snapped = L.snap(v);
259
- const coarse = !!L.step && (L.step / L.span) * usableOf() >= SNAP_PX;
260
- const target = coarse ? L.ratioOf(snapped) : L.ratioOf(v);
261
- if (animate || coarse) {
262
- if (target !== d.target[i]) glide(ratios[i], target, L.reduce).start();
263
- } else {
264
- ratios[i].setValue(target);
265
- }
266
- d.target[i] = target;
267
- if (snapped === d.cur[i]) return;
268
- const prev = d.cur[i];
269
- const next = [...d.cur];
270
- next[i] = snapped;
271
- d.cur = next;
272
- // Uncontrolled continuous sliders only re-render when the shown text changes.
273
- if (!L.controlled && (L.step || L.fmt(prev) !== L.fmt(snapped))) setInner(next);
274
- L.onValueChange?.(L.out(next));
275
- };
284
+ const finish = (seek: boolean) => {
285
+ const L = live.current;
286
+ const d = drag.current;
287
+ if (!d.dragging) return;
288
+ if (seek) moveTo(d.startX, true);
289
+ glide(pick(ratios, d.thumb), L.ratioOf(nth(d.cur, d.thumb)), L.reduce).start();
290
+ d.dragging = false;
291
+ d.target = [-1, -1];
292
+ if (!L.controlled) setInner(d.cur);
293
+ setActive(null);
294
+ L.onSlidingComplete?.(L.out(d.cur));
295
+ };
276
296
 
277
- const finish = (seek: boolean) => {
297
+ const pan = usePanResponder({
298
+ onStartShouldSetPanResponder: () => !live.current.disabled,
299
+ onMoveShouldSetPanResponder: () => !live.current.disabled,
300
+ onPanResponderTerminationRequest: () => false,
301
+ onShouldBlockNativeResponder: () => true,
302
+ onPanResponderGrant: (e: GestureResponderEvent) => {
278
303
  const L = live.current;
304
+ const x = e.nativeEvent.locationX;
305
+ const cur = [...L.vals];
306
+ const xs = cur.map((v) => L.inset + L.ratioOf(v) * usableOf());
307
+ let thumb = 0;
308
+ if (L.range) {
309
+ const d0 = Math.abs(x - nth(xs, 0));
310
+ const d1 = Math.abs(x - nth(xs, 1));
311
+ thumb = d0 === d1 ? (x > nth(xs, 1) ? 1 : 0) : d1 < d0 ? 1 : 0;
312
+ }
313
+ // Grabbing the thumb (or anywhere on a bar) drags relative to it; elsewhere seeks first.
314
+ const onThumb = !L.isBar && Math.abs(x - nth(xs, thumb)) <= Math.max(L.T / 2 + 8, 22);
315
+ const relative = L.isBar || onThumb;
316
+ drag.current = { dragging: true, thumb, startX: x, origin: relative ? nth(xs, thumb) : x, relative, moved: false, cur, target: [-1, -1] };
317
+ setActive(thumb);
318
+ L.onSlidingStart?.(L.out(cur));
319
+ if (!relative) moveTo(x, true);
320
+ },
321
+ onPanResponderMove: (_, g) => {
279
322
  const d = drag.current;
280
- if (!d.dragging) return;
281
- if (seek) moveTo(d.startX, true);
282
- glide(ratios[d.thumb], L.ratioOf(d.cur[d.thumb]), L.reduce).start();
283
- d.dragging = false;
284
- d.target = [-1, -1];
285
- if (!L.controlled) setInner(d.cur);
286
- setActive(null);
287
- L.onSlidingComplete?.(L.out(d.cur));
288
- };
289
-
290
- return PanResponder.create({
291
- onStartShouldSetPanResponder: () => !live.current.disabled,
292
- onMoveShouldSetPanResponder: () => !live.current.disabled,
293
- onPanResponderTerminationRequest: () => false,
294
- onShouldBlockNativeResponder: () => true,
295
- onPanResponderGrant: (e: GestureResponderEvent) => {
296
- const L = live.current;
297
- const x = e.nativeEvent.locationX;
298
- const cur = [...L.vals];
299
- const xs = cur.map((v) => L.inset + L.ratioOf(v) * usableOf());
300
- let thumb = 0;
301
- if (L.range) {
302
- const d0 = Math.abs(x - xs[0]);
303
- const d1 = Math.abs(x - xs[1]);
304
- thumb = d0 === d1 ? (x > xs[1] ? 1 : 0) : d1 < d0 ? 1 : 0;
305
- }
306
- // Grabbing the thumb (or anywhere on a bar) drags relative to it; elsewhere seeks first.
307
- const onThumb = !L.isBar && Math.abs(x - xs[thumb]) <= Math.max(L.T / 2 + 8, 22);
308
- const relative = L.isBar || onThumb;
309
- drag.current = { dragging: true, thumb, startX: x, origin: relative ? xs[thumb] : x, relative, moved: false, cur, target: [-1, -1] };
310
- setActive(thumb);
311
- L.onSlidingStart?.(L.out(cur));
312
- if (!relative) moveTo(x, true);
313
- },
314
- onPanResponderMove: (_, g) => {
315
- const d = drag.current;
316
- if (Math.abs(g.dx) >= 3) d.moved = true;
317
- if (!d.moved && live.current.isBar) return; // keep a tap on a bar a tap
318
- moveTo(d.origin + g.dx, false);
319
- },
320
- // A tap on a bar (no drag) still seeks.
321
- onPanResponderRelease: () => finish(drag.current.relative && live.current.isBar && !drag.current.moved),
322
- onPanResponderTerminate: () => finish(false),
323
- });
324
- }, [ratios]);
323
+ if (Math.abs(g.dx) >= 3) d.moved = true;
324
+ if (!d.moved && live.current.isBar) return; // keep a tap on a bar a tap
325
+ moveTo(d.origin + g.dx, false);
326
+ },
327
+ // A tap on a bar (no drag) still seeks.
328
+ onPanResponderRelease: () => finish(drag.current.relative && live.current.isBar && !drag.current.moved),
329
+ onPanResponderTerminate: () => finish(false),
330
+ });
325
331
 
326
332
  const stepBy = (i: number, dir: 1 | -1, amount?: number) => {
327
333
  if (disabled) return;
328
334
  const inc = amount ?? (step || span / 100);
329
335
  const next = [...vals];
330
- next[i] = snap(next[i] + dir * inc);
331
- if (range) next[i] = i === 0 ? Math.min(next[i], vals[1]) : Math.max(next[i], vals[0]);
332
- if (next[i] === vals[i]) return;
336
+ let moved = snap(nth(next, i) + dir * inc);
337
+ if (range) moved = i === 0 ? Math.min(moved, nth(vals, 1)) : Math.max(moved, nth(vals, 0));
338
+ next[i] = moved;
339
+ if (moved === vals[i]) return;
333
340
  if (!controlled) setInner(next);
334
341
  onValueChange?.(out(next));
335
342
  onSlidingComplete?.(out(next));
@@ -341,9 +348,9 @@ export function Slider(props: SliderProps) {
341
348
  const usable = Math.max(1, w - 2 * inset);
342
349
  const geo = useMemo(() => {
343
350
  if (!w) return null;
344
- const px = ratios.map((r) =>
345
- r.interpolate({ inputRange: [0, 1], outputRange: [inset, inset + usable], extrapolate: 'clamp' })
346
- );
351
+ const toPx = (r: Animated.Value) =>
352
+ r.interpolate({ inputRange: [0, 1], outputRange: [inset, inset + usable], extrapolate: 'clamp' });
353
+ const px = [toPx(ratios[0]), toPx(ratios[1])] as const;
347
354
  // Single: a full-width fill slid left so its edge sits at the thumb. Range: a full-width fill
348
355
  // scaled about its centre to span the two thumbs. Both are clipped by a rounded container.
349
356
  const shift = Animated.add(px[0], -w);
@@ -386,7 +393,7 @@ export function Slider(props: SliderProps) {
386
393
  }, [marks, stepped, step, span, min]);
387
394
  const hasMarkLabels = markList.some((m) => m.label);
388
395
  const minMax = showMinMax || minLabel !== undefined || maxLabel !== undefined;
389
- const inFill = (v: number) => (range ? v >= vals[0] && v <= vals[1] : v <= vals[0]);
396
+ const inFill = (v: number) => (range ? v >= nth(vals, 0) && v <= nth(vals, 1) : v <= nth(vals, 0));
390
397
  const dot = Math.max(2, Math.round(trackH * (isBar ? 0.12 : 0.4)));
391
398
 
392
399
  // ---- colours --------------------------------------------------------------------------------
@@ -425,7 +432,7 @@ export function Slider(props: SliderProps) {
425
432
  <View>{icon !== undefined ? drawIcon(icon, iconColor) : null}</View>
426
433
  {showValue === 'always' ? (
427
434
  <Text numberOfLines={1} selectable={false} maxFontSizeMultiplier={FONT_CAP} style={[styles.barValue, ff, { color: textColor, fontSize: sz.font + 1 }, labelStyle]}>
428
- {range ? `${fmt(vals[0])} – ${fmt(vals[1])}` : fmt(vals[0])}
435
+ {range ? `${fmt(nth(vals, 0))} – ${fmt(nth(vals, 1))}` : fmt(nth(vals, 0))}
429
436
  </Text>
430
437
  ) : null}
431
438
  </View>
@@ -450,7 +457,7 @@ export function Slider(props: SliderProps) {
450
457
  accessibilityRole={!range ? 'adjustable' : undefined}
451
458
  accessibilityLabel={accessibilityLabel}
452
459
  accessibilityState={{ disabled: !!disabled }}
453
- accessibilityValue={!range ? { min, max, now: vals[0], text: fmt(vals[0]) } : undefined}
460
+ accessibilityValue={!range ? { min, max, now: nth(vals, 0), text: fmt(nth(vals, 0)) } : undefined}
454
461
  accessibilityActions={!range ? [{ name: 'increment' }, { name: 'decrement' }] : undefined}
455
462
  onAccessibilityAction={(e: AccessibilityActionEvent) => stepBy(0, e.nativeEvent.actionName === 'increment' ? 1 : -1)}
456
463
  >
@@ -481,7 +488,7 @@ export function Slider(props: SliderProps) {
481
488
  ) : null}
482
489
 
483
490
  <View
484
- {...pan.panHandlers}
491
+ {...pan}
485
492
  onLayout={(e) => setW(e.nativeEvent.layout.width)}
486
493
  style={[styles.surface, { height: surfaceH }, !disabled && styles.pointer]}
487
494
  >
@@ -535,7 +542,7 @@ export function Slider(props: SliderProps) {
535
542
  key={i}
536
543
  accessible={range}
537
544
  accessibilityRole={range ? 'adjustable' : undefined}
538
- accessibilityLabel={range ? `${accessibilityLabel ?? 'Range'} ${i === 0 ? 'minimum' : 'maximum'}` : undefined}
545
+ accessibilityLabel={range ? (i === 0 ? labels.rangeMinimum : labels.rangeMaximum)(accessibilityLabel ?? labels.range) : undefined}
539
546
  accessibilityValue={range ? { min, max, now: v, text: fmt(v) } : undefined}
540
547
  accessibilityActions={range ? [{ name: 'increment' }, { name: 'decrement' }] : undefined}
541
548
  onAccessibilityAction={(e: AccessibilityActionEvent) => stepBy(i, e.nativeEvent.actionName === 'increment' ? 1 : -1)}