@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,4 +1,4 @@
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,
@@ -22,26 +22,26 @@ export type AnimatedTextTrigger = 'mount' | 'change';
22
22
  export interface AnimatedTextProps {
23
23
  /** The text to animate. */
24
24
  children: string;
25
- variant?: AnimatedTextVariant;
25
+ variant?: AnimatedTextVariant | undefined;
26
26
  /** Default 'change' for cascade and typewriter (typewriter keeps typing when the new text extends the old), 'mount' otherwise. */
27
- trigger?: AnimatedTextTrigger;
27
+ trigger?: AnimatedTextTrigger | undefined;
28
28
  /** Wait before the first animation, in ms. */
29
- delay?: number;
29
+ delay?: number | undefined;
30
30
  /** Time between words (reveal) or letters (everything else), in ms. Shimmer: ms of sweep per letter. */
31
- stagger?: number;
31
+ stagger?: number | undefined;
32
32
  /** Speed multiplier: 2 plays twice as fast. */
33
- speed?: number;
33
+ speed?: number | undefined;
34
34
  /** Text colour (default: theme foreground). */
35
- color?: string;
35
+ color?: string | undefined;
36
36
  /** Blinking caret for `typewriter` (default true). */
37
- caret?: boolean;
37
+ caret?: boolean | undefined;
38
38
  /** Change it to replay the animation. */
39
- replayKey?: string | number;
39
+ replayKey?: string | number | undefined;
40
40
  /** Called when an animation finishes (never for the looping shimmer). */
41
- onComplete?: () => void;
42
- style?: StyleProp<ViewStyle>;
41
+ onComplete?: (() => void) | undefined;
42
+ style?: StyleProp<ViewStyle> | undefined;
43
43
  /** fontSize, fontWeight, lineHeight, letterSpacing, textAlign… applied to every letter. */
44
- textStyle?: StyleProp<TextStyle>;
44
+ textStyle?: StyleProp<TextStyle> | undefined;
45
45
  }
46
46
 
47
47
  const STAGGER: Record<AnimatedTextVariant, number> = { reveal: 70, shimmer: 80, cascade: 28, scramble: 45, typewriter: 32 };
@@ -158,6 +158,8 @@ interface Ctx {
158
158
  space: number;
159
159
  justify: ViewStyle['justifyContent'];
160
160
  reduce: boolean;
161
+ /** Per-letter delay for building the animations while rendering. */
162
+ stagger: number;
161
163
  /** Latest timing props, read when an animation starts (so re-renders never restart one). */
162
164
  live: React.RefObject<{ delay: number; speed: number; stagger: number }>;
163
165
  done: () => void;
@@ -187,7 +189,9 @@ function Reveal({ layout, ctx }: { layout: Layout; ctx: Ctx }) {
187
189
  const { reduce, live, done, lh } = ctx;
188
190
  const [clock] = useState(() => new Animated.Value(0));
189
191
  const words = useRef(layout.words);
190
- words.current = layout.words;
192
+ useLayoutEffect(() => {
193
+ words.current = layout.words;
194
+ });
191
195
  const played = useRef(false);
192
196
 
193
197
  useEffect(() => {
@@ -210,7 +214,7 @@ function Reveal({ layout, ctx }: { layout: Layout; ctx: Ctx }) {
210
214
  return () => anim.stop();
211
215
  }, [reduce, clock, live, done]);
212
216
 
213
- const stagger = live.current.stagger;
217
+ const stagger = ctx.stagger;
214
218
  const anims = useMemo(
215
219
  () =>
216
220
  layout.lines.flat().map((word) => {
@@ -226,7 +230,8 @@ function Reveal({ layout, ctx }: { layout: Layout; ctx: Ctx }) {
226
230
  return (
227
231
  <Flow layout={layout} ctx={ctx}>
228
232
  {(word) => {
229
- const a = anims[word.w];
233
+ // One entry per word of `layout`, indexed by word.w.
234
+ const a = anims[word.w]!;
230
235
  return (
231
236
  <Animated.Text key={word.w} style={[ctx.glyph, { opacity: a.opacity, transform: [{ translateY: a.translateY }] }]}>
232
237
  {word.text}
@@ -239,10 +244,9 @@ function Reveal({ layout, ctx }: { layout: Layout; ctx: Ctx }) {
239
244
 
240
245
  /** A band of light sweeps across the letters on a loop — one native-driven clock, a per-letter opacity wave. */
241
246
  function Shimmer({ layout, ctx }: { layout: Layout; ctx: Ctx }) {
242
- const { reduce, live } = ctx;
247
+ const { reduce, live, stagger } = ctx;
243
248
  const [clock] = useState(() => new Animated.Value(0));
244
249
  const n = layout.count;
245
- const stagger = live.current.stagger;
246
250
  const sweep = Math.min(2400, Math.max(900, n * stagger));
247
251
  const period = sweep + 650;
248
252
 
@@ -300,13 +304,17 @@ type RollAnim = { opacity: Animated.AnimatedInterpolation<number>; translateY: A
300
304
  function RollWord({ word, anims, glyph, lh }: { word: Word; anims: RollAnim[]; glyph: StyleProp<TextStyle>; lh: number }) {
301
305
  return (
302
306
  <View style={styles.word}>
303
- {word.units.map((u) => (
304
- <View key={u.i} style={{ height: lh, overflow: 'hidden' }}>
305
- <Animated.Text style={[glyph, { opacity: anims[u.i].opacity, transform: [{ translateY: anims[u.i].translateY }] }]}>
306
- {u.ch}
307
- </Animated.Text>
308
- </View>
309
- ))}
307
+ {word.units.map((u) => {
308
+ // One entry per letter of the layout, indexed by u.i.
309
+ const a = anims[u.i]!;
310
+ return (
311
+ <View key={u.i} style={{ height: lh, overflow: 'hidden' }}>
312
+ <Animated.Text style={[glyph, { opacity: a.opacity, transform: [{ translateY: a.translateY }] }]}>
313
+ {u.ch}
314
+ </Animated.Text>
315
+ </View>
316
+ );
317
+ })}
310
318
  </View>
311
319
  );
312
320
  }
@@ -318,28 +326,26 @@ interface Roll {
318
326
  prevWidth: number;
319
327
  animated: boolean;
320
328
  id: number;
329
+ /** A fresh clock per roll, created at 0 so the first frame of a change is already in place. */
330
+ clock: Animated.Value;
321
331
  }
322
332
 
323
333
  /** Old letters roll up and out while the new text's letters roll in from below, left to right. */
324
334
  function Cascade({ text, ctx, changes }: { text: string; ctx: Ctx; changes: boolean }) {
325
335
  const { reduce, live, done, lh, glyph } = ctx;
326
- const width = useRef(0);
327
- const [roll, setRoll] = useState<Roll>({ text, prev: null, prevWidth: 0, animated: true, id: 0 });
336
+ const [width, setWidth] = useState(0);
337
+ const [roll, setRoll] = useState<Roll>(() => ({ text, prev: null, prevWidth: 0, animated: true, id: 0, clock: new Animated.Value(0) }));
328
338
  if (roll.text !== text) {
329
- setRoll({ text, prev: changes ? roll.text : null, prevWidth: width.current, animated: changes, id: roll.id + 1 });
330
- }
331
-
332
- // A fresh clock per roll, created at 0 so the first frame of a change is already in place.
333
- const clockRef = useRef<{ id: number; v: Animated.Value } | null>(null);
334
- if (!clockRef.current || clockRef.current.id !== roll.id) {
335
- clockRef.current = { id: roll.id, v: new Animated.Value(roll.animated ? 0 : END) };
339
+ setRoll({ text, prev: changes ? roll.text : null, prevWidth: width, animated: changes, id: roll.id + 1, clock: new Animated.Value(changes ? 0 : END) });
336
340
  }
337
- const clock = clockRef.current.v;
341
+ const clock = roll.clock;
338
342
 
339
343
  const layout = useMemo(() => tokenize(roll.text), [roll.text]);
340
344
  const prevLayout = useMemo(() => (roll.prev ? tokenize(roll.prev) : null), [roll.prev]);
341
345
  const counts = useRef({ next: 0, prev: 0, animated: true, text });
342
- counts.current = { next: layout.count, prev: prevLayout?.count ?? 0, animated: roll.animated, text: roll.text };
346
+ useLayoutEffect(() => {
347
+ counts.current = { next: layout.count, prev: prevLayout?.count ?? 0, animated: roll.animated, text: roll.text };
348
+ });
343
349
 
344
350
  const id = roll.id;
345
351
  const played = useRef(-1);
@@ -372,7 +378,7 @@ function Cascade({ text, ctx, changes }: { text: string; ctx: Ctx; changes: bool
372
378
  return () => anim.stop();
373
379
  }, [id, reduce, clock, live, done]);
374
380
 
375
- const stagger = live.current.stagger;
381
+ const stagger = ctx.stagger;
376
382
  const enter = useMemo(
377
383
  () =>
378
384
  Array.from({ length: layout.count }, (_, i) => ({
@@ -401,7 +407,7 @@ function Cascade({ text, ctx, changes }: { text: string; ctx: Ctx; changes: bool
401
407
  : { left: 0, width: pw || undefined };
402
408
 
403
409
  return (
404
- <View onLayout={(e: LayoutChangeEvent) => (width.current = e.nativeEvent.layout.width)}>
410
+ <View onLayout={(e: LayoutChangeEvent) => setWidth(e.nativeEvent.layout.width)}>
405
411
  <Flow layout={layout} ctx={ctx}>
406
412
  {(word) => <RollWord key={word.w} word={word} anims={enter} glyph={glyph} lh={lh} />}
407
413
  </Flow>
@@ -436,10 +442,12 @@ function Scramble({ text, layout, ctx }: { text: string; layout: Layout; ctx: Ct
436
442
  // Glyph per letter: null = settled, '' = hidden (whole words of connected scripts fade in instead).
437
443
  const [frame, setFrame] = useState<{ text: string; glyphs: (string | null)[] } | null>(() => ({
438
444
  text,
439
- glyphs: units.map((u, k) => (u.whole ? '' : poolFor(u.ch)[(k * 7 + 3) % poolFor(u.ch).length])),
445
+ glyphs: units.map((u, k) => (u.whole ? '' : poolFor(u.ch).charAt((k * 7 + 3) % poolFor(u.ch).length))),
440
446
  }));
441
447
  const latest = useRef({ text, units });
442
- latest.current = { text, units };
448
+ useLayoutEffect(() => {
449
+ latest.current = { text, units };
450
+ });
443
451
  const played = useRef(false);
444
452
 
445
453
  useEffect(() => {
@@ -462,7 +470,7 @@ function Scramble({ text, layout, ctx }: { text: string; layout: Layout; ctx: Ct
462
470
  pending = true;
463
471
  if (u.whole) return '';
464
472
  const pool = poolFor(u.ch);
465
- return pool[Math.floor(Math.random() * pool.length)];
473
+ return pool.charAt(Math.floor(Math.random() * pool.length));
466
474
  });
467
475
  setFrame(pending ? { text: now, glyphs } : null);
468
476
  if (!pending) {
@@ -600,9 +608,13 @@ export function AnimatedText({
600
608
  flat.textAlign === 'center' ? 'center' : flat.textAlign === 'right' ? 'flex-end' : 'flex-start';
601
609
 
602
610
  const live = useRef({ delay, speed: Math.max(0.1, speed), stagger: stagger ?? STAGGER[variant] });
603
- live.current = { delay, speed: Math.max(0.1, speed), stagger: stagger ?? STAGGER[variant] };
611
+ useLayoutEffect(() => {
612
+ live.current = { delay, speed: Math.max(0.1, speed), stagger: stagger ?? STAGGER[variant] };
613
+ });
604
614
  const complete = useRef(onComplete);
605
- complete.current = onComplete;
615
+ useLayoutEffect(() => {
616
+ complete.current = onComplete;
617
+ });
606
618
  const [done] = useState(() => () => complete.current?.());
607
619
 
608
620
  const layout = useMemo(() => tokenize(text), [text]);
@@ -612,6 +624,7 @@ export function AnimatedText({
612
624
  space: fontSize * 0.28 * fontScale,
613
625
  justify,
614
626
  reduce,
627
+ stagger: stagger ?? STAGGER[variant],
615
628
  live,
616
629
  done,
617
630
  };