@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
@@ -6,6 +6,7 @@ import {
6
6
  Text,
7
7
  View,
8
8
  useColorScheme,
9
+ useWindowDimensions,
9
10
  } from 'react-native';
10
11
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
11
12
 
@@ -17,7 +18,7 @@ interface TabScreenProps {
17
18
  description: string;
18
19
  accent: string;
19
20
  /** Label of a button pinned to the bottom of the screen, above the tab bar. */
20
- action?: string;
21
+ action?: string | undefined;
21
22
  }
22
23
 
23
24
  export function TabScreen({ title, description, accent, action }: TabScreenProps) {
@@ -27,6 +28,12 @@ export function TabScreen({ title, description, accent, action }: TabScreenProps
27
28
  const bottomInset = useTabBarInset(24);
28
29
  // Bottom-anchored UI sits on top of the bar's footprint, not on the screen edge.
29
30
  const actionBottom = useTabBarInset(12);
31
+ // Phones: full width, one card per row. Tablets and open foldables: a centred column, two cards per row.
32
+ const { width } = useWindowDimensions();
33
+ const columnW = Math.min(width, COLUMN_MAX);
34
+ const side = Math.max(0, (width - columnW) / 2) + 20;
35
+ const columns = width >= 600 ? 2 : 1;
36
+ const cardW = columns > 1 ? (columnW - 40 - CARD_GAP * (columns - 1)) / columns : undefined;
30
37
  const bg = isDark ? '#0f0f1a' : '#f2f2f7';
31
38
  const text = isDark ? '#fff' : '#1c1c1e';
32
39
  const sub = isDark ? '#9898a8' : '#6c6c70';
@@ -38,6 +45,7 @@ export function TabScreen({ title, description, accent, action }: TabScreenProps
38
45
  contentContainerStyle={[
39
46
  styles.scroll,
40
47
  { paddingTop: insets.top + 12, paddingBottom: bottomInset },
48
+ { width: '100%', maxWidth: COLUMN_MAX, alignSelf: 'center' },
41
49
  ]}
42
50
  showsVerticalScrollIndicator={false}
43
51
  // Pad by hand on every bar: useTabBarInset() already counts the floating bar and home indicator.
@@ -54,27 +62,29 @@ export function TabScreen({ title, description, accent, action }: TabScreenProps
54
62
  <Text style={[styles.title, { color: text }]}>{title}</Text>
55
63
  <Text style={[styles.subtitle, { color: sub }]}>{description}</Text>
56
64
 
57
- {SECTIONS.map((n) => (
58
- <View
59
- key={n}
60
- style={[
61
- styles.card,
62
- { backgroundColor: isDark ? '#1c1c2e' : '#ffffff' },
63
- ]}
64
- >
65
- <Text style={[styles.cardTitle, { color: text }]}>Section {n}</Text>
66
- <Text style={[styles.cardSub, { color: sub }]}>
67
- iOS 26+ gets the system Liquid Glass bar. Older iOS, Android and web get the
68
- blur glass fallback — both merge into a glass circle when you scroll down.
69
- </Text>
70
- </View>
71
- ))}
65
+ <View style={columns > 1 ? styles.grid : undefined}>
66
+ {SECTIONS.map((n) => (
67
+ <View
68
+ key={n}
69
+ style={[
70
+ styles.card,
71
+ { width: cardW, backgroundColor: isDark ? '#1c1c2e' : '#ffffff' },
72
+ ]}
73
+ >
74
+ <Text style={[styles.cardTitle, { color: text }]}>Section {n}</Text>
75
+ <Text style={[styles.cardSub, { color: sub }]}>
76
+ iOS 26+ gets the system Liquid Glass bar. Older iOS, Android and web get the
77
+ blur glass fallback — both merge into a glass circle when you scroll down.
78
+ </Text>
79
+ </View>
80
+ ))}
81
+ </View>
72
82
  {action ? <View style={{ height: 64 }} /> : null}
73
83
  </ScrollView>
74
84
  {action ? (
75
85
  <Pressable
76
86
  accessibilityRole="button"
77
- style={({ pressed }) => [styles.action, { bottom: actionBottom, backgroundColor: accent, opacity: pressed ? 0.85 : 1 }]}
87
+ style={({ pressed }) => [styles.action, { left: side, right: side, bottom: actionBottom, backgroundColor: accent, opacity: pressed ? 0.85 : 1 }]}
78
88
  >
79
89
  <Text style={styles.actionText}>{action}</Text>
80
90
  </Pressable>
@@ -83,7 +93,7 @@ export function TabScreen({ title, description, accent, action }: TabScreenProps
83
93
  );
84
94
  }
85
95
 
86
- export function TabScreenFromConfig({ tabName, action }: { tabName: string; action?: string }) {
96
+ export function TabScreenFromConfig({ tabName, action }: { tabName: string; action?: string | undefined }) {
87
97
  const tab = getTab(tabName);
88
98
  if (!tab) {
89
99
  return null;
@@ -103,6 +113,9 @@ export function TabScreenFromConfig({ tabName, action }: { tabName: string; acti
103
113
  }
104
114
 
105
115
  const SECTIONS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
116
+ /** Widest the screen's column gets on tablets. */
117
+ const COLUMN_MAX = 720;
118
+ const CARD_GAP = 10;
106
119
 
107
120
  const styles = StyleSheet.create({
108
121
  root: { flex: 1 },
@@ -127,6 +140,7 @@ const styles = StyleSheet.create({
127
140
  },
128
141
  title: { fontSize: 34, fontWeight: '400', letterSpacing: -0.5 },
129
142
  subtitle: { fontSize: 15, marginTop: 8, marginBottom: 20, lineHeight: 22 },
143
+ grid: { flexDirection: 'row', flexWrap: 'wrap', columnGap: CARD_GAP },
130
144
  card: {
131
145
  borderRadius: 14,
132
146
  padding: 16,
@@ -143,5 +157,5 @@ const styles = StyleSheet.create({
143
157
  alignItems: 'center',
144
158
  justifyContent: 'center',
145
159
  },
146
- actionText: { color: '#fff', fontSize: 16, fontWeight: '500' },
160
+ actionText: { color: '#fff', fontSize: 16, fontWeight: '500', includeFontPadding: false, textAlignVertical: 'center' },
147
161
  });
@@ -106,7 +106,7 @@ export function resolveIconNames(tab: {
106
106
  icon?: TabIconPreset;
107
107
  iconName?: string;
108
108
  iconSelected?: string;
109
- }): { name?: string; selected?: string } {
109
+ }): { name?: string | undefined; selected?: string | undefined } {
110
110
  if (tab.icon?.name) {
111
111
  return { name: tab.icon.name, selected: tab.icon.selected };
112
112
  }
@@ -4,6 +4,7 @@
4
4
  "title": "Native bottom tabs (Liquid Glass)",
5
5
  "description": "Real Liquid Glass NativeTabs on iOS 26+, and a floating blur glass tab bar with the same scroll-collapse on iOS 18 and older, Android and web.",
6
6
  "tier": "free",
7
+ "aliases": [],
7
8
  "dependencies": [
8
9
  "expo-router",
9
10
  "expo-glass-effect",
@@ -1,4 +1,4 @@
1
- import React, { useRef, useState } from 'react';
1
+ import React, { useState } from 'react';
2
2
  import {
3
3
  Animated,
4
4
  Image,
@@ -41,61 +41,61 @@ export interface ListItemIconProps {
41
41
 
42
42
  export interface ListItemAvatar {
43
43
  /** Image URL. Falls back to initials while loading or when it fails. */
44
- src?: string;
44
+ src?: string | undefined;
45
45
  /** Used for the initials and the fallback colour. */
46
- name?: string;
46
+ name?: string | undefined;
47
47
  /** Fallback circle colour (default: picked from the name). */
48
- color?: string;
48
+ color?: string | undefined;
49
49
  /** Shows a green presence dot. */
50
- online?: boolean;
50
+ online?: boolean | undefined;
51
51
  }
52
52
 
53
53
  export interface ListItemProps {
54
54
  title: string;
55
55
  /** One muted line under the title. */
56
- subtitle?: string;
56
+ subtitle?: string | undefined;
57
57
  /** Longer muted text under the subtitle, clamped to `numberOfLines`. */
58
- description?: string;
58
+ description?: string | undefined;
59
59
  /** Lines shown of `description` (default 2). */
60
- numberOfLines?: number;
60
+ numberOfLines?: number | undefined;
61
61
  /** Small text at the right end of the title row, e.g. a time ("9:41"). */
62
- meta?: string;
62
+ meta?: string | undefined;
63
63
 
64
64
  /** Built-in icon name or your own icon, drawn in a rounded tile. */
65
- icon?: ListItemIconName | ((props: ListItemIconProps) => React.ReactNode);
65
+ icon?: (ListItemIconName | ((props: ListItemIconProps) => React.ReactNode)) | undefined;
66
66
  /** Tile colour (default: theme accent; the danger colour when `destructive`). */
67
- iconColor?: string;
67
+ iconColor?: string | undefined;
68
68
  /** 'filled' white glyph on a solid tile (iOS Settings) · 'tinted' coloured glyph on a soft tile. */
69
- iconVariant?: 'filled' | 'tinted';
69
+ iconVariant?: 'filled' | 'tinted' | undefined;
70
70
  /** Round avatar instead of an icon tile. */
71
- avatar?: ListItemAvatar;
71
+ avatar?: ListItemAvatar | undefined;
72
72
  /** Anything else on the left (overrides `icon` / `avatar`). */
73
- leading?: React.ReactNode;
73
+ leading?: React.ReactNode | undefined;
74
74
 
75
75
  /** Muted value text on the right, e.g. "English" or "1.2 GB". */
76
- value?: string;
76
+ value?: string | undefined;
77
77
  /** `true` draws a dot; a number or string draws a pill (99+ above 99). */
78
- badge?: boolean | number | string;
78
+ badge?: boolean | number | string | undefined;
79
79
  /** Shows a chevron. Defaults to true when the row is pressable and has no `trailing` / `checked`. */
80
- chevron?: boolean;
80
+ chevron?: boolean | undefined;
81
81
  /** Shows an accent check mark (for pickers). */
82
- checked?: boolean;
82
+ checked?: boolean | undefined;
83
83
  /** Anything on the right, e.g. a <Switch />. Rendered after `value` and `badge`. */
84
- trailing?: React.ReactNode;
84
+ trailing?: React.ReactNode | undefined;
85
85
 
86
- onPress?: () => void;
87
- onLongPress?: () => void;
86
+ onPress?: (() => void) | undefined;
87
+ onLongPress?: (() => void) | undefined;
88
88
  /** Red title and icon, for "Sign out" or "Delete account". */
89
- destructive?: boolean;
90
- disabled?: boolean;
91
- size?: ListItemSize;
89
+ destructive?: boolean | undefined;
90
+ disabled?: boolean | undefined;
91
+ size?: ListItemSize | undefined;
92
92
  /** Hairline under the row that starts past the leading slot. ListGroup / ListSection set it for you. */
93
- divider?: boolean;
93
+ divider?: boolean | undefined;
94
94
  /** Highlight colour for checks, badges and default icon tiles (default: theme accent). */
95
- accent?: string;
96
- titleStyle?: StyleProp<TextStyle>;
97
- accessibilityLabel?: string;
98
- style?: StyleProp<ViewStyle>;
95
+ accent?: string | undefined;
96
+ titleStyle?: StyleProp<TextStyle> | undefined;
97
+ accessibilityLabel?: string | undefined;
98
+ style?: StyleProp<ViewStyle> | undefined;
99
99
  }
100
100
 
101
101
  const SIZES: Record<ListItemSize, { minH: number; tile: number; glyph: number; avatar: number; title: number; sub: number; padY: number }> = {
@@ -109,7 +109,7 @@ const SIZES: Record<ListItemSize, { minH: number; tile: number; glyph: number; a
109
109
  const FONT_CAP = 1.35;
110
110
  const PAD_X = 16;
111
111
  const GAP = 12;
112
- const AVATAR_COLORS = ['#f97316', '#0ea5e9', '#22c55e', '#a855f7', '#ec4899', '#14b8a6', '#6366f1', '#eab308'];
112
+ const AVATAR_COLORS: [string, ...string[]] = ['#f97316', '#0ea5e9', '#22c55e', '#a855f7', '#ec4899', '#14b8a6', '#6366f1', '#eab308'];
113
113
 
114
114
  /**
115
115
  * Settings / contacts style row: icon tile, avatar or custom leading; title, subtitle and
@@ -143,7 +143,7 @@ export function ListItem({
143
143
  accessibilityLabel,
144
144
  style,
145
145
  }: ListItemProps) {
146
- const { colors, radius, font } = useKit();
146
+ const { colors, radius, font, labels } = useKit();
147
147
  const ff = font.family ? { fontFamily: font.family } : null;
148
148
  const tint = accent ?? colors.accent;
149
149
  const s = SIZES[size];
@@ -152,7 +152,7 @@ export function ListItem({
152
152
  const titleColor = destructive ? colors.danger : colors.foreground;
153
153
 
154
154
  // Press highlight: an overlay whose opacity runs on the native driver.
155
- const highlight = useRef(new Animated.Value(0)).current;
155
+ const [highlight] = useState(() => new Animated.Value(0));
156
156
  const fade = (to: number, duration: number) =>
157
157
  Animated.timing(highlight, { toValue: to, duration, useNativeDriver: true }).start();
158
158
 
@@ -257,7 +257,7 @@ export function ListItem({
257
257
  onPressOut={() => fade(0, 320)}
258
258
  accessibilityRole="button"
259
259
  accessibilityState={{ disabled: !!disabled, checked: checked === undefined ? undefined : checked }}
260
- accessibilityLabel={accessibilityLabel ?? [title, subtitle, value, badgeText ? `${badgeText} new` : null].filter(Boolean).join(', ')}
260
+ accessibilityLabel={accessibilityLabel ?? [title, subtitle, value, badgeText ? labels.newCount(String(badgeText)) : null].filter(Boolean).join(', ')}
261
261
  style={rowStyle}
262
262
  >
263
263
  <Animated.View
@@ -274,13 +274,13 @@ export function ListItem({
274
274
 
275
275
  export interface ListGroupProps {
276
276
  /** Small uppercase heading above the card. */
277
- header?: string;
277
+ header?: string | undefined;
278
278
  /** Muted explanation under the card. */
279
- footer?: string;
279
+ footer?: string | undefined;
280
280
  /** Card colour (default: theme card). */
281
- background?: string;
281
+ background?: string | undefined;
282
282
  children: React.ReactNode;
283
- style?: StyleProp<ViewStyle>;
283
+ style?: StyleProp<ViewStyle> | undefined;
284
284
  }
285
285
 
286
286
  /**
@@ -312,12 +312,12 @@ export function ListGroup({ header, footer, background, children, style }: ListG
312
312
 
313
313
  export interface ListSectionProps {
314
314
  /** Section heading, e.g. "A" in a contacts list or "Today" in an inbox. */
315
- header?: string;
316
- footer?: string;
315
+ header?: string | undefined;
316
+ footer?: string | undefined;
317
317
  /** Hairline under the last row too (default false). */
318
- dividerAfterLast?: boolean;
318
+ dividerAfterLast?: boolean | undefined;
319
319
  children: React.ReactNode;
320
- style?: StyleProp<ViewStyle>;
320
+ style?: StyleProp<ViewStyle> | undefined;
321
321
  }
322
322
 
323
323
  /** Plain full-width section: header text, then rows with inset separators. */
@@ -360,7 +360,7 @@ function AvatarCircle({ src, name = '', color, online, size, ring }: ListItemAva
360
360
  .join('');
361
361
  let hash = 0;
362
362
  for (let i = 0; i < name.length; i++) hash = (hash * 31 + name.charCodeAt(i)) | 0;
363
- const bg = color ?? AVATAR_COLORS[Math.abs(hash) % AVATAR_COLORS.length];
363
+ const bg = color ?? AVATAR_COLORS[Math.abs(hash) % AVATAR_COLORS.length] ?? AVATAR_COLORS[0];
364
364
  const dot = Math.max(10, Math.round(size * 0.28));
365
365
  return (
366
366
  <View style={{ width: size, height: size }}>
@@ -507,7 +507,7 @@ const styles = StyleSheet.create({
507
507
  value: { fontSize: 15, flexShrink: 1 },
508
508
  dot: { width: 9, height: 9, borderRadius: 5 },
509
509
  badge: { minWidth: 22, minHeight: 22, paddingHorizontal: 7, alignItems: 'center', justifyContent: 'center' },
510
- badgeText: { fontSize: 12, fontWeight: '400', fontVariant: ['tabular-nums'] },
510
+ badgeText: { fontSize: 12, fontWeight: '400', fontVariant: ['tabular-nums'], includeFontPadding: false, textAlignVertical: 'center' },
511
511
  divider: { position: 'absolute', right: 0, bottom: 0, height: StyleSheet.hairlineWidth },
512
512
  avatar: { alignItems: 'center', justifyContent: 'center', overflow: 'hidden' },
513
513
  online: { position: 'absolute', right: -1, bottom: -1, borderWidth: 2 },