@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,5 +1,5 @@
1
1
  import * as Haptics from 'expo-haptics';
2
- import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, type ComponentRef } from 'react';
2
+ import React, { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, type ComponentRef } from 'react';
3
3
  import {
4
4
  AccessibilityInfo,
5
5
  Animated,
@@ -7,7 +7,6 @@ import {
7
7
  Easing,
8
8
  Image,
9
9
  Modal,
10
- PanResponder,
11
10
  Platform,
12
11
  Pressable,
13
12
  ScrollView,
@@ -23,7 +22,7 @@ import {
23
22
  } from 'react-native';
24
23
  import Svg, { Path } from 'react-native-svg';
25
24
 
26
- import { alpha, motion, useKit } from '@/lib/kit';
25
+ import { alpha, motion, useKit, usePanResponder } from '@/lib/kit';
27
26
 
28
27
  export type DrawerSide = 'left' | 'right';
29
28
  /**
@@ -135,11 +134,12 @@ export function Drawer({
135
134
  haptics = true,
136
135
  topInset,
137
136
  bottomInset,
138
- accessibilityLabel = 'Navigation drawer',
137
+ accessibilityLabel: accessibilityLabelProp,
139
138
  style,
140
139
  panelStyle,
141
140
  }: DrawerProps) {
142
- const { colors, dark, radius } = useKit();
141
+ const { colors, dark, radius, labels } = useKit();
142
+ const accessibilityLabel = accessibilityLabelProp ?? labels.navigationDrawer;
143
143
  const reduceMotion = useReduceMotion();
144
144
  const win = useWindowDimensions();
145
145
  const inline = children !== undefined;
@@ -159,13 +159,17 @@ export function Drawer({
159
159
  const [inner, setInner] = useState(defaultOpen);
160
160
  const isOpen = open ?? inner;
161
161
  const [mounted, setMounted] = useState(isOpen); // Modal mode only
162
+ // Mount as soon as it opens (state adjusted during render); unmounted after the exit animation.
163
+ if (isOpen && !inline && !mounted) setMounted(true);
162
164
  // Progress 0 (closed) → 1 (open), native-driven; every transform and opacity reads from it.
163
- const p = useRef(new Animated.Value(isOpen ? 1 : 0)).current;
165
+ const [p] = useState(() => new Animated.Value(isOpen ? 1 : 0));
164
166
  // Same progress on the JS driver, for the screen's corner radius and edge colour (separate view).
165
- const r = useRef(new Animated.Value(isOpen ? 1 : 0)).current;
167
+ const [r] = useState(() => new Animated.Value(isOpen ? 1 : 0));
166
168
 
167
169
  const live = useRef({ open: isOpen, w: W, box, reduce: reduceMotion, haptics, movesScreen, edgeSwipe, edgeWidth, dismissible });
168
- live.current = { open: isOpen, w: W, box, reduce: reduceMotion, haptics, movesScreen, edgeSwipe, edgeWidth, dismissible };
170
+ useLayoutEffect(() => {
171
+ live.current = { open: isOpen, w: W, box, reduce: reduceMotion, haptics, movesScreen, edgeSwipe, edgeWidth, dismissible };
172
+ });
169
173
 
170
174
  const setOpen = useCallback(
171
175
  (next: boolean) => {
@@ -175,7 +179,9 @@ export function Drawer({
175
179
  [open, onOpenChange],
176
180
  );
177
181
  const setOpenRef = useRef(setOpen);
178
- setOpenRef.current = setOpen;
182
+ useLayoutEffect(() => {
183
+ setOpenRef.current = setOpen;
184
+ });
179
185
 
180
186
  const target = useRef<0 | 1>(isOpen ? 1 : 0);
181
187
  const settle = useCallback(
@@ -203,7 +209,6 @@ export function Drawer({
203
209
  );
204
210
 
205
211
  useEffect(() => {
206
- if (isOpen && !inline) setMounted(true);
207
212
  // Skip when a drag release already started a velocity spring towards this state.
208
213
  if (target.current !== (isOpen ? 1 : 0)) settle(isOpen ? 1 : 0);
209
214
  }, [isOpen, settle, inline]);
@@ -242,50 +247,47 @@ export function Drawer({
242
247
  // zone (so taps there still reach the screen), `drag` captures horizontal drags on the open
243
248
  // panel / scrim (so vertical scrolling and taps inside the panel keep working).
244
249
  const start = useRef(0);
245
- const pans = useMemo(() => {
246
- const toward = (dx: number) => (side === 'left' ? dx : -dx); // + means "more open"
247
- const horizontal = (g: PanResponderGestureState) => Math.abs(g.dx) > 10 && Math.abs(g.dx) > Math.abs(g.dy) * 1.5;
248
- const base = {
249
- onPanResponderTerminationRequest: () => false,
250
- onPanResponderGrant: () => {
251
- p.stopAnimation((v) => (start.current = v));
252
- r.stopAnimation();
253
- if (!live.current.open && !inline) setMounted(true);
254
- },
255
- onPanResponderMove: (_: unknown, g: PanResponderGestureState) => {
256
- const w = live.current.w || 1;
257
- let next = start.current + toward(g.dx) / w;
258
- if (next > 1) next = 1 + rubber((next - 1) * w, EXTRA) / w;
259
- if (next < 0) next = 0;
260
- p.setValue(next);
261
- if (live.current.movesScreen) r.setValue(Math.min(1, next));
262
- },
263
- onPanResponderRelease: (_: unknown, g: PanResponderGestureState) => {
264
- const w = live.current.w || 1;
265
- const v = toward(g.vx); // px/ms, + means opening
266
- const projected = start.current + toward(g.dx) / w + (v * 200) / w;
267
- let to: 0 | 1 = projected > 0.5 ? 1 : 0;
268
- if (Math.abs(v) > 0.35) to = v > 0 ? 1 : 0; // a flick decides on its own
269
- settle(to, (v * 1000) / w);
270
- if ((to === 1) !== live.current.open) setOpenRef.current(to === 1);
271
- },
272
- onPanResponderTerminate: () => settle(live.current.open ? 1 : 0),
273
- };
274
- const edge = PanResponder.create({
275
- ...base,
276
- onMoveShouldSetPanResponderCapture: (_, g) => {
277
- const l = live.current;
278
- if (!l.edgeSwipe || l.open || !horizontal(g) || toward(g.dx) <= 0) return false;
279
- const x = g.x0 - rootX.current;
280
- return side === 'left' ? x <= l.edgeWidth : x >= l.box - l.edgeWidth;
281
- },
282
- });
283
- const drag = PanResponder.create({
284
- ...base,
285
- onMoveShouldSetPanResponderCapture: (_, g) => live.current.dismissible && live.current.open && horizontal(g),
286
- });
287
- return { edge, drag };
288
- }, [p, r, side, settle, inline]);
250
+ const toward = (dx: number) => (side === 'left' ? dx : -dx); // + means "more open"
251
+ const horizontal = (g: PanResponderGestureState) => Math.abs(g.dx) > 10 && Math.abs(g.dx) > Math.abs(g.dy) * 1.5;
252
+ const base = {
253
+ onPanResponderTerminationRequest: () => false,
254
+ onPanResponderGrant: () => {
255
+ p.stopAnimation((v) => (start.current = v));
256
+ r.stopAnimation();
257
+ if (!live.current.open && !inline) setMounted(true);
258
+ },
259
+ onPanResponderMove: (_: unknown, g: PanResponderGestureState) => {
260
+ const w = live.current.w || 1;
261
+ let next = start.current + toward(g.dx) / w;
262
+ if (next > 1) next = 1 + rubber((next - 1) * w, EXTRA) / w;
263
+ if (next < 0) next = 0;
264
+ p.setValue(next);
265
+ if (live.current.movesScreen) r.setValue(Math.min(1, next));
266
+ },
267
+ onPanResponderRelease: (_: unknown, g: PanResponderGestureState) => {
268
+ const w = live.current.w || 1;
269
+ const v = toward(g.vx); // px/ms, + means opening
270
+ const projected = start.current + toward(g.dx) / w + (v * 200) / w;
271
+ let to: 0 | 1 = projected > 0.5 ? 1 : 0;
272
+ if (Math.abs(v) > 0.35) to = v > 0 ? 1 : 0; // a flick decides on its own
273
+ settle(to, (v * 1000) / w);
274
+ if ((to === 1) !== live.current.open) setOpenRef.current(to === 1);
275
+ },
276
+ onPanResponderTerminate: () => settle(live.current.open ? 1 : 0),
277
+ };
278
+ const edgePan = usePanResponder({
279
+ ...base,
280
+ onMoveShouldSetPanResponderCapture: (_, g) => {
281
+ const l = live.current;
282
+ if (!l.edgeSwipe || l.open || !horizontal(g) || toward(g.dx) <= 0) return false;
283
+ const x = g.x0 - rootX.current;
284
+ return side === 'left' ? x <= l.edgeWidth : x >= l.box - l.edgeWidth;
285
+ },
286
+ });
287
+ const dragPan = usePanResponder({
288
+ ...base,
289
+ onMoveShouldSetPanResponderCapture: (_, g) => live.current.dismissible && live.current.open && horizontal(g),
290
+ });
289
291
 
290
292
  const ctx = useMemo(
291
293
  () => ({ open: isOpen, setOpen, toggle: () => setOpen(!isOpen), close: () => setOpen(false) }),
@@ -328,7 +330,7 @@ export function Drawer({
328
330
 
329
331
  const panel = (
330
332
  <Animated.View
331
- {...(dismissible ? pans.drag.panHandlers : null)}
333
+ {...(dismissible ? dragPan : null)}
332
334
  accessibilityViewIsModal={isOpen}
333
335
  accessibilityLabel={accessibilityLabel}
334
336
  importantForAccessibility={isOpen ? 'yes' : 'no-hide-descendants'}
@@ -393,7 +395,7 @@ export function Drawer({
393
395
 
394
396
  const scrim = (
395
397
  <Animated.View
396
- {...(dismissible ? pans.drag.panHandlers : null)}
398
+ {...(dismissible ? dragPan : null)}
397
399
  pointerEvents={isOpen ? 'auto' : 'none'}
398
400
  style={[StyleSheet.absoluteFill, { backgroundColor: colors.overlay, opacity: scrimOpacity }]}
399
401
  >
@@ -401,7 +403,7 @@ export function Drawer({
401
403
  style={StyleSheet.absoluteFill}
402
404
  onPress={() => dismissible && setOpen(false)}
403
405
  accessibilityRole="button"
404
- accessibilityLabel="Close drawer"
406
+ accessibilityLabel={labels.closeDrawer}
405
407
  />
406
408
  </Animated.View>
407
409
  );
@@ -421,7 +423,7 @@ export function Drawer({
421
423
  <DrawerContext.Provider value={ctx}>
422
424
  <View
423
425
  ref={rootRef}
424
- {...pans.edge.panHandlers}
426
+ {...edgePan}
425
427
  onTouchStart={measure}
426
428
  onLayout={(e: LayoutChangeEvent) => {
427
429
  setBox(e.nativeEvent.layout.width);
@@ -474,13 +476,15 @@ export interface DrawerHeaderProps {
474
476
  /** Shows a close control on the trailing edge (default false). */
475
477
  showClose?: boolean;
476
478
  onClose?: () => void;
479
+ /** Screen-reader name of the close button (default: the kit's `labels.closeMenu`). */
480
+ closeLabel?: string;
477
481
  children?: React.ReactNode;
478
482
  style?: StyleProp<ViewStyle>;
479
483
  }
480
484
 
481
485
  /** Title block with optional leading node and close button — drop into `header`. */
482
- export function DrawerHeader({ title, subtitle, leading, showClose = false, onClose, children, style }: DrawerHeaderProps) {
483
- const { colors, radius, dark, font } = useKit();
486
+ export function DrawerHeader({ title, subtitle, leading, showClose = false, onClose, closeLabel, children, style }: DrawerHeaderProps) {
487
+ const { colors, radius, dark, font, labels } = useKit();
484
488
  const ff = font.family ? { fontFamily: font.family } : null;
485
489
  const drawerCtx = useContext(DrawerContext);
486
490
  const close = onClose ?? (() => drawerCtx?.close());
@@ -505,7 +509,7 @@ export function DrawerHeader({ title, subtitle, leading, showClose = false, onCl
505
509
  <Pressable
506
510
  onPress={close}
507
511
  accessibilityRole="button"
508
- accessibilityLabel="Close menu"
512
+ accessibilityLabel={closeLabel ?? labels.closeMenu}
509
513
  hitSlop={8}
510
514
  style={({ pressed }) => [
511
515
  styles.closeBtn,
@@ -558,9 +562,9 @@ export function DrawerProfile({
558
562
  accessibilityHint,
559
563
  style,
560
564
  }: DrawerProfileProps) {
561
- const { colors, radius, dark, font } = useKit();
565
+ const { colors, radius, dark, font, labels } = useKit();
562
566
  const ff = font.family ? { fontFamily: font.family } : null;
563
- const press = useRef(new Animated.Value(0)).current;
567
+ const [press] = useState(() => new Animated.Value(0));
564
568
  const stacked = layout === 'stacked';
565
569
  const trailing =
566
570
  accessory !== undefined ? (
@@ -597,7 +601,7 @@ export function DrawerProfile({
597
601
  onPressOut={() => spring(0)}
598
602
  accessibilityRole={onPress ? 'button' : undefined}
599
603
  accessibilityLabel={email ? `${name}, ${email}` : name}
600
- accessibilityHint={accessibilityHint ?? (onPress ? 'Switch account' : undefined)}
604
+ accessibilityHint={accessibilityHint ?? (onPress ? labels.switchAccount : undefined)}
601
605
  style={({ pressed }) => [
602
606
  stacked ? styles.profileStacked : styles.profileRow,
603
607
  {
@@ -626,7 +630,7 @@ export function DrawerProfile({
626
630
  );
627
631
  }
628
632
 
629
- function ProfileAvatar({ name, src, size, status }: { name: string; src?: string; size: number; status?: DrawerPresence }) {
633
+ function ProfileAvatar({ name, src, size, status }: { name: string; src?: string | undefined; size: number; status?: DrawerPresence | undefined }) {
630
634
  const { colors, radius, dark, font } = useKit();
631
635
  const ff = font.family ? { fontFamily: font.family } : null;
632
636
  const ring = useContext(SurfaceContext) ?? colors.card;
@@ -811,10 +815,10 @@ export function DrawerItem({
811
815
  const ink = dark ? tint : mix(tint, colors.foreground, 0.22);
812
816
 
813
817
  // Native-driven: pill presence, icon pop, badge pop, press scale — each on its own value.
814
- const on = useRef(new Animated.Value(active ? 1 : 0)).current;
815
- const pop = useRef(new Animated.Value(1)).current;
816
- const badgePop = useRef(new Animated.Value(1)).current;
817
- const press = useRef(new Animated.Value(0)).current;
818
+ const [on] = useState(() => new Animated.Value(active ? 1 : 0));
819
+ const [pop] = useState(() => new Animated.Value(1));
820
+ const [badgePop] = useState(() => new Animated.Value(1));
821
+ const [press] = useState(() => new Animated.Value(0));
818
822
  const first = useRef(true);
819
823
  const firstBadge = useRef(true);
820
824