@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,4 +1,4 @@
1
- import React, { createContext, useContext, useEffect, useId, useMemo, useRef, useState, type ComponentRef } from 'react';
1
+ import React, { createContext, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, type ComponentRef } from 'react';
2
2
  import {
3
3
  AccessibilityInfo,
4
4
  Animated,
@@ -18,23 +18,30 @@ import { alpha, motion, useKit } from '@/lib/kit';
18
18
  export type SkeletonVariant = 'shimmer' | 'wave' | 'pulse' | 'static';
19
19
 
20
20
  export interface SkeletonProps {
21
- width?: DimensionValue;
22
- height?: DimensionValue;
21
+ width?: DimensionValue | undefined;
22
+
23
+ height?: DimensionValue | undefined;
24
+
23
25
  /**
24
26
  * Corner radius: a theme step, 'capsule' for bars that follow the kit radius (a pill at the
25
27
  * default radius), 'full' for true pills / circles, or points.
26
28
  */
27
- radius?: 'sm' | 'md' | 'lg' | 'xl' | 'capsule' | 'full' | number;
29
+ radius?: 'sm' | 'md' | 'lg' | 'xl' | 'capsule' | 'full' | number | undefined;
30
+
28
31
  /**
29
32
  * 'shimmer' sweeps one angled light band across every block on screen, 'wave' brightens blocks
30
33
  * one after another from top to bottom, 'pulse' breathes, 'static' does nothing.
31
34
  */
32
- variant?: SkeletonVariant;
35
+ variant?: SkeletonVariant | undefined;
36
+
33
37
  /** Base colour of the block (default: a faint tint of the foreground). */
34
- color?: string;
38
+ color?: string | undefined;
39
+
35
40
  /** Centred overlay, e.g. a faint glyph (see `Skeleton.Media`). */
36
- children?: React.ReactNode;
37
- style?: StyleProp<ViewStyle>;
41
+ children?: React.ReactNode | undefined;
42
+
43
+ style?: StyleProp<ViewStyle> | undefined;
44
+
38
45
  }
39
46
 
40
47
  /* ------------------------------------------------------------------------------------------------
@@ -142,8 +149,10 @@ function measure(node: ComponentRef<typeof View> | null, e: LayoutChangeEvent, d
142
149
  }
143
150
 
144
151
  export interface SkeletonGroupProps {
145
- children?: React.ReactNode;
146
- style?: StyleProp<ViewStyle>;
152
+ children?: React.ReactNode | undefined;
153
+
154
+ style?: StyleProp<ViewStyle> | undefined;
155
+
147
156
  }
148
157
 
149
158
  /**
@@ -216,7 +225,7 @@ export function Skeleton({ width = '100%', height = 14, radius = 'sm', variant =
216
225
  const local = (x: number) => start + (end - start) * easeSweep(x / SWEEP) - dy * TAN - dx - bw / 2;
217
226
  const s = sample(local, 0, SWEEP, 24);
218
227
  s.inputRange.push(1);
219
- s.outputRange.push(s.outputRange[s.outputRange.length - 1]);
228
+ s.outputRange.push(s.outputRange[s.outputRange.length - 1] ?? 0);
220
229
  return t.interpolate(s);
221
230
  }, [mode, B, W, H, dx, dy, bw, t]);
222
231
 
@@ -301,14 +310,20 @@ export function Skeleton({ width = '100%', height = 14, radius = 'sm', variant =
301
310
 
302
311
  export interface SkeletonTextProps {
303
312
  /** Number of lines (default 3). */
304
- lines?: number;
313
+ lines?: number | undefined;
314
+
305
315
  /** Line height in points (default 12). */
306
- lineHeight?: number;
307
- gap?: number;
316
+ lineHeight?: number | undefined;
317
+
318
+ gap?: number | undefined;
319
+
308
320
  /** Width of the last line, so it reads like a paragraph (default '60%'). */
309
- lastLineWidth?: DimensionValue;
310
- variant?: SkeletonVariant;
311
- style?: StyleProp<ViewStyle>;
321
+ lastLineWidth?: DimensionValue | undefined;
322
+
323
+ variant?: SkeletonVariant | undefined;
324
+
325
+ style?: StyleProp<ViewStyle> | undefined;
326
+
312
327
  }
313
328
 
314
329
  const RAGGED: DimensionValue[] = ['100%', '94%', '97%', '90%'];
@@ -331,10 +346,14 @@ export function SkeletonText({ lines = 3, lineHeight = 12, gap = 10, lastLineWid
331
346
  }
332
347
 
333
348
  export interface SkeletonAvatarProps {
334
- size?: number;
335
- shape?: 'circle' | 'rounded';
336
- variant?: SkeletonVariant;
337
- style?: StyleProp<ViewStyle>;
349
+ size?: number | undefined;
350
+
351
+ shape?: 'circle' | 'rounded' | undefined;
352
+
353
+ variant?: SkeletonVariant | undefined;
354
+
355
+ style?: StyleProp<ViewStyle> | undefined;
356
+
338
357
  }
339
358
 
340
359
  /** Round (or rounded-square) avatar placeholder. */
@@ -353,17 +372,25 @@ export function SkeletonAvatar({ size = 40, shape = 'circle', variant, style }:
353
372
 
354
373
  export interface SkeletonMediaProps {
355
374
  /** Width / height ratio (default 16 / 9). Ignored when `height` is set. */
356
- aspectRatio?: number;
375
+ aspectRatio?: number | undefined;
376
+
357
377
  /** Fixed height in points. */
358
- height?: number;
359
- width?: DimensionValue;
378
+ height?: number | undefined;
379
+
380
+ width?: DimensionValue | undefined;
381
+
360
382
  /** Draw a faint image glyph in the centre (default false). */
361
- glyph?: boolean;
383
+ glyph?: boolean | undefined;
384
+
362
385
  /** Glyph size in points (default 28). */
363
- glyphSize?: number;
364
- radius?: SkeletonProps['radius'];
365
- variant?: SkeletonVariant;
366
- style?: StyleProp<ViewStyle>;
386
+ glyphSize?: number | undefined;
387
+
388
+ radius?: SkeletonProps['radius'] | undefined;
389
+
390
+ variant?: SkeletonVariant | undefined;
391
+
392
+ style?: StyleProp<ViewStyle> | undefined;
393
+
367
394
  }
368
395
 
369
396
  /** Image / video placeholder (optionally with a faint picture glyph in the middle). */
@@ -404,16 +431,23 @@ export function SkeletonMedia({
404
431
 
405
432
  export interface SkeletonListProps {
406
433
  /** Number of rows (default 4). */
407
- rows?: number;
434
+ rows?: number | undefined;
435
+
408
436
  /** Avatar size (default 44). 0 hides it. */
409
- avatarSize?: number;
410
- avatarShape?: 'circle' | 'rounded';
437
+ avatarSize?: number | undefined;
438
+
439
+ avatarShape?: 'circle' | 'rounded' | undefined;
440
+
411
441
  /** Trailing short bar, like a time stamp or price (default true). */
412
- trailing?: boolean;
442
+ trailing?: boolean | undefined;
443
+
413
444
  /** Hairline between rows (default false). */
414
- dividers?: boolean;
415
- variant?: SkeletonVariant;
416
- style?: StyleProp<ViewStyle>;
445
+ dividers?: boolean | undefined;
446
+
447
+ variant?: SkeletonVariant | undefined;
448
+
449
+ style?: StyleProp<ViewStyle> | undefined;
450
+
417
451
  }
418
452
 
419
453
  const TITLE_W: DimensionValue[] = ['58%', '44%', '66%', '50%', '62%'];
@@ -458,16 +492,23 @@ export function SkeletonList({
458
492
 
459
493
  export interface SkeletonCardProps {
460
494
  /** Show the media block (default true). */
461
- media?: boolean;
462
- mediaHeight?: number;
495
+ media?: boolean | undefined;
496
+
497
+ mediaHeight?: number | undefined;
498
+
463
499
  /** Show the author row: avatar, name, handle · time, overflow menu (default true). */
464
- header?: boolean;
500
+ header?: boolean | undefined;
501
+
465
502
  /** Body text lines (default 2). */
466
- lines?: number;
503
+ lines?: number | undefined;
504
+
467
505
  /** Like / comment / share row along the bottom (default true). */
468
- actions?: boolean;
469
- variant?: SkeletonVariant;
470
- style?: StyleProp<ViewStyle>;
506
+ actions?: boolean | undefined;
507
+
508
+ variant?: SkeletonVariant | undefined;
509
+
510
+ style?: StyleProp<ViewStyle> | undefined;
511
+
471
512
  }
472
513
 
473
514
  /** A feed post placeholder: author row, caption, media and an action bar — borderless, like the content it stands in for. */
@@ -511,21 +552,30 @@ export function SkeletonCard({
511
552
 
512
553
  export interface SkeletonNavProps {
513
554
  /** App bar: back + title + trailing actions (default true). */
514
- header?: boolean;
555
+ header?: boolean | undefined;
556
+
515
557
  /** Show a back chevron placeholder (default true when header is on). */
516
- back?: boolean;
558
+ back?: boolean | undefined;
559
+
517
560
  /** Trailing action pills in the header (default 2). */
518
- actions?: number;
561
+ actions?: number | undefined;
562
+
519
563
  /** Bottom tab bar (default true). */
520
- tabs?: boolean;
564
+ tabs?: boolean | undefined;
565
+
521
566
  /** Number of bottom tabs (default 4). */
522
- tabCount?: number;
567
+ tabCount?: number | undefined;
568
+
523
569
  /** Body: how many list rows to show while content loads (default 4). 0 = empty body. */
524
- rows?: number;
570
+ rows?: number | undefined;
571
+
525
572
  /** Custom body instead of the default list rows. */
526
- children?: React.ReactNode;
527
- variant?: SkeletonVariant;
528
- style?: StyleProp<ViewStyle>;
573
+ children?: React.ReactNode | undefined;
574
+
575
+ variant?: SkeletonVariant | undefined;
576
+
577
+ style?: StyleProp<ViewStyle> | undefined;
578
+
529
579
  }
530
580
 
531
581
  /**
@@ -586,16 +636,22 @@ export interface SkeletonRevealProps {
586
636
  /** While true the skeleton shows; flip to false when the data arrives. */
587
637
  loading: boolean;
588
638
  /** Placeholder shown while loading (default: three text lines). */
589
- skeleton?: React.ReactNode;
639
+ skeleton?: React.ReactNode | undefined;
640
+
590
641
  /** The real content. Top-level children rise in one after another. */
591
- children?: React.ReactNode;
642
+ children?: React.ReactNode | undefined;
643
+
592
644
  /** Delay between each top-level child rising in, in ms (default 70). 0 reveals it as one block. */
593
- stagger?: number;
645
+ stagger?: number | undefined;
646
+
594
647
  /** Glide the container height from the skeleton's height to the content's (default true). */
595
- animateHeight?: boolean;
596
- /** Screen reader label while loading (default 'Loading'). */
597
- loadingLabel?: string;
598
- style?: StyleProp<ViewStyle>;
648
+ animateHeight?: boolean | undefined;
649
+
650
+ /** Screen reader label while loading (default: the kit's `labels.loading`). */
651
+ loadingLabel?: string | undefined;
652
+
653
+ style?: StyleProp<ViewStyle> | undefined;
654
+
599
655
  }
600
656
 
601
657
  type Layer = 'skeleton' | 'content';
@@ -615,9 +671,10 @@ export function SkeletonReveal({
615
671
  children,
616
672
  stagger = 70,
617
673
  animateHeight = true,
618
- loadingLabel = 'Loading',
674
+ loadingLabel,
619
675
  style,
620
676
  }: SkeletonRevealProps) {
677
+ const { labels } = useKit();
621
678
  const reduce = useReduceMotion();
622
679
  const [mode, setMode] = useState<Layer>(loading ? 'skeleton' : 'content');
623
680
  const [leaving, setLeaving] = useState<Layer | null>(null);
@@ -625,9 +682,9 @@ export function SkeletonReveal({
625
682
  const heights = useRef<Record<Layer, number>>({ skeleton: 0, content: 0 });
626
683
  const pending = useRef(false);
627
684
  const token = useRef(0);
628
- const h = useRef(new Animated.Value(0)).current; // JS driver: container height
629
- const skA = useRef(new Animated.Value(loading ? 1 : 0)).current; // native: skeleton layer
630
- const ctA = useRef(new Animated.Value(loading ? 0 : 1)).current; // native: content layer
685
+ const [h] = useState(() => new Animated.Value(0)); // JS driver: container height
686
+ const [skA] = useState(() => new Animated.Value(loading ? 1 : 0)); // native: skeleton layer
687
+ const [ctA] = useState(() => new Animated.Value(loading ? 0 : 1)); // native: content layer
631
688
 
632
689
  const items = React.Children.toArray(children);
633
690
  const staggered = stagger > 0 && items.length > 1;
@@ -658,37 +715,49 @@ export function SkeletonReveal({
658
715
  });
659
716
  };
660
717
 
661
- useEffect(() => {
718
+ // `loading` flipped: swap layers while rendering, then start the animation in a layout effect so the
719
+ // held height and the new layer's starting opacity are set before that frame paints.
720
+ const [loadingSeen, setLoadingSeen] = useState(loading);
721
+ const [turn, setTurn] = useState<{ next: Layer; prev: Layer; holdHeight: boolean; ready: boolean } | null>(null);
722
+ if (loading !== loadingSeen) {
723
+ setLoadingSeen(loading);
662
724
  const next: Layer = loading ? 'skeleton' : 'content';
663
- if (next === mode) return;
664
- const prev = mode;
725
+ if (next !== mode) {
726
+ if (reduce) {
727
+ setFixed(false);
728
+ setLeaving(null);
729
+ } else {
730
+ if (animateHeight) setFixed(true);
731
+ setLeaving(mode);
732
+ }
733
+ setTurn({ next, prev: mode, holdHeight: animateHeight && !fixed, ready: leaving === next });
734
+ setMode(next);
735
+ }
736
+ }
737
+
738
+ useLayoutEffect(() => {
739
+ if (!turn) return;
740
+ const { next, prev } = turn;
665
741
  if (reduce) {
666
742
  token.current += 1;
667
743
  value(prev).setValue(0);
668
744
  value(next).setValue(1);
669
- setFixed(false);
670
- setLeaving(null);
671
- setMode(next);
672
745
  return;
673
746
  }
674
747
  if (animateHeight) {
675
748
  h.stopAnimation();
676
- if (!fixed) h.setValue(heights.current[prev]);
677
- setFixed(true);
749
+ if (turn.holdHeight) h.setValue(heights.current[prev]);
678
750
  }
679
- const wasMounted = leaving === next;
680
- setLeaving(prev);
681
- setMode(next);
682
- if (wasMounted && heights.current[next] > 0) {
751
+ if (turn.ready && heights.current[next] > 0) {
683
752
  run(next, prev);
684
753
  } else {
685
754
  value(next).setValue(0);
686
755
  pending.current = true;
687
756
  }
688
757
  // eslint-disable-next-line react-hooks/exhaustive-deps
689
- }, [loading]);
758
+ }, [turn]);
690
759
 
691
- const onLayout = (l: Layer) => (e: LayoutChangeEvent) => {
760
+ const measure = (l: Layer, e: LayoutChangeEvent) => {
692
761
  heights.current[l] = e.nativeEvent.layout.height;
693
762
  if (l === mode && pending.current) {
694
763
  pending.current = false;
@@ -726,9 +795,9 @@ export function SkeletonReveal({
726
795
  <Animated.View
727
796
  accessible={mode === 'skeleton'}
728
797
  accessibilityRole="progressbar"
729
- accessibilityLabel={loadingLabel}
798
+ accessibilityLabel={loadingLabel ?? labels.loading}
730
799
  pointerEvents="none"
731
- onLayout={onLayout('skeleton')}
800
+ onLayout={(e) => measure('skeleton', e)}
732
801
  style={[layerStyle('skeleton'), { opacity: skA }]}
733
802
  >
734
803
  <SkeletonGroup>{skeleton ?? <SkeletonText lines={3} />}</SkeletonGroup>
@@ -738,7 +807,7 @@ export function SkeletonReveal({
738
807
  <Animated.View
739
808
  accessibilityLiveRegion="polite"
740
809
  pointerEvents={mode === 'content' ? 'auto' : 'none'}
741
- onLayout={onLayout('content')}
810
+ onLayout={(e) => measure('content', e)}
742
811
  style={[layerStyle('content'), staggered ? null : rise(0)]}
743
812
  >
744
813
  {staggered