@expokit/cli 0.4.3 → 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 (242) hide show
  1. package/README.md +30 -3
  2. package/bin/expo-kit.js +749 -288
  3. package/bin/lib.mjs +313 -4
  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/flip-calendar.json +106 -0
  12. package/registry/_docs/glass-search-bar.json +1 -1
  13. package/registry/_docs/grain.json +6 -1
  14. package/registry/_docs/isometric-bar.json +6 -0
  15. package/registry/_docs/liquidglass-tab.json +1 -1
  16. package/registry/_docs/password-input.json +1 -1
  17. package/registry/_docs/paywall.json +144 -0
  18. package/registry/_docs/pixel-bar.json +6 -0
  19. package/registry/_docs/polar-area.json +6 -0
  20. package/registry/_docs/qr-code-button.json +9 -3
  21. package/registry/_docs/ruler-time-picker.json +147 -0
  22. package/registry/_docs/scratch-card.json +7 -1
  23. package/registry/_docs/search-bar.json +1 -1
  24. package/registry/_docs/select.json +2 -2
  25. package/registry/_docs/slide-to-confirm.json +2 -2
  26. package/registry/_docs/spinner.json +1 -1
  27. package/registry/_docs/stateful-button.json +2 -2
  28. package/registry/_docs/swipe-deck.json +1 -1
  29. package/registry/_docs/time-picker.json +2 -2
  30. package/registry/_docs/timeline-chart.json +6 -0
  31. package/registry/_docs/toast.json +15 -3
  32. package/registry/_docs/treemap.json +6 -0
  33. package/registry/_docs/waterfall.json +6 -0
  34. package/registry/accordion/files/src/components/ui/accordion.tsx +6 -4
  35. package/registry/accordion/files/src/lib/kit.tsx +462 -12
  36. package/registry/accordion.json +1 -0
  37. package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +50 -58
  38. package/registry/action-sheet/files/src/lib/kit.tsx +462 -12
  39. package/registry/action-sheet.json +1 -0
  40. package/registry/alert/files/src/components/ui/alert.tsx +20 -13
  41. package/registry/alert/files/src/lib/kit.tsx +462 -12
  42. package/registry/alert.json +3 -4
  43. package/registry/assistant-app.json +1 -0
  44. package/registry/avatar/files/src/components/ui/avatar.tsx +89 -74
  45. package/registry/avatar/files/src/lib/kit.tsx +462 -12
  46. package/registry/avatar.json +1 -0
  47. package/registry/badge/files/src/components/ui/badge.tsx +36 -36
  48. package/registry/badge/files/src/lib/kit.tsx +462 -12
  49. package/registry/badge.json +1 -0
  50. package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +39 -44
  51. package/registry/bottom-nav/files/src/lib/kit.tsx +462 -12
  52. package/registry/bottom-nav.json +5 -0
  53. package/registry/bottom-sheet/files/src/components/ui/bottom-sheet.tsx +357 -300
  54. package/registry/bottom-sheet/files/src/lib/kit.tsx +462 -12
  55. package/registry/bottom-sheet.json +5 -1
  56. package/registry/bullet.json +1 -0
  57. package/registry/button/files/src/components/ui/button.tsx +23 -34
  58. package/registry/button/files/src/lib/kit.tsx +462 -12
  59. package/registry/button-3d/files/src/components/ui/button-3d.tsx +6 -9
  60. package/registry/button-3d/files/src/lib/kit.tsx +462 -12
  61. package/registry/button-3d.json +1 -0
  62. package/registry/button.json +1 -0
  63. package/registry/calendar/files/src/components/ui/calendar.tsx +77 -79
  64. package/registry/calendar/files/src/lib/kit.tsx +462 -12
  65. package/registry/calendar.json +1 -0
  66. package/registry/card/files/src/components/ui/card.tsx +7 -6
  67. package/registry/card/files/src/lib/kit.tsx +462 -12
  68. package/registry/card-stack.json +1 -0
  69. package/registry/card.json +1 -0
  70. package/registry/carousel.json +1 -0
  71. package/registry/chat.json +7 -0
  72. package/registry/checkbox/files/src/components/ui/checkbox.tsx +4 -4
  73. package/registry/checkbox/files/src/lib/kit.tsx +462 -12
  74. package/registry/checkbox.json +1 -0
  75. package/registry/checkout.json +1 -0
  76. package/registry/chip-group/files/src/components/ui/chip-group.tsx +24 -24
  77. package/registry/chip-group/files/src/lib/kit.tsx +462 -12
  78. package/registry/chip-group.json +1 -0
  79. package/registry/confetti/files/src/components/ui/confetti.tsx +15 -14
  80. package/registry/confetti.json +3 -6
  81. package/registry/coupon/files/src/components/ui/coupon.tsx +47 -52
  82. package/registry/coupon/files/src/components/ui/flip-card.tsx +9 -14
  83. package/registry/coupon/files/src/components/ui/tilt-card.tsx +80 -101
  84. package/registry/coupon/files/src/lib/kit.tsx +462 -12
  85. package/registry/coupon/files/src/lib/ticket-shape.tsx +54 -44
  86. package/registry/coupon.json +1 -0
  87. package/registry/cube-pager.json +1 -0
  88. package/registry/dashboard.json +1 -0
  89. package/registry/date-picker/files/src/components/ui/calendar.tsx +77 -79
  90. package/registry/date-picker/files/src/components/ui/date-picker.tsx +64 -71
  91. package/registry/date-picker/files/src/lib/kit.tsx +462 -12
  92. package/registry/date-picker.json +1 -0
  93. package/registry/dialog/files/src/components/ui/dialog.tsx +66 -68
  94. package/registry/dialog/files/src/lib/kit.tsx +462 -12
  95. package/registry/dialog.json +1 -0
  96. package/registry/donut-shop.json +1 -0
  97. package/registry/drawer/files/src/components/ui/drawer.tsx +76 -88
  98. package/registry/drawer/files/src/lib/kit.tsx +462 -12
  99. package/registry/drawer.json +1 -0
  100. package/registry/fab.json +1 -0
  101. package/registry/fintech-app.json +1 -0
  102. package/registry/flip-calendar/files/src/components/ui/flip-calendar.tsx +632 -0
  103. package/registry/flip-calendar/files/src/lib/kit.tsx +672 -0
  104. package/registry/flip-calendar.json +24 -0
  105. package/registry/flip-card/files/src/components/ui/flip-card.tsx +9 -14
  106. package/registry/flip-card/files/src/lib/kit.tsx +462 -12
  107. package/registry/flip-card.json +1 -0
  108. package/registry/floating-input/files/src/components/ui/floating-input.tsx +33 -33
  109. package/registry/floating-input/files/src/lib/kit.tsx +462 -12
  110. package/registry/floating-input.json +1 -0
  111. package/registry/food-app.json +1 -0
  112. package/registry/gift-card.json +1 -0
  113. package/registry/glass-button.json +5 -0
  114. package/registry/glass-nav-bar.json +1 -0
  115. package/registry/glass-search-bar.json +1 -0
  116. package/registry/glass-segmented.json +1 -0
  117. package/registry/glass-sheet.json +1 -0
  118. package/registry/glass-tab-bar.json +5 -0
  119. package/registry/glass.json +1 -0
  120. package/registry/grain/files/src/components/ui/grain.tsx +16 -14
  121. package/registry/grain.json +8 -6
  122. package/registry/inbox.json +1 -0
  123. package/registry/isometric-bar.json +1 -0
  124. package/registry/kit/files/src/lib/kit.tsx +462 -12
  125. package/registry/kit.json +1 -0
  126. package/registry/liquidglass-tab/files/src/app/_layout.tsx +0 -1
  127. package/registry/liquidglass-tab/files/src/components/GlassTabBar.tsx +29 -16
  128. package/registry/liquidglass-tab/files/src/components/TabScreen.tsx +33 -19
  129. package/registry/liquidglass-tab/files/src/lib/tab-icons.ts +1 -1
  130. package/registry/liquidglass-tab.json +1 -0
  131. package/registry/list-item/files/src/components/ui/list-item.tsx +43 -43
  132. package/registry/list-item/files/src/lib/kit.tsx +462 -12
  133. package/registry/list-item.json +1 -0
  134. package/registry/loyalty-card.json +1 -0
  135. package/registry/morphing-button-group.json +1 -0
  136. package/registry/number-ticker.json +1 -0
  137. package/registry/offers.json +1 -0
  138. package/registry/onboarding.json +1 -0
  139. package/registry/orb-reveal.json +1 -0
  140. package/registry/otp-input/files/src/components/ui/otp-input.tsx +49 -30
  141. package/registry/otp-input/files/src/lib/kit.tsx +462 -12
  142. package/registry/otp-input.json +1 -0
  143. package/registry/parallax-layers.json +1 -0
  144. package/registry/parallax-scroll.json +1 -0
  145. package/registry/password-input/files/src/components/ui/floating-input.tsx +33 -33
  146. package/registry/password-input/files/src/components/ui/password-input.tsx +29 -25
  147. package/registry/password-input/files/src/lib/kit.tsx +462 -12
  148. package/registry/password-input.json +1 -0
  149. package/registry/payment-card/files/src/components/ui/flip-card.tsx +9 -14
  150. package/registry/payment-card/files/src/components/ui/payment-card.tsx +26 -20
  151. package/registry/payment-card/files/src/components/ui/tilt-card.tsx +80 -101
  152. package/registry/payment-card/files/src/lib/kit.tsx +462 -12
  153. package/registry/payment-card.json +1 -0
  154. package/registry/paywall.json +23 -0
  155. package/registry/pixel-bar.json +1 -0
  156. package/registry/polar-area.json +1 -0
  157. package/registry/profile.json +1 -0
  158. package/registry/progress/files/src/components/ui/progress.tsx +31 -29
  159. package/registry/progress/files/src/lib/kit.tsx +462 -12
  160. package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +21 -21
  161. package/registry/progress-steps/files/src/lib/kit.tsx +462 -12
  162. package/registry/progress-steps.json +1 -0
  163. package/registry/progress.json +1 -0
  164. package/registry/qr-code-button.json +1 -0
  165. package/registry/radio-group/files/src/components/ui/radio-group.tsx +19 -19
  166. package/registry/radio-group/files/src/lib/kit.tsx +462 -12
  167. package/registry/radio-group.json +1 -0
  168. package/registry/rating.json +1 -0
  169. package/registry/ring-carousel.json +1 -0
  170. package/registry/ruler-time-picker/files/src/components/ui/ruler-time-picker.tsx +832 -0
  171. package/registry/ruler-time-picker/files/src/lib/kit.tsx +672 -0
  172. package/registry/ruler-time-picker.json +26 -0
  173. package/registry/scratch-card.json +1 -0
  174. package/registry/search-bar/files/src/components/ui/search-bar.tsx +28 -28
  175. package/registry/search-bar/files/src/lib/kit.tsx +462 -12
  176. package/registry/search-bar.json +1 -0
  177. package/registry/search-results.json +4 -0
  178. package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +11 -17
  179. package/registry/segmented-control/files/src/lib/kit.tsx +462 -12
  180. package/registry/segmented-control.json +1 -0
  181. package/registry/select/files/src/components/ui/select.tsx +107 -104
  182. package/registry/select/files/src/lib/kit.tsx +462 -12
  183. package/registry/select.json +6 -0
  184. package/registry/settings.json +1 -0
  185. package/registry/sign-in.json +1 -0
  186. package/registry/skeleton/files/src/components/ui/skeleton.tsx +149 -80
  187. package/registry/skeleton/files/src/lib/kit.tsx +462 -12
  188. package/registry/skeleton.json +1 -0
  189. package/registry/slide-to-confirm.json +1 -0
  190. package/registry/slider/files/src/components/ui/slider.tsx +106 -108
  191. package/registry/slider/files/src/lib/kit.tsx +462 -12
  192. package/registry/slider.json +5 -0
  193. package/registry/spin-wheel.json +1 -0
  194. package/registry/spinner/files/src/components/ui/spinner.tsx +39 -29
  195. package/registry/spinner/files/src/lib/kit.tsx +462 -12
  196. package/registry/spinner.json +6 -0
  197. package/registry/stateful-button/files/src/components/ui/stateful-button.tsx +39 -32
  198. package/registry/stateful-button/files/src/lib/kit.tsx +462 -12
  199. package/registry/stateful-button.json +5 -0
  200. package/registry/stepper/files/src/components/ui/stepper.tsx +40 -38
  201. package/registry/stepper/files/src/lib/kit.tsx +462 -12
  202. package/registry/stepper.json +1 -0
  203. package/registry/swipe-deck.json +1 -0
  204. package/registry/swipeable-row.json +4 -0
  205. package/registry/switch/files/src/components/ui/switch.tsx +41 -47
  206. package/registry/switch/files/src/lib/kit.tsx +462 -12
  207. package/registry/switch.json +1 -0
  208. package/registry/tabs/files/src/components/ui/tabs.tsx +61 -64
  209. package/registry/tabs/files/src/lib/kit.tsx +462 -12
  210. package/registry/tabs.json +1 -0
  211. package/registry/text-animation/files/src/components/ui/text-animation.tsx +55 -42
  212. package/registry/text-animation/files/src/lib/kit.tsx +462 -12
  213. package/registry/text-animation.json +7 -0
  214. package/registry/textarea/files/src/components/ui/textarea.tsx +10 -10
  215. package/registry/textarea/files/src/lib/kit.tsx +462 -12
  216. package/registry/textarea.json +1 -0
  217. package/registry/ticket-card.json +1 -0
  218. package/registry/ticket-shape/files/src/lib/kit.tsx +462 -12
  219. package/registry/ticket-shape/files/src/lib/ticket-shape.tsx +54 -44
  220. package/registry/ticket-shape.json +1 -0
  221. package/registry/tilt-card/files/src/components/ui/tilt-card.tsx +80 -101
  222. package/registry/tilt-card/files/src/lib/kit.tsx +462 -12
  223. package/registry/tilt-card.json +1 -0
  224. package/registry/time-picker/files/src/components/ui/time-picker.tsx +136 -131
  225. package/registry/time-picker/files/src/lib/kit.tsx +462 -12
  226. package/registry/time-picker.json +1 -0
  227. package/registry/timeline/files/src/components/ui/avatar.tsx +89 -74
  228. package/registry/timeline/files/src/components/ui/timeline.tsx +49 -57
  229. package/registry/timeline/files/src/lib/kit.tsx +462 -12
  230. package/registry/timeline-chart.json +1 -0
  231. package/registry/timeline.json +1 -0
  232. package/registry/toast/files/src/components/ui/toast.tsx +543 -465
  233. package/registry/toast/files/src/lib/kit.tsx +462 -12
  234. package/registry/toast.json +7 -5
  235. package/registry/treemap.json +1 -0
  236. package/registry/waterfall.json +1 -0
  237. package/registry/wheel-list.json +1 -0
  238. package/schema/components.schema.json +1 -0
  239. package/registry/alert/files/src/components/ui/button.tsx +0 -303
  240. package/registry/confetti/files/src/lib/kit.tsx +0 -222
  241. package/registry/grain/files/src/lib/kit.tsx +0 -222
  242. package/registry/toast/files/src/components/ui/button.tsx +0 -303
@@ -1,10 +1,9 @@
1
- import React, { useEffect, useMemo, useRef, useState, type ComponentRef } from 'react';
1
+ import React, { useEffect, useLayoutEffect, useMemo, useRef, useState, type ComponentRef } from 'react';
2
2
  import {
3
3
  AccessibilityInfo,
4
4
  Animated,
5
5
  Easing,
6
6
  Modal,
7
- PanResponder,
8
7
  Pressable,
9
8
  ScrollView,
10
9
  StyleSheet,
@@ -26,7 +25,7 @@ import {
26
25
  type CalendarProps,
27
26
  type DateRange,
28
27
  } from '@/components/ui/calendar';
29
- import { alpha, motion, readableOn, useKit } from '@/lib/kit';
28
+ import { alpha, defaultLabels, motion, readableOn, useKit, useLayout, usePanResponder, type Labels } from '@/lib/kit';
30
29
 
31
30
  export type { DateRange } from '@/components/ui/calendar';
32
31
  export type DatePickerSize = 'sm' | 'md' | 'lg';
@@ -148,40 +147,40 @@ const EMPTY_RANGE: DateRange = { start: null, end: null };
148
147
 
149
148
  const isRange = (v: Picked): v is DateRange => !!v && !(v instanceof Date);
150
149
 
151
- /** Built-in single-date shortcuts, computed relative to now. */
152
- export function singleDatePresets(): DatePickerPreset<Date>[] {
150
+ /** Built-in single-date shortcuts, computed relative to now. Pass `useKit().labels` for translated names. */
151
+ export function singleDatePresets(labels: Labels = defaultLabels): DatePickerPreset<Date>[] {
153
152
  const today = () => startOfDay(new Date());
154
153
  return [
155
- { label: 'Today', value: today },
156
- { label: 'Tomorrow', value: () => addDays(today(), 1) },
157
- { label: 'Next Monday', value: () => addDays(today(), ((8 - today().getDay()) % 7) || 7) },
158
- { label: 'In a week', value: () => addDays(today(), 7) },
159
- { label: 'In a month', value: () => new Date(today().getFullYear(), today().getMonth() + 1, today().getDate()) },
154
+ { label: labels.today, value: today },
155
+ { label: labels.tomorrow, value: () => addDays(today(), 1) },
156
+ { label: labels.nextMonday, value: () => addDays(today(), ((8 - today().getDay()) % 7) || 7) },
157
+ { label: labels.inAWeek, value: () => addDays(today(), 7) },
158
+ { label: labels.inAMonth, value: () => new Date(today().getFullYear(), today().getMonth() + 1, today().getDate()) },
160
159
  ];
161
160
  }
162
161
 
163
- /** Built-in range shortcuts, computed relative to now. */
164
- export function rangeDatePresets(): DatePickerPreset<DateRange>[] {
162
+ /** Built-in range shortcuts, computed relative to now. Pass `useKit().labels` for translated names. */
163
+ export function rangeDatePresets(labels: Labels = defaultLabels): DatePickerPreset<DateRange>[] {
165
164
  const today = () => startOfDay(new Date());
166
165
  return [
167
- { label: 'Today', value: () => ({ start: today(), end: today() }) },
168
- { label: 'Next 7 days', value: () => ({ start: today(), end: addDays(today(), 6) }) },
166
+ { label: labels.today, value: () => ({ start: today(), end: today() }) },
167
+ { label: labels.next7Days, value: () => ({ start: today(), end: addDays(today(), 6) }) },
169
168
  {
170
- label: 'Next weekend',
169
+ label: labels.nextWeekend,
171
170
  value: () => {
172
171
  const sat = addDays(today(), ((6 - today().getDay() + 7) % 7) || 7);
173
172
  return { start: sat, end: addDays(sat, 1) };
174
173
  },
175
174
  },
176
175
  {
177
- label: 'This month',
176
+ label: labels.thisMonth,
178
177
  value: () => {
179
178
  const t = today();
180
179
  return { start: new Date(t.getFullYear(), t.getMonth(), 1), end: new Date(t.getFullYear(), t.getMonth() + 1, 0) };
181
180
  },
182
181
  },
183
- { label: 'Last 7 days', value: () => ({ start: addDays(today(), -6), end: today() }) },
184
- { label: 'Last 30 days', value: () => ({ start: addDays(today(), -29), end: today() }) },
182
+ { label: labels.last7Days, value: () => ({ start: addDays(today(), -6), end: today() }) },
183
+ { label: labels.last30Days, value: () => ({ start: addDays(today(), -29), end: today() }) },
185
184
  ];
186
185
  }
187
186
 
@@ -231,7 +230,7 @@ export function DatePicker(props: DatePickerProps) {
231
230
  iconColor,
232
231
  menuStyle,
233
232
  } = props as unknown as FlatProps;
234
- const { colors, radius, font } = useKit();
233
+ const { colors, radius, font, labels } = useKit();
235
234
  const ff = font.family ? { fontFamily: font.family } : null;
236
235
  const tint = accent ?? colors.accent;
237
236
  const bg = surface ?? colors.background;
@@ -244,11 +243,14 @@ export function DatePicker(props: DatePickerProps) {
244
243
  typeof userField.borderRadius === 'number' ? userField.borderRadius : size === 'sm' ? radius.md - 2 : size === 'lg' ? radius.lg : radius.md;
245
244
  const restIcon = iconColor ?? colors.mutedForeground;
246
245
  const { width: winW, height: winH } = useWindowDimensions();
246
+ const layout = useLayout();
247
247
  const ranged = mode === 'range';
248
248
  const reduce = useReduceMotion();
249
249
  // The pan responder is built once; it reads Reduce Motion through this ref.
250
250
  const live = useRef({ reduce });
251
- live.current = { reduce };
251
+ useLayoutEffect(() => {
252
+ live.current = { reduce };
253
+ });
252
254
 
253
255
  const [inner, setInner] = useState<Picked>(defaultValue);
254
256
  const current = value !== undefined ? value : inner;
@@ -264,17 +266,17 @@ export function DatePicker(props: DatePickerProps) {
264
266
 
265
267
  const ok = !error && !!success;
266
268
  const floating = open || hasValue;
267
- const float = useRef(new Animated.Value(floating ? 1 : 0)).current; // native
268
- const focus = useRef(new Animated.Value(0)).current; // JS: border + label colour
269
- const halo = useRef(new Animated.Value(0)).current; // native
270
- const show = useRef(new Animated.Value(0)).current; // native: backdrop + sheet
271
- const drag = useRef(new Animated.Value(0)).current; // native
272
- const shake = useRef(new Animated.Value(0)).current; // native
273
- const check = useRef(new Animated.Value(ok ? 1 : 0)).current; // native
269
+ const [float] = useState(() => new Animated.Value(floating ? 1 : 0)); // native
270
+ const [focus] = useState(() => new Animated.Value(0)); // JS: border + label colour
271
+ const [halo] = useState(() => new Animated.Value(0)); // native
272
+ const [show] = useState(() => new Animated.Value(0)); // native: backdrop + sheet
273
+ const [drag] = useState(() => new Animated.Value(0)); // native
274
+ const [shake] = useState(() => new Animated.Value(0)); // native
275
+ const [check] = useState(() => new Animated.Value(ok ? 1 : 0)); // native
274
276
 
275
277
  const asSheet = presentation !== 'popover';
276
- // Centred dialog instead of a bottom sheet.
277
- const wide = presentation === 'dialog' || (presentation === 'auto' && winW >= 640);
278
+ // Centred dialog instead of a bottom sheet: on foldables, tablets and landscape phones.
279
+ const wide = presentation === 'dialog' || (presentation === 'auto' && (layout.wide || layout.landscape));
278
280
 
279
281
  useEffect(() => {
280
282
  if (reduce) {
@@ -400,9 +402,9 @@ export function DatePicker(props: DatePickerProps) {
400
402
 
401
403
  const presetList = useMemo<DatePickerPreset<Picked>[]>(() => {
402
404
  if (!presets) return [];
403
- if (presets === true) return (ranged ? rangeDatePresets() : singleDatePresets()) as DatePickerPreset<Picked>[];
405
+ if (presets === true) return (ranged ? rangeDatePresets(labels) : singleDatePresets(labels)) as DatePickerPreset<Picked>[];
404
406
  return presets;
405
- }, [presets, ranged]);
407
+ }, [presets, ranged, labels]);
406
408
  const resolve = (p: DatePickerPreset<Picked>) => (typeof p.value === 'function' ? p.value() : p.value);
407
409
  const presetOk = (v: Picked) => {
408
410
  const ds = isRange(v) ? [v.start, v.end] : [v];
@@ -413,23 +415,21 @@ export function DatePicker(props: DatePickerProps) {
413
415
  const same = (a: Picked, b: Picked) =>
414
416
  isRange(a) && isRange(b) ? isSameDay(a.start, b.start) && isSameDay(a.end, b.end) : !isRange(a) && !isRange(b) && isSameDay(a, b);
415
417
 
416
- const pan = useMemo(() => {
417
- const settle = () =>
418
- live.current.reduce
419
- ? Animated.timing(drag, { toValue: 0, duration: 150, easing: Easing.bezier(0.32, 0.72, 0, 1), useNativeDriver: true }).start()
420
- : Animated.spring(drag, { toValue: 0, useNativeDriver: true, ...motion.snappy }).start();
421
- return PanResponder.create({
422
- onMoveShouldSetPanResponder: (_, g) => g.dy > 4 && Math.abs(g.dy) > Math.abs(g.dx),
423
- onPanResponderMove: (_, g) => drag.setValue(Math.max(0, g.dy)),
424
- onPanResponderRelease: (_, g) => {
425
- if (g.dy > 110 || g.vy > 1.1) {
426
- setOpen(false);
427
- onOpenChange?.(false);
428
- } else settle();
429
- },
430
- onPanResponderTerminate: settle,
431
- });
432
- }, [drag, onOpenChange]);
418
+ const settle = () =>
419
+ reduce
420
+ ? Animated.timing(drag, { toValue: 0, duration: 150, easing: Easing.bezier(0.32, 0.72, 0, 1), useNativeDriver: true }).start()
421
+ : Animated.spring(drag, { toValue: 0, useNativeDriver: true, ...motion.snappy }).start();
422
+ const pan = usePanResponder({
423
+ onMoveShouldSetPanResponder: (_, g) => g.dy > 4 && Math.abs(g.dy) > Math.abs(g.dx),
424
+ onPanResponderMove: (_, g) => drag.setValue(Math.max(0, g.dy)),
425
+ onPanResponderRelease: (_, g) => {
426
+ if (g.dy > 110 || g.vy > 1.1) {
427
+ setOpen(false);
428
+ onOpenChange?.(false);
429
+ } else settle();
430
+ },
431
+ onPanResponderTerminate: settle,
432
+ });
433
433
 
434
434
  const active = error ? colors.danger : tint;
435
435
  const rest = error ? colors.danger : ok ? colors.success : ((userBorder as string | undefined) ?? colors.input);
@@ -450,8 +450,8 @@ export function DatePicker(props: DatePickerProps) {
450
450
 
451
451
  const subtitle = isRange(draft)
452
452
  ? draft.start
453
- ? `${fmt.header(draft.start)} → ${draft.end ? fmt.header(draft.end) : 'pick an end date'}`
454
- : 'Pick a start date'
453
+ ? `${fmt.header(draft.start)} → ${draft.end ? fmt.header(draft.end) : labels.pickEndDate}`
454
+ : labels.pickStartDate
455
455
  : draft
456
456
  ? fmt.header(draft)
457
457
  : placeholder ?? null;
@@ -519,7 +519,7 @@ export function DatePicker(props: DatePickerProps) {
519
519
  style={({ pressed }) => [styles.footBtn, { borderRadius: radius.md, opacity: draft ? (pressed ? 0.6 : 1) : 0.4 }]}
520
520
  >
521
521
  <Text maxFontSizeMultiplier={FONT_CAP} style={[styles.footText, ff, { color: colors.foreground }]}>
522
- Clear
522
+ {labels.clear}
523
523
  </Text>
524
524
  </Pressable>
525
525
  <Pressable
@@ -528,7 +528,7 @@ export function DatePicker(props: DatePickerProps) {
528
528
  style={({ pressed }) => [styles.footBtn, styles.footMain, { backgroundColor: tint, borderRadius: radius.md, opacity: pressed ? 0.85 : 1 }]}
529
529
  >
530
530
  <Text maxFontSizeMultiplier={FONT_CAP} style={[styles.footText, ff, { color: readableOn(tint) }]}>
531
- {draftDays > 1 ? `Done · ${draftDays} days` : 'Done'}
531
+ {draftDays > 1 ? labels.doneDays(draftDays) : labels.done}
532
532
  </Text>
533
533
  </Pressable>
534
534
  </View>
@@ -568,7 +568,7 @@ export function DatePicker(props: DatePickerProps) {
568
568
  disabled={disabled}
569
569
  accessibilityRole="button"
570
570
  accessibilityLabel={label}
571
- accessibilityValue={{ text: text || 'Not set' }}
571
+ accessibilityValue={{ text: text || labels.notSet }}
572
572
  accessibilityState={{ expanded: open, disabled: !!disabled }}
573
573
  style={{ opacity: disabled ? 0.5 : 1 }}
574
574
  >
@@ -596,7 +596,7 @@ export function DatePicker(props: DatePickerProps) {
596
596
  !text && { color: placeholderTextColor ?? colors.mutedForeground },
597
597
  ]}
598
598
  >
599
- {text || (open || !floatingLabel ? placeholder ?? (ranged ? 'Select dates' : 'Select a date') : '')}
599
+ {text || (open || !floatingLabel ? placeholder ?? (ranged ? labels.selectDates : labels.selectDate) : '')}
600
600
  </Text>
601
601
  {floatingLabel ? (
602
602
  <Animated.View
@@ -636,7 +636,7 @@ export function DatePicker(props: DatePickerProps) {
636
636
  onPress={() => commit(null)}
637
637
  hitSlop={12}
638
638
  accessibilityRole="button"
639
- accessibilityLabel={`Clear ${label}`}
639
+ accessibilityLabel={labels.clearField(label)}
640
640
  style={[styles.clear, { backgroundColor: alpha(restIcon, 0.22) }]}
641
641
  >
642
642
  <Icon name="x" size={11} color={iconColor ?? colors.foreground} strokeWidth={2.4} />
@@ -650,7 +650,7 @@ export function DatePicker(props: DatePickerProps) {
650
650
 
651
651
  <Modal visible={mounted} transparent animationType="none" statusBarTranslucent onRequestClose={close}>
652
652
  <Animated.View style={[StyleSheet.absoluteFill, { backgroundColor: asSheet ? colors.overlay : 'transparent', opacity: show }]}>
653
- <Pressable style={StyleSheet.absoluteFill} onPress={close} accessibilityLabel="Close" accessibilityRole="button" />
653
+ <Pressable style={StyleSheet.absoluteFill} onPress={close} accessibilityLabel={labels.close} accessibilityRole="button" />
654
654
  </Animated.View>
655
655
  {asSheet ? (
656
656
  <View style={[styles.sheetHost, wide && styles.centered]} pointerEvents="box-none">
@@ -661,7 +661,7 @@ export function DatePicker(props: DatePickerProps) {
661
661
  { backgroundColor: colors.popover, borderColor: colors.border, maxHeight: winH * 0.92 },
662
662
  wide
663
663
  ? { width: Math.min(420, winW - 48), borderRadius: radius.xl, paddingBottom: 14 }
664
- : { alignSelf: 'stretch', borderTopLeftRadius: radius.xl + 4, borderTopRightRadius: radius.xl + 4, paddingBottom: 30 },
664
+ : { width: layout.sheetWidth, alignSelf: 'center', borderTopLeftRadius: radius.xl + 4, borderTopRightRadius: radius.xl + 4, paddingBottom: 30 },
665
665
  menuStyle,
666
666
  wide
667
667
  ? {
@@ -678,7 +678,7 @@ export function DatePicker(props: DatePickerProps) {
678
678
  },
679
679
  ]}
680
680
  >
681
- <View {...pan.panHandlers} style={styles.sheetHeader}>
681
+ <View {...pan} style={styles.sheetHeader}>
682
682
  {!wide ? <View style={[styles.grabber, { backgroundColor: colors.border }]} /> : null}
683
683
  <Text style={[styles.title, ff, { color: colors.foreground }]}>{title ?? label}</Text>
684
684
  {subtitle ? (
@@ -736,7 +736,7 @@ function FieldMessage({
736
736
  style?: StyleProp<TextStyle>;
737
737
  }) {
738
738
  const { colors, font } = useKit();
739
- const enter = useRef(new Animated.Value(tone === 'helper' ? 1 : 0)).current; // native
739
+ const [enter] = useState(() => new Animated.Value(tone === 'helper' ? 1 : 0)); // native
740
740
  useEffect(() => {
741
741
  if (reduce) Animated.timing(enter, { toValue: 1, duration: 150, useNativeDriver: true }).start();
742
742
  else Animated.spring(enter, { toValue: 1, useNativeDriver: true, ...motion.snappy }).start();
@@ -816,11 +816,8 @@ const styles = StyleSheet.create({
816
816
  centered: { justifyContent: 'center', alignItems: 'center' },
817
817
  sheet: {
818
818
  borderWidth: StyleSheet.hairlineWidth,
819
- shadowColor: '#000',
820
- shadowOpacity: 0.18,
821
- shadowRadius: 24,
822
- shadowOffset: { width: 0, height: -4 },
823
- elevation: 16,
819
+ // boxShadow, not shadow* / elevation: the same soft shadow on iOS, Android and web.
820
+ boxShadow: '0px -4px 48px rgba(0,0,0,0.18)',
824
821
  },
825
822
  sheetHeader: { alignItems: 'center', flexShrink: 0, paddingTop: 10, paddingBottom: 8, paddingHorizontal: 16 },
826
823
  grabber: { width: 40, height: 5, borderRadius: 3, marginBottom: 12 },
@@ -830,21 +827,17 @@ const styles = StyleSheet.create({
830
827
  calendarScroll: { flexGrow: 0, flexShrink: 1 },
831
828
  presets: { gap: 8, paddingHorizontal: 16, paddingVertical: 8 },
832
829
  preset: { minHeight: 34, paddingHorizontal: 14, borderWidth: 1, alignItems: 'center', justifyContent: 'center' },
833
- presetText: { fontSize: 13.5, fontWeight: '400' },
830
+ presetText: { fontSize: 13.5, fontWeight: '400', includeFontPadding: false, textAlignVertical: 'center' },
834
831
  calendar: { paddingHorizontal: 16, paddingTop: 4, paddingBottom: 6 },
835
832
  footer: { flexDirection: 'row', flexShrink: 0, gap: 8, paddingTop: 12, paddingHorizontal: 16, borderTopWidth: StyleSheet.hairlineWidth },
836
833
  footBtn: { minHeight: 46, paddingHorizontal: 20, alignItems: 'center', justifyContent: 'center' },
837
834
  footMain: { flex: 1 },
838
- footText: { fontSize: 15, fontWeight: '400' },
835
+ footText: { fontSize: 15, fontWeight: '400', includeFontPadding: false, textAlignVertical: 'center' },
839
836
  popover: {
840
837
  position: 'absolute',
841
838
  paddingTop: 10,
842
839
  paddingBottom: 10,
843
840
  borderWidth: StyleSheet.hairlineWidth,
844
- shadowColor: '#000',
845
- shadowOpacity: 0.16,
846
- shadowRadius: 20,
847
- shadowOffset: { width: 0, height: 10 },
848
- elevation: 14,
841
+ boxShadow: '0px 10px 40px rgba(0,0,0,0.16)',
849
842
  },
850
843
  });