@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
@@ -24,60 +24,60 @@ export interface FloatingInputProps extends Omit<TextInputProps, 'style' | 'plac
24
24
  /** Sits inside the field like a placeholder, then floats onto the border when focused or filled. */
25
25
  label: string;
26
26
  /** `false` puts the label above the field as static text (no notch) and shows `placeholder` inside. */
27
- floatingLabel?: boolean;
27
+ floatingLabel?: boolean | undefined;
28
28
  /** Hint inside the empty field — only used when `floatingLabel` is false. */
29
- placeholder?: string;
30
- variant?: FloatingInputVariant;
29
+ placeholder?: string | undefined;
30
+ variant?: FloatingInputVariant | undefined;
31
31
  /** Error message. Turns the field red, shows it with an icon and shakes the field once when it appears. */
32
- error?: string;
32
+ error?: string | undefined;
33
33
  /** Valid state: success border and an animated check. A string is shown as the message under the field. */
34
- success?: boolean | string;
34
+ success?: boolean | string | undefined;
35
35
  /** Hint under the field (replaced by the error / success message while one is shown). */
36
- helper?: string;
36
+ helper?: string | undefined;
37
37
  /** Colour of the focused border, halo and floating label (default: theme accent). */
38
- accent?: string;
38
+ accent?: string | undefined;
39
39
  /** Background behind the field — the floating label cuts its notch with this colour. */
40
- surface?: string;
41
- left?: React.ReactNode;
42
- right?: React.ReactNode;
40
+ surface?: string | undefined;
41
+ left?: React.ReactNode | undefined;
42
+ right?: React.ReactNode | undefined;
43
43
  /** Text before the value, shown once the label floats — '₹', '+91', 'https://'. */
44
- prefix?: string;
44
+ prefix?: string | undefined;
45
45
  /** Text after the value, shown once the label floats — 'INR', 'kg', '.com'. */
46
- suffix?: string;
46
+ suffix?: string | undefined;
47
47
  /** Show an animated clear button while there is text. */
48
- clearable?: boolean;
48
+ clearable?: boolean | undefined;
49
49
  /** Show `length/maxLength` under the field (needs `maxLength`). */
50
- counter?: boolean;
50
+ counter?: boolean | undefined;
51
51
  /** 'sm' 44pt / 14px · 'md' 54pt / 16px · 'lg' 64pt / 18px — type, padding and icons scale too. */
52
- size?: 'sm' | 'md' | 'lg';
52
+ size?: 'sm' | 'md' | 'lg' | undefined;
53
53
  /** Root wrapper (the one that shakes) — margins, width, flex. */
54
- style?: StyleProp<ViewStyle>;
54
+ style?: StyleProp<ViewStyle> | undefined;
55
55
  /**
56
56
  * The bordered box: background, border width / colour, radius, height, padding. Applied after
57
57
  * the computed style, so it wins. A `borderColor` replaces the resting border only — focus
58
58
  * (accent), error and success colours still take over in those states. A numeric `height` /
59
59
  * `borderRadius` also re-centres the label and resizes the focus halo.
60
60
  */
61
- fieldStyle?: StyleProp<ViewStyle>;
61
+ fieldStyle?: StyleProp<ViewStyle> | undefined;
62
62
  /** The typed text — fontSize, fontWeight, color, letterSpacing, fontFamily… */
63
- inputStyle?: StyleProp<TextStyle>;
63
+ inputStyle?: StyleProp<TextStyle> | undefined;
64
64
  /**
65
65
  * The label, floating or above the field. Its `color` is the resting colour; focus (accent),
66
66
  * error (danger) and success colours still take over in those states. With `fontSize` the
67
67
  * floating label rests at that size and floats at the same ratio as the default, and the notch
68
68
  * follows the rendered label width.
69
69
  */
70
- labelStyle?: StyleProp<TextStyle>;
70
+ labelStyle?: StyleProp<TextStyle> | undefined;
71
71
  /** Helper / error / success text under the field. `color` applies to the helper; error and success keep their tone colour. */
72
- messageStyle?: StyleProp<TextStyle>;
72
+ messageStyle?: StyleProp<TextStyle> | undefined;
73
73
  /** `prefix` text. */
74
- prefixStyle?: StyleProp<TextStyle>;
74
+ prefixStyle?: StyleProp<TextStyle> | undefined;
75
75
  /** `suffix` text. */
76
- suffixStyle?: StyleProp<TextStyle>;
76
+ suffixStyle?: StyleProp<TextStyle> | undefined;
77
77
  /** The `length/maxLength` counter. Turns danger at the limit regardless. */
78
- counterStyle?: StyleProp<TextStyle>;
78
+ counterStyle?: StyleProp<TextStyle> | undefined;
79
79
  /** Built-in icons at rest — the clear button (default: theme mutedForeground). */
80
- iconColor?: string;
80
+ iconColor?: string | undefined;
81
81
  }
82
82
 
83
83
  /** Everything that scales with `size`: height, type, padding, icons and the floated label. */
@@ -169,7 +169,7 @@ export const FloatingInput = forwardRef<ComponentRef<typeof TextInput>, Floating
169
169
  },
170
170
  ref
171
171
  ) {
172
- const { colors, radius, font } = useKit();
172
+ const { colors, radius, font, labels } = useKit();
173
173
  const reduce = useReduceMotion();
174
174
  const ff = font.family ? { fontFamily: font.family } : null;
175
175
  const S = FLOATING_INPUT_SIZES[size];
@@ -197,12 +197,12 @@ export const FloatingInput = forwardRef<ComponentRef<typeof TextInput>, Floating
197
197
  const ok = !error && !!success;
198
198
  const successText = typeof success === 'string' && ok ? success : undefined;
199
199
 
200
- const float = useRef(new Animated.Value(floating ? 1 : 0)).current; // native: label + affixes
201
- const glow = useRef(new Animated.Value(0)).current; // native: halo + indicator bar
202
- const focus = useRef(new Animated.Value(0)).current; // JS: border / label colour
203
- const shake = useRef(new Animated.Value(0)).current; // native
204
- const clear = useRef(new Animated.Value(clearable && text ? 1 : 0)).current; // native
205
- const check = useRef(new Animated.Value(ok ? 1 : 0)).current; // native
200
+ const [float] = useState(() => new Animated.Value(floating ? 1 : 0)); // native: label + affixes
201
+ const [glow] = useState(() => new Animated.Value(0)); // native: halo + indicator bar
202
+ const [focus] = useState(() => new Animated.Value(0)); // JS: border / label colour
203
+ const [shake] = useState(() => new Animated.Value(0)); // native
204
+ const [clear] = useState(() => new Animated.Value(clearable && text ? 1 : 0)); // native
205
+ const [check] = useState(() => new Animated.Value(ok ? 1 : 0)); // native
206
206
 
207
207
  useEffect(() => {
208
208
  if (reduce) float.setValue(floating ? 1 : 0);
@@ -400,7 +400,7 @@ export const FloatingInput = forwardRef<ComponentRef<typeof TextInput>, Floating
400
400
  <Pressable
401
401
  hitSlop={(44 - S.icon) / 2}
402
402
  accessibilityRole="button"
403
- accessibilityLabel={`Clear ${label}`}
403
+ accessibilityLabel={labels.clearField(label)}
404
404
  onPress={() => {
405
405
  setText('');
406
406
  inputRef.current?.focus();
@@ -493,7 +493,7 @@ function FieldMessage({
493
493
  reduce: boolean;
494
494
  }) {
495
495
  const { font } = useKit();
496
- const enter = useRef(new Animated.Value(tone === 'helper' ? 1 : 0)).current; // native
496
+ const [enter] = useState(() => new Animated.Value(tone === 'helper' ? 1 : 0)); // native
497
497
  useEffect(() => {
498
498
  if (tone === 'helper') return;
499
499
  if (reduce) Animated.timing(enter, { toValue: 1, duration: 150, useNativeDriver: true }).start();
@@ -1,4 +1,4 @@
1
- import React, { forwardRef, useEffect, useMemo, useRef, useState, type ComponentRef } from 'react';
1
+ import React, { forwardRef, useEffect, useMemo, useState, type ComponentRef } from 'react';
2
2
  import { AccessibilityInfo, Animated, Pressable, StyleSheet, Text, View, type TextInput } from 'react-native';
3
3
  import Svg, { Circle, Path } from 'react-native-svg';
4
4
 
@@ -21,24 +21,23 @@ export const DEFAULT_PASSWORD_REQUIREMENTS: PasswordRequirement[] = [
21
21
 
22
22
  export interface PasswordInputProps extends Omit<FloatingInputProps, 'secureTextEntry' | 'right'> {
23
23
  /** Show the 4-segment strength meter under the field. */
24
- strength?: boolean;
24
+ strength?: boolean | undefined;
25
25
  /** Replace the built-in scorer (return 0–4). */
26
- score?: (password: string) => number;
26
+ score?: ((password: string) => number) | undefined;
27
27
  /** Checklist under the field; each row ticks itself off when met. `true` uses DEFAULT_PASSWORD_REQUIREMENTS. */
28
- requirements?: PasswordRequirement[] | boolean;
28
+ requirements?: PasswordRequirement[] | boolean | undefined;
29
29
  /** Makes this a "confirm password" field: shows a match / mismatch message once the user types. */
30
- confirmOf?: string;
30
+ confirmOf?: string | undefined;
31
31
  /** Message when `confirmOf` matches (default 'Passwords match'). */
32
- matchMessage?: string;
32
+ matchMessage?: string | undefined;
33
33
  /** Message when `confirmOf` doesn't match (default "Passwords don't match"). */
34
- mismatchMessage?: string;
34
+ mismatchMessage?: string | undefined;
35
35
  }
36
36
 
37
37
  // Controls stop growing at iOS's largest standard text size (xxxL ≈ 1.35×) so they keep their shape;
38
38
  // content text scales all the way.
39
39
  const FONT_CAP = 1.35;
40
40
 
41
- const LEVEL_LABELS = ['Too short', 'Weak', 'Okay', 'Strong', 'Very strong'];
42
41
  const ICON = { sm: 18, md: 22, lg: 24 };
43
42
 
44
43
  export function scorePassword(pw: string) {
@@ -60,8 +59,8 @@ export const PasswordInput = forwardRef<ComponentRef<typeof TextInput>, Password
60
59
  score = scorePassword,
61
60
  requirements,
62
61
  confirmOf,
63
- matchMessage = 'Passwords match',
64
- mismatchMessage = "Passwords don't match",
62
+ matchMessage: matchMessageProp,
63
+ mismatchMessage: mismatchMessageProp,
65
64
  value,
66
65
  defaultValue,
67
66
  onChangeText,
@@ -73,7 +72,9 @@ export const PasswordInput = forwardRef<ComponentRef<typeof TextInput>, Password
73
72
  },
74
73
  ref
75
74
  ) {
76
- const { colors } = useKit();
75
+ const { colors, labels } = useKit();
76
+ const matchMessage = matchMessageProp ?? labels.passwordsMatch;
77
+ const mismatchMessage = mismatchMessageProp ?? labels.passwordsDontMatch;
77
78
  const reduce = useReduceMotion();
78
79
  const [hidden, setHidden] = useState(true);
79
80
  const [inner, setInner] = useState(defaultValue ?? '');
@@ -87,13 +88,15 @@ export const PasswordInput = forwardRef<ComponentRef<typeof TextInput>, Password
87
88
  const mismatch =
88
89
  confirming && text.length > 0 && !matches && (text.length >= confirmOf.length || !confirmOf.startsWith(text));
89
90
 
90
- const eye = useRef(new Animated.Value(hidden ? 0 : 1)).current; // native: 1 = visible
91
+ const [eye] = useState(() => new Animated.Value(hidden ? 0 : 1)); // native: 1 = visible
91
92
  useEffect(() => {
92
93
  if (reduce) Animated.timing(eye, { toValue: hidden ? 0 : 1, duration: 150, useNativeDriver: true }).start();
93
94
  else Animated.spring(eye, { toValue: hidden ? 0 : 1, useNativeDriver: true, ...motion.snappy }).start();
94
95
  }, [hidden, eye, reduce]);
95
96
 
96
97
  const iconColor = iconColorProp ?? colors.mutedForeground;
98
+ const errorText = error ?? (mismatch ? mismatchMessage : undefined);
99
+ const successValue = success ?? (matches ? matchMessage : undefined);
97
100
  const iconSize = ICON[size];
98
101
  const fade = (on: 0 | 1) => ({
99
102
  opacity: eye.interpolate({ inputRange: [0, 1], outputRange: on ? [0, 1] : [1, 0], extrapolate: 'clamp' }),
@@ -112,14 +115,15 @@ export const PasswordInput = forwardRef<ComponentRef<typeof TextInput>, Password
112
115
  ref={ref}
113
116
  {...props}
114
117
  size={size}
115
- iconColor={iconColorProp}
118
+ // FloatingInput's optional props don't accept an explicit undefined (exactOptionalPropertyTypes).
119
+ {...(iconColorProp !== undefined ? { iconColor: iconColorProp } : null)}
116
120
  value={text}
117
121
  onChangeText={(next) => {
118
122
  if (value === undefined) setInner(next);
119
123
  onChangeText?.(next);
120
124
  }}
121
- error={error ?? (mismatch ? mismatchMessage : undefined)}
122
- success={success ?? (matches ? matchMessage : undefined)}
125
+ {...(errorText !== undefined ? { error: errorText } : null)}
126
+ {...(successValue !== undefined ? { success: successValue } : null)}
123
127
  secureTextEntry={hidden}
124
128
  autoCapitalize="none"
125
129
  autoCorrect={false}
@@ -128,7 +132,7 @@ export const PasswordInput = forwardRef<ComponentRef<typeof TextInput>, Password
128
132
  hitSlop={(44 - iconSize) / 2}
129
133
  onPress={() => setHidden((h) => !h)}
130
134
  accessibilityRole="button"
131
- accessibilityLabel={hidden ? 'Show password' : 'Hide password'}
135
+ accessibilityLabel={hidden ? labels.showPassword : labels.hidePassword}
132
136
  accessibilityState={{ checked: !hidden }}
133
137
  style={{ width: iconSize, height: iconSize }}
134
138
  >
@@ -161,11 +165,11 @@ const EYE_OFF =
161
165
 
162
166
  /** Four segments that spring-fill left to right; the colour slides with the score. */
163
167
  function Meter({ level, reduce }: { level: number; reduce: boolean }) {
164
- const { colors, dark, font } = useKit();
168
+ const { colors, dark, font, labels } = useKit();
165
169
  const palette = [colors.mutedForeground, colors.danger, colors.warning, colors.success, colors.success];
166
- const fills = useRef([0, 1, 2, 3].map(() => new Animated.Value(0))).current; // native: scaleX
167
- const tone = useRef(new Animated.Value(level)).current; // JS: colour
168
- const enter = useRef(new Animated.Value(0)).current; // native
170
+ const [fills] = useState(() => [0, 1, 2, 3].map(() => new Animated.Value(0))); // native: scaleX
171
+ const [tone] = useState(() => new Animated.Value(level)); // JS: colour
172
+ const [enter] = useState(() => new Animated.Value(0)); // native
169
173
 
170
174
  useEffect(() => {
171
175
  if (reduce) enter.setValue(1);
@@ -200,7 +204,7 @@ function Meter({ level, reduce }: { level: number; reduce: boolean }) {
200
204
  },
201
205
  ]}
202
206
  accessible
203
- accessibilityLabel={`Password strength: ${LEVEL_LABELS[level]}`}
207
+ accessibilityLabel={labels.passwordStrength(labels.passwordLevels[level] ?? '')}
204
208
  >
205
209
  <View style={styles.bars}>
206
210
  {fills.map((a, i) => (
@@ -216,7 +220,7 @@ function Meter({ level, reduce }: { level: number; reduce: boolean }) {
216
220
  </View>
217
221
  ))}
218
222
  </View>
219
- <Text maxFontSizeMultiplier={FONT_CAP} style={[styles.level, font.family ? { fontFamily: font.family } : null, { color: palette[level] }]}>{LEVEL_LABELS[level]}</Text>
223
+ <Text maxFontSizeMultiplier={FONT_CAP} style={[styles.level, font.family ? { fontFamily: font.family } : null, { color: palette[level] }]}>{labels.passwordLevels[level]}</Text>
220
224
  </Animated.View>
221
225
  );
222
226
  }
@@ -232,8 +236,8 @@ function Checklist({ rules, value, reduce }: { rules: PasswordRequirement[]; val
232
236
  }
233
237
 
234
238
  function Requirement({ label, met, reduce }: { label: string; met: boolean; reduce: boolean }) {
235
- const { colors, dark, font } = useKit();
236
- const tick = useRef(new Animated.Value(met ? 1 : 0)).current; // native
239
+ const { colors, dark, font, labels } = useKit();
240
+ const [tick] = useState(() => new Animated.Value(met ? 1 : 0)); // native
237
241
  useEffect(() => {
238
242
  if (reduce) Animated.timing(tick, { toValue: met ? 1 : 0, duration: 150, useNativeDriver: true }).start();
239
243
  else Animated.spring(tick, { toValue: met ? 1 : 0, useNativeDriver: true, ...motion.bouncy }).start();
@@ -243,7 +247,7 @@ function Requirement({ label, met, reduce }: { label: string; met: boolean; redu
243
247
  <View
244
248
  style={styles.rule}
245
249
  accessible
246
- accessibilityLabel={`${label}: ${met ? 'done' : 'not yet'}`}
250
+ accessibilityLabel={met ? labels.ruleMet(label) : labels.ruleUnmet(label)}
247
251
  accessibilityState={{ checked: met }}
248
252
  >
249
253
  <View style={styles.ruleIcon}>