@expokit/cli 0.0.0-stage → 0.2.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 (302) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +32 -2
  3. package/bin/expo-kit.js +1204 -0
  4. package/bin/lib.mjs +343 -0
  5. package/bin/mcp.mjs +338 -0
  6. package/package.json +44 -4
  7. package/registry/_docs/accordion.json +99 -0
  8. package/registry/_docs/action-sheet.json +110 -0
  9. package/registry/_docs/alert.json +127 -0
  10. package/registry/_docs/avatar.json +203 -0
  11. package/registry/_docs/badge.json +117 -0
  12. package/registry/_docs/bottom-nav.json +119 -0
  13. package/registry/_docs/bottom-sheet.json +158 -0
  14. package/registry/_docs/bullet.json +24 -0
  15. package/registry/_docs/button-3d.json +122 -0
  16. package/registry/_docs/button.json +115 -0
  17. package/registry/_docs/calendar.json +182 -0
  18. package/registry/_docs/card-stack.json +108 -0
  19. package/registry/_docs/card.json +111 -0
  20. package/registry/_docs/carousel.json +102 -0
  21. package/registry/_docs/checkbox.json +93 -0
  22. package/registry/_docs/checkout.json +102 -0
  23. package/registry/_docs/chip-group.json +116 -0
  24. package/registry/_docs/color-swatch.json +66 -0
  25. package/registry/_docs/confetti.json +82 -0
  26. package/registry/_docs/coupon.json +176 -0
  27. package/registry/_docs/cube-pager.json +84 -0
  28. package/registry/_docs/dashboard.json +120 -0
  29. package/registry/_docs/date-picker.json +249 -0
  30. package/registry/_docs/dialog.json +162 -0
  31. package/registry/_docs/drawer.json +171 -0
  32. package/registry/_docs/fab.json +120 -0
  33. package/registry/_docs/flip-card.json +111 -0
  34. package/registry/_docs/floating-input.json +191 -0
  35. package/registry/_docs/gift-card.json +108 -0
  36. package/registry/_docs/glass-button.json +118 -0
  37. package/registry/_docs/glass-nav-bar.json +78 -0
  38. package/registry/_docs/glass-search-bar.json +90 -0
  39. package/registry/_docs/glass-segmented.json +66 -0
  40. package/registry/_docs/glass-sheet.json +108 -0
  41. package/registry/_docs/glass-tab-bar.json +76 -0
  42. package/registry/_docs/glass.json +78 -0
  43. package/registry/_docs/inbox.json +72 -0
  44. package/registry/_docs/isometric-bar.json +72 -0
  45. package/registry/_docs/kit.json +11 -0
  46. package/registry/_docs/kpi-card.json +11 -0
  47. package/registry/_docs/like-button.json +72 -0
  48. package/registry/_docs/liquidglass-tab.json +27 -0
  49. package/registry/_docs/list-item.json +200 -0
  50. package/registry/_docs/loyalty-card.json +108 -0
  51. package/registry/_docs/morph-button.json +108 -0
  52. package/registry/_docs/number-ticker.json +96 -0
  53. package/registry/_docs/offers.json +78 -0
  54. package/registry/_docs/onboarding.json +66 -0
  55. package/registry/_docs/otp-input.json +134 -0
  56. package/registry/_docs/pagination-dots.json +88 -0
  57. package/registry/_docs/parallax-layers.json +84 -0
  58. package/registry/_docs/parallax-scroll.json +186 -0
  59. package/registry/_docs/password-input.json +92 -0
  60. package/registry/_docs/payment-card.json +153 -0
  61. package/registry/_docs/pixel-bar.json +66 -0
  62. package/registry/_docs/polar-area.json +78 -0
  63. package/registry/_docs/profile.json +84 -0
  64. package/registry/_docs/progress-steps.json +92 -0
  65. package/registry/_docs/progress.json +132 -0
  66. package/registry/_docs/radio-group.json +92 -0
  67. package/registry/_docs/rating.json +90 -0
  68. package/registry/_docs/ring-carousel.json +102 -0
  69. package/registry/_docs/scratch-card.json +90 -0
  70. package/registry/_docs/search-bar.json +140 -0
  71. package/registry/_docs/search-results.json +120 -0
  72. package/registry/_docs/segmented-control.json +103 -0
  73. package/registry/_docs/select.json +252 -0
  74. package/registry/_docs/settings.json +72 -0
  75. package/registry/_docs/shimmer-button.json +78 -0
  76. package/registry/_docs/sign-in.json +198 -0
  77. package/registry/_docs/skeleton.json +142 -0
  78. package/registry/_docs/slide-to-confirm.json +84 -0
  79. package/registry/_docs/slider.json +157 -0
  80. package/registry/_docs/spin-wheel.json +114 -0
  81. package/registry/_docs/spinner.json +91 -0
  82. package/registry/_docs/stepper.json +168 -0
  83. package/registry/_docs/swipe-deck.json +102 -0
  84. package/registry/_docs/swipeable-row.json +72 -0
  85. package/registry/_docs/switch.json +104 -0
  86. package/registry/_docs/tabs.json +105 -0
  87. package/registry/_docs/tag-input.json +128 -0
  88. package/registry/_docs/textarea.json +175 -0
  89. package/registry/_docs/ticket-card.json +174 -0
  90. package/registry/_docs/ticket-shape.json +11 -0
  91. package/registry/_docs/tilt-card.json +141 -0
  92. package/registry/_docs/time-picker.json +253 -0
  93. package/registry/_docs/timeline-chart.json +30 -0
  94. package/registry/_docs/timeline.json +156 -0
  95. package/registry/_docs/toast.json +161 -0
  96. package/registry/_docs/treemap.json +30 -0
  97. package/registry/_docs/waterfall.json +36 -0
  98. package/registry/_docs/wheel-list.json +102 -0
  99. package/registry/accordion/files/src/components/ui/accordion.tsx +233 -0
  100. package/registry/accordion/files/src/lib/kit.tsx +219 -0
  101. package/registry/accordion.json +17 -0
  102. package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +580 -0
  103. package/registry/action-sheet/files/src/lib/kit.tsx +219 -0
  104. package/registry/action-sheet.json +18 -0
  105. package/registry/alert/files/src/components/ui/alert.tsx +654 -0
  106. package/registry/alert/files/src/components/ui/button.tsx +274 -0
  107. package/registry/alert/files/src/lib/kit.tsx +219 -0
  108. package/registry/alert.json +20 -0
  109. package/registry/avatar/files/src/components/ui/avatar.tsx +790 -0
  110. package/registry/avatar/files/src/lib/kit.tsx +219 -0
  111. package/registry/avatar.json +18 -0
  112. package/registry/badge/files/src/components/ui/badge.tsx +316 -0
  113. package/registry/badge/files/src/lib/kit.tsx +219 -0
  114. package/registry/badge.json +17 -0
  115. package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +1050 -0
  116. package/registry/bottom-nav/files/src/lib/kit.tsx +219 -0
  117. package/registry/bottom-nav.json +18 -0
  118. package/registry/bottom-sheet/files/src/components/ui/bottom-sheet.tsx +585 -0
  119. package/registry/bottom-sheet/files/src/lib/kit.tsx +219 -0
  120. package/registry/bottom-sheet.json +17 -0
  121. package/registry/bullet.json +14 -0
  122. package/registry/button/files/src/components/ui/button.tsx +274 -0
  123. package/registry/button/files/src/lib/kit.tsx +219 -0
  124. package/registry/button-3d/files/src/components/ui/button-3d.tsx +212 -0
  125. package/registry/button-3d/files/src/lib/kit.tsx +219 -0
  126. package/registry/button-3d.json +18 -0
  127. package/registry/button.json +18 -0
  128. package/registry/calendar/files/src/components/ui/calendar.tsx +822 -0
  129. package/registry/calendar/files/src/lib/kit.tsx +219 -0
  130. package/registry/calendar.json +17 -0
  131. package/registry/card/files/src/components/ui/card.tsx +300 -0
  132. package/registry/card/files/src/lib/kit.tsx +219 -0
  133. package/registry/card-stack.json +15 -0
  134. package/registry/card.json +17 -0
  135. package/registry/carousel.json +15 -0
  136. package/registry/checkbox/files/src/components/ui/checkbox.tsx +150 -0
  137. package/registry/checkbox/files/src/lib/kit.tsx +219 -0
  138. package/registry/checkbox.json +17 -0
  139. package/registry/checkout.json +26 -0
  140. package/registry/chip-group/files/src/components/ui/chip-group.tsx +269 -0
  141. package/registry/chip-group/files/src/lib/kit.tsx +219 -0
  142. package/registry/chip-group.json +17 -0
  143. package/registry/color-swatch.json +17 -0
  144. package/registry/confetti/files/src/components/ui/confetti.tsx +222 -0
  145. package/registry/confetti/files/src/lib/kit.tsx +219 -0
  146. package/registry/confetti.json +15 -0
  147. package/registry/coupon/files/src/components/ui/coupon.tsx +698 -0
  148. package/registry/coupon/files/src/components/ui/flip-card.tsx +205 -0
  149. package/registry/coupon/files/src/components/ui/tilt-card.tsx +635 -0
  150. package/registry/coupon/files/src/lib/kit.tsx +219 -0
  151. package/registry/coupon/files/src/lib/ticket-shape.tsx +543 -0
  152. package/registry/coupon.json +23 -0
  153. package/registry/cube-pager.json +15 -0
  154. package/registry/dashboard.json +32 -0
  155. package/registry/date-picker/files/src/components/ui/calendar.tsx +822 -0
  156. package/registry/date-picker/files/src/components/ui/date-picker.tsx +796 -0
  157. package/registry/date-picker/files/src/lib/kit.tsx +219 -0
  158. package/registry/date-picker.json +19 -0
  159. package/registry/dialog/files/src/components/ui/dialog.tsx +759 -0
  160. package/registry/dialog/files/src/lib/kit.tsx +219 -0
  161. package/registry/dialog.json +18 -0
  162. package/registry/drawer/files/src/components/ui/drawer.tsx +1135 -0
  163. package/registry/drawer/files/src/lib/kit.tsx +219 -0
  164. package/registry/drawer.json +18 -0
  165. package/registry/fab.json +18 -0
  166. package/registry/flip-card/files/src/components/ui/flip-card.tsx +205 -0
  167. package/registry/flip-card/files/src/lib/kit.tsx +219 -0
  168. package/registry/flip-card.json +15 -0
  169. package/registry/floating-input/files/src/components/ui/floating-input.tsx +528 -0
  170. package/registry/floating-input/files/src/lib/kit.tsx +219 -0
  171. package/registry/floating-input.json +17 -0
  172. package/registry/gift-card.json +21 -0
  173. package/registry/glass-button.json +22 -0
  174. package/registry/glass-nav-bar.json +21 -0
  175. package/registry/glass-search-bar.json +21 -0
  176. package/registry/glass-segmented.json +22 -0
  177. package/registry/glass-sheet.json +22 -0
  178. package/registry/glass-tab-bar.json +22 -0
  179. package/registry/glass.json +19 -0
  180. package/registry/inbox.json +30 -0
  181. package/registry/isometric-bar.json +14 -0
  182. package/registry/kit/files/src/lib/kit.tsx +219 -0
  183. package/registry/kit.json +11 -0
  184. package/registry/kpi-card/files/src/components/charts/kpi-card.tsx +45 -0
  185. package/registry/kpi-card.json +14 -0
  186. package/registry/like-button.json +18 -0
  187. package/registry/liquidglass-tab/files/src/app/(tabs)/_layout.tsx +97 -0
  188. package/registry/liquidglass-tab/files/src/app/(tabs)/alerts.tsx +5 -0
  189. package/registry/liquidglass-tab/files/src/app/(tabs)/explore.tsx +5 -0
  190. package/registry/liquidglass-tab/files/src/app/(tabs)/index.tsx +5 -0
  191. package/registry/liquidglass-tab/files/src/app/(tabs)/profile.tsx +5 -0
  192. package/registry/liquidglass-tab/files/src/app/_layout.tsx +33 -0
  193. package/registry/liquidglass-tab/files/src/components/GlassTabBar.tsx +259 -0
  194. package/registry/liquidglass-tab/files/src/components/TabScreen.tsx +122 -0
  195. package/registry/liquidglass-tab/files/src/lib/tab-bar.ts +61 -0
  196. package/registry/liquidglass-tab/files/src/lib/tab-icons.ts +130 -0
  197. package/registry/liquidglass-tab/files/src/lib/tabs.ts +108 -0
  198. package/registry/liquidglass-tab.json +23 -0
  199. package/registry/list-item/files/src/components/ui/list-item.tsx +508 -0
  200. package/registry/list-item/files/src/lib/kit.tsx +219 -0
  201. package/registry/list-item.json +17 -0
  202. package/registry/loyalty-card.json +23 -0
  203. package/registry/morph-button.json +18 -0
  204. package/registry/number-ticker.json +15 -0
  205. package/registry/offers.json +24 -0
  206. package/registry/onboarding.json +17 -0
  207. package/registry/otp-input/files/src/components/ui/otp-input.tsx +380 -0
  208. package/registry/otp-input/files/src/lib/kit.tsx +219 -0
  209. package/registry/otp-input.json +17 -0
  210. package/registry/pagination-dots/files/src/components/ui/pagination-dots.tsx +262 -0
  211. package/registry/pagination-dots/files/src/lib/kit.tsx +219 -0
  212. package/registry/pagination-dots.json +15 -0
  213. package/registry/parallax-layers.json +15 -0
  214. package/registry/parallax-scroll.json +17 -0
  215. package/registry/password-input/files/src/components/ui/floating-input.tsx +528 -0
  216. package/registry/password-input/files/src/components/ui/password-input.tsx +271 -0
  217. package/registry/password-input/files/src/lib/kit.tsx +219 -0
  218. package/registry/password-input.json +19 -0
  219. package/registry/payment-card/files/src/components/ui/flip-card.tsx +205 -0
  220. package/registry/payment-card/files/src/components/ui/payment-card.tsx +385 -0
  221. package/registry/payment-card/files/src/components/ui/tilt-card.tsx +635 -0
  222. package/registry/payment-card/files/src/lib/kit.tsx +219 -0
  223. package/registry/payment-card.json +21 -0
  224. package/registry/pixel-bar.json +14 -0
  225. package/registry/polar-area.json +14 -0
  226. package/registry/profile.json +17 -0
  227. package/registry/progress/files/src/components/ui/progress.tsx +883 -0
  228. package/registry/progress/files/src/lib/kit.tsx +219 -0
  229. package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +559 -0
  230. package/registry/progress-steps/files/src/lib/kit.tsx +219 -0
  231. package/registry/progress-steps.json +18 -0
  232. package/registry/progress.json +17 -0
  233. package/registry/radio-group/files/src/components/ui/radio-group.tsx +327 -0
  234. package/registry/radio-group/files/src/lib/kit.tsx +219 -0
  235. package/registry/radio-group.json +18 -0
  236. package/registry/rating.json +18 -0
  237. package/registry/ring-carousel.json +15 -0
  238. package/registry/scratch-card.json +18 -0
  239. package/registry/search-bar/files/src/components/ui/search-bar.tsx +530 -0
  240. package/registry/search-bar/files/src/lib/kit.tsx +219 -0
  241. package/registry/search-bar.json +17 -0
  242. package/registry/search-results.json +30 -0
  243. package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +341 -0
  244. package/registry/segmented-control/files/src/lib/kit.tsx +219 -0
  245. package/registry/segmented-control.json +18 -0
  246. package/registry/select/files/src/components/ui/select.tsx +1398 -0
  247. package/registry/select/files/src/lib/kit.tsx +219 -0
  248. package/registry/select.json +17 -0
  249. package/registry/settings.json +26 -0
  250. package/registry/shimmer-button.json +17 -0
  251. package/registry/sign-in.json +26 -0
  252. package/registry/skeleton/files/src/components/ui/skeleton.tsx +799 -0
  253. package/registry/skeleton/files/src/lib/kit.tsx +219 -0
  254. package/registry/skeleton.json +17 -0
  255. package/registry/slide-to-confirm.json +18 -0
  256. package/registry/slider/files/src/components/ui/slider.tsx +716 -0
  257. package/registry/slider/files/src/lib/kit.tsx +219 -0
  258. package/registry/slider.json +17 -0
  259. package/registry/spin-wheel.json +18 -0
  260. package/registry/spinner/files/src/components/ui/spinner.tsx +335 -0
  261. package/registry/spinner/files/src/lib/kit.tsx +219 -0
  262. package/registry/spinner.json +17 -0
  263. package/registry/stepper/files/src/components/ui/stepper.tsx +646 -0
  264. package/registry/stepper/files/src/lib/kit.tsx +219 -0
  265. package/registry/stepper.json +17 -0
  266. package/registry/swipe-deck.json +15 -0
  267. package/registry/swipeable-row.json +15 -0
  268. package/registry/switch/files/src/components/ui/switch.tsx +344 -0
  269. package/registry/switch/files/src/lib/kit.tsx +219 -0
  270. package/registry/switch.json +18 -0
  271. package/registry/tabs/files/src/components/ui/tabs.tsx +539 -0
  272. package/registry/tabs/files/src/lib/kit.tsx +219 -0
  273. package/registry/tabs.json +17 -0
  274. package/registry/tag-input/files/src/components/ui/tag-input.tsx +720 -0
  275. package/registry/tag-input/files/src/lib/kit.tsx +219 -0
  276. package/registry/tag-input.json +17 -0
  277. package/registry/textarea/files/src/components/ui/textarea.tsx +553 -0
  278. package/registry/textarea/files/src/lib/kit.tsx +219 -0
  279. package/registry/textarea.json +17 -0
  280. package/registry/ticket-card.json +24 -0
  281. package/registry/ticket-shape/files/src/lib/kit.tsx +219 -0
  282. package/registry/ticket-shape/files/src/lib/ticket-shape.tsx +543 -0
  283. package/registry/ticket-shape.json +17 -0
  284. package/registry/tilt-card/files/src/components/ui/tilt-card.tsx +635 -0
  285. package/registry/tilt-card/files/src/lib/kit.tsx +219 -0
  286. package/registry/tilt-card.json +17 -0
  287. package/registry/time-picker/files/src/components/ui/time-picker.tsx +1159 -0
  288. package/registry/time-picker/files/src/lib/kit.tsx +219 -0
  289. package/registry/time-picker.json +17 -0
  290. package/registry/timeline/files/src/components/ui/avatar.tsx +790 -0
  291. package/registry/timeline/files/src/components/ui/timeline.tsx +1059 -0
  292. package/registry/timeline/files/src/lib/kit.tsx +219 -0
  293. package/registry/timeline-chart.json +14 -0
  294. package/registry/timeline.json +20 -0
  295. package/registry/toast/files/src/components/ui/button.tsx +274 -0
  296. package/registry/toast/files/src/components/ui/toast.tsx +1284 -0
  297. package/registry/toast/files/src/lib/kit.tsx +219 -0
  298. package/registry/toast.json +20 -0
  299. package/registry/treemap.json +14 -0
  300. package/registry/waterfall.json +14 -0
  301. package/registry/wheel-list.json +17 -0
  302. package/schema/components.schema.json +13 -0
@@ -0,0 +1,117 @@
1
+ {
2
+ "name": "badge",
3
+ "category": "Display",
4
+ "aliases": [],
5
+ "exportName": "BadgeIcon",
6
+ "files": [
7
+ "src/components/ui/badge.tsx"
8
+ ],
9
+ "props": [
10
+ [
11
+ "variant",
12
+ "\"solid\" | \"soft\" | \"outline\" | \"dot\"",
13
+ "\"soft\"",
14
+ "Filled, tinted, bordered, or a neutral outline with a coloured leading dot."
15
+ ],
16
+ [
17
+ "tone",
18
+ "\"neutral\" | \"accent\" | \"success\" | \"warning\" | \"danger\"",
19
+ "\"neutral\"",
20
+ "Semantic colour."
21
+ ],
22
+ [
23
+ "size",
24
+ "\"sm\" | \"md\" | \"lg\"",
25
+ "\"md\"",
26
+ "20 / 24 / 28pt tall."
27
+ ],
28
+ [
29
+ "color",
30
+ "string",
31
+ "—",
32
+ "Custom colour (overrides tone)."
33
+ ],
34
+ [
35
+ "icon",
36
+ "ReactNode",
37
+ "—",
38
+ "Shown before the label."
39
+ ],
40
+ [
41
+ "shape",
42
+ "\"pill\" | \"rounded\"",
43
+ "\"pill\"",
44
+ "Corner style."
45
+ ],
46
+ [
47
+ "pulse",
48
+ "boolean",
49
+ "false",
50
+ "Pulsing dot for live states (dot variant)."
51
+ ],
52
+ [
53
+ "NotificationBadge.count",
54
+ "number",
55
+ "0",
56
+ "Number shown; 0 hides it."
57
+ ],
58
+ [
59
+ "NotificationBadge.showZero",
60
+ "boolean",
61
+ "false",
62
+ "Keep the badge visible (showing 0) when count is 0."
63
+ ],
64
+ [
65
+ "NotificationBadge.max",
66
+ "number",
67
+ "99",
68
+ "Shows \"99+\" above this."
69
+ ],
70
+ [
71
+ "NotificationBadge.dot",
72
+ "boolean",
73
+ "false",
74
+ "Dot only, no number."
75
+ ],
76
+ [
77
+ "NotificationBadge.children",
78
+ "ReactNode",
79
+ "—",
80
+ "Element the badge is pinned to (top-right)."
81
+ ],
82
+ [
83
+ "NotificationBadge.offset",
84
+ "[number, number]",
85
+ "[0, 0]",
86
+ "Nudge from the corner."
87
+ ],
88
+ [
89
+ "style",
90
+ "StyleProp<ViewStyle>",
91
+ "—",
92
+ "Style for the badge pill; NotificationBadge applies it to the anchor wrapper or bare bubble wrapper."
93
+ ]
94
+ ],
95
+ "examples": [
96
+ {
97
+ "name": "Variants",
98
+ "title": "Variants",
99
+ "code": "import { Pressable, Text, View } from 'react-native';\nimport { Badge, BadgeIcon, BadgeIconTile, NotificationBadge } from '@/components/ui/badge';\n\nconst TONES = ['neutral', 'accent', 'success', 'warning', 'danger'] as const;\n\nexport default function Variants() {\n return (\n <View style={{ gap: 10 }}>\n {(['solid', 'soft', 'outline', 'dot'] as const).map((variant) => (\n <View key={variant} style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 6 }}>\n {TONES.map((tone) => (\n <Badge key={tone} variant={variant} tone={tone}>\n {tone[0].toUpperCase() + tone.slice(1)}\n </Badge>\n ))}\n </View>\n ))}\n </View>\n );\n}\n"
100
+ },
101
+ {
102
+ "name": "Statuses",
103
+ "title": "Statuses",
104
+ "code": "import { Pressable, Text, View } from 'react-native';\nimport { Badge, BadgeIcon, BadgeIconTile, NotificationBadge } from '@/components/ui/badge';\nimport { alpha, useKit } from '@/lib/kit';\n\nexport default function Statuses() {\n const { colors } = useKit();\n return (\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8 }}>\n <Badge variant=\"dot\" tone=\"success\" pulse>Live</Badge>\n <Badge variant=\"soft\" tone=\"success\" icon={<BadgeIcon name=\"package\" color={colors.success} size={14} />}>Paid</Badge>\n <Badge variant=\"soft\" tone=\"warning\">Pending review</Badge>\n <Badge variant=\"outline\" tone=\"danger\">Overdue</Badge>\n <Badge variant=\"solid\" tone=\"accent\" shape=\"rounded\">PRO</Badge>\n <Badge variant=\"soft\" tone=\"accent\" icon={<BadgeIcon name=\"heart\" color={colors.accent} size={14} />}>Saved</Badge>\n </View>\n );\n}\n"
105
+ },
106
+ {
107
+ "name": "Sizes",
108
+ "title": "Sizes",
109
+ "code": "import { Pressable, Text, View } from 'react-native';\nimport { Badge, BadgeIcon, BadgeIconTile, NotificationBadge } from '@/components/ui/badge';\n\nexport default function Sizes() {\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>\n <Badge size=\"sm\" tone=\"accent\">Small</Badge>\n <Badge size=\"md\" tone=\"accent\">Medium</Badge>\n <Badge size=\"lg\" tone=\"accent\">Large</Badge>\n </View>\n );\n}\n"
110
+ },
111
+ {
112
+ "name": "Notifications",
113
+ "title": "Notifications",
114
+ "code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { Badge, BadgeIcon, BadgeIconTile, NotificationBadge } from '@/components/ui/badge';\nimport { alpha, useKit } from '@/lib/kit';\n\nexport default function Notifications() {\n const { colors, radius } = useKit();\n const [count, setCount] = useState(3);\n return (\n <View style={{ gap: 18 }}>\n <View style={{ flexDirection: 'row', gap: 26 }}>\n <NotificationBadge count={count}>\n <BadgeIconTile name=\"bell\" backgroundColor={alpha(colors.accent, 0.12)} color={colors.accent} />\n </NotificationBadge>\n <NotificationBadge count={count * 37} tone=\"accent\">\n <BadgeIconTile name=\"message\" backgroundColor={alpha(colors.accent, 0.1)} color={colors.accent} />\n </NotificationBadge>\n <NotificationBadge count={count} dot tone=\"success\">\n <BadgeIconTile name=\"package\" backgroundColor={alpha(colors.success, 0.12)} color={colors.success} />\n </NotificationBadge>\n </View>\n <View style={{ flexDirection: 'row', gap: 10 }}>\n {[\n ['+ New', () => setCount((c) => c + 1)],\n ['Mark all read', () => setCount(0)],\n ].map(([label, onPress]) => (\n <Pressable\n key={label as string}\n onPress={onPress as () => void}\n style={{ paddingHorizontal: 14, height: 36, borderRadius: radius.capsule(36), justifyContent: 'center', backgroundColor: colors.muted }}\n >\n <Text style={{ fontWeight: '400', color: colors.foreground }}>{label as string}</Text>\n </Pressable>\n ))}\n </View>\n </View>\n );\n}\n"
115
+ }
116
+ ]
117
+ }
@@ -0,0 +1,119 @@
1
+ {
2
+ "name": "bottom-nav",
3
+ "category": "Navigation",
4
+ "aliases": [
5
+ "bottom-tabs",
6
+ "tab-bar",
7
+ "bottom-bar"
8
+ ],
9
+ "exportName": "BottomNav",
10
+ "files": [
11
+ "src/components/ui/bottom-nav.tsx"
12
+ ],
13
+ "props": [
14
+ [
15
+ "items",
16
+ "{ key, label, icon, badge?, action? }[]",
17
+ "—",
18
+ "Tabs. `icon` is a built-in name (home, explore, search, projects, saved, profile, inbox, activity, analytics, message, work, browse, library, music, create) or a render function `({ color, size, active }) => node` — called for both the outline (`active: false`) and filled (`active: true`) states so it can morph."
19
+ ],
20
+ [
21
+ "value / defaultValue",
22
+ "string",
23
+ "first tab",
24
+ "Active tab key (controlled / uncontrolled)."
25
+ ],
26
+ [
27
+ "onValueChange",
28
+ "(key: string) => void",
29
+ "—",
30
+ "Called when a tab is pressed."
31
+ ],
32
+ [
33
+ "onAction",
34
+ "(key: string) => void",
35
+ "—",
36
+ "Called when an `action: true` item (e.g. Create) is pressed. Action items render as a solid accent button and never become active."
37
+ ],
38
+ [
39
+ "variant",
40
+ "\"minimal\" | \"floating\" | \"pill\" | \"center-fab\" | \"indicator\"",
41
+ "\"minimal\"",
42
+ "Look. Deprecated values still render: `glass`, `neumorphic`, `dock`, `curved` → `floating`; `gradient`, `outline` → `pill`."
43
+ ],
44
+ [
45
+ "accent",
46
+ "string",
47
+ "theme accent",
48
+ "Active colour and the action button fill."
49
+ ],
50
+ [
51
+ "gradient",
52
+ "[string, string]",
53
+ "none (solid accent)",
54
+ "Opt-in two-colour gradient for the action button."
55
+ ],
56
+ [
57
+ "showLabels",
58
+ "boolean",
59
+ "true",
60
+ "Show labels. `floating` and `pill` reveal only the active label; the others show all."
61
+ ],
62
+ [
63
+ "bottomInset",
64
+ "number",
65
+ "0",
66
+ "Extra bottom padding — pass `useSafeAreaInsets().bottom` when the bar sits at the screen edge."
67
+ ],
68
+ [
69
+ "homeIndicator",
70
+ "boolean",
71
+ "minimal",
72
+ "Draw the iOS home-indicator line."
73
+ ],
74
+ [
75
+ "haptics",
76
+ "boolean",
77
+ "true",
78
+ "Selection tick on tab change, light impact on the action button."
79
+ ],
80
+ [
81
+ "style",
82
+ "StyleProp<ViewStyle>",
83
+ "—",
84
+ "Style of the bar (the outer shadow view for `floating`)."
85
+ ]
86
+ ],
87
+ "examples": [
88
+ {
89
+ "name": "MinimalClean",
90
+ "title": "Minimal Clean",
91
+ "code": "import React, { useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { BottomNav, type BottomNavItem } from '@/components/ui/bottom-nav';\nimport { alpha, useKit } from '@/lib/kit';\n\ntype Row = [title: string, subtitle: string, meta: string];\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, children }: { title: string; rows: Row[]; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={{\n height: 300,\n alignSelf: 'stretch',\n borderRadius: radius.scale(28),\n overflow: 'hidden',\n borderWidth: 1,\n borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06),\n backgroundColor: colors.background,\n justifyContent: 'space-between',\n }}\n >\n <View style={{ paddingHorizontal: 18, paddingTop: 18, gap: 14 }}>\n <Text style={{ fontSize: 22, fontWeight: '400', letterSpacing: -0.5, color: colors.foreground }}>{title}</Text>\n {rows.map(([t, s, m]) => (\n <View key={t} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View\n style={{\n width: 36,\n height: 36,\n borderRadius: radius.capsule(36),\n backgroundColor: alpha(colors.accent, dark ? 0.22 : 0.12),\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{t.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text numberOfLines={1} style={{ fontSize: 14.5, fontWeight: '400', color: colors.foreground }}>{t}</Text>\n <Text numberOfLines={1} style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{s}</Text>\n </View>\n <Text style={{ fontSize: 12, fontWeight: '400', color: colors.mutedForeground }}>{m}</Text>\n </View>\n ))}\n </View>\n {children}\n </View>\n );\n}\n\nconst ROWS: Record<string, Row[]> = {\n home: [\n ['Maya Chen', 'Shipped the onboarding redesign', '2m'],\n ['Leo Park', 'Booked the team offsite for June', '1h'],\n ],\n explore: [\n ['Design Systems Weekly', 'Motion tokens that scale', 'New'],\n ['Product Notes', 'How Linear plans a cycle', '4m read'],\n ],\n search: [\n ['Recent: invoices', 'Q2 invoices · 14 results', 'Today'],\n ['Recent: Maya', 'People · Design team', 'Mon'],\n ],\n projects: [\n ['Mobile app 2.0', '18 of 24 tasks done', '75%'],\n ['Brand refresh', 'Review on Thursday', '40%'],\n ],\n saved: [\n ['Kyoto travel guide', 'Saved from Atlas', 'Apr 2'],\n ['Sourdough basics', 'Saved from Recipes', 'Mar 28'],\n ],\n inbox: [\n ['Priya Nair', 'Can you review the pricing page?', '9:41'],\n ['GitHub', 'PR #482 was approved', '9:12'],\n ],\n analytics: [\n ['Weekly active users', '12,480 · up 8.2%', '7d'],\n ['Conversion', '3.4% · up 0.6 pts', '7d'],\n ],\n activity: [\n ['Leo Park', 'Commented on Launch plan', '5m'],\n ['Ana Ruiz', 'Moved Pricing to Done', '22m'],\n ],\n profile: [\n ['Karan Kumar', 'Product designer · Berlin', 'Edit'],\n ['Notifications', 'Mentions and replies only', 'On'],\n ],\n home2: [\n ['Late Night Drive', 'Playlist · 42 songs', '2h 31m'],\n ['Midnight City', 'M83 · Hurry Up, We\\'re Dreaming', '4:03'],\n ],\n browse: [\n ['New this week', 'Fresh releases picked for you', '24'],\n ['Focus', 'Instrumental, low tempo', '60'],\n ],\n find: [\n ['Recent: Tycho', 'Artist · Ambient', 'Yesterday'],\n ['Recent: lo-fi beats', 'Playlist · 120 songs', 'Sun'],\n ],\n library: [\n ['Liked songs', '1,204 songs', 'Pinned'],\n ['Downloaded', '86 songs · 412 MB', 'Offline'],\n ],\n};\n\nconst titleOf = (items: BottomNavItem[], key: string) => items.find((i) => i.key === key)?.label ?? '';\n\nexport default function MinimalClean() {\n const items: BottomNavItem[] = [\n { key: 'home', label: 'Home', icon: 'home' },\n { key: 'explore', label: 'Explore', icon: 'explore' },\n { key: 'projects', label: 'Projects', icon: 'projects' },\n { key: 'saved', label: 'Saved', icon: 'saved' },\n { key: 'profile', label: 'Profile', icon: 'profile' },\n ];\n const [tab, setTab] = useState('home');\n return (\n <Screen title={titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav variant=\"minimal\" items={items} value={tab} onValueChange={setTab} />\n </Screen>\n );\n}\n"
92
+ },
93
+ {
94
+ "name": "Floating",
95
+ "title": "Floating",
96
+ "code": "import React, { useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { BottomNav, type BottomNavItem } from '@/components/ui/bottom-nav';\nimport { alpha, useKit } from '@/lib/kit';\n\ntype Row = [title: string, subtitle: string, meta: string];\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, children }: { title: string; rows: Row[]; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={{\n height: 300,\n alignSelf: 'stretch',\n borderRadius: radius.scale(28),\n overflow: 'hidden',\n borderWidth: 1,\n borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06),\n backgroundColor: colors.background,\n justifyContent: 'space-between',\n }}\n >\n <View style={{ paddingHorizontal: 18, paddingTop: 18, gap: 14 }}>\n <Text style={{ fontSize: 22, fontWeight: '400', letterSpacing: -0.5, color: colors.foreground }}>{title}</Text>\n {rows.map(([t, s, m]) => (\n <View key={t} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View\n style={{\n width: 36,\n height: 36,\n borderRadius: radius.capsule(36),\n backgroundColor: alpha(colors.accent, dark ? 0.22 : 0.12),\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{t.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text numberOfLines={1} style={{ fontSize: 14.5, fontWeight: '400', color: colors.foreground }}>{t}</Text>\n <Text numberOfLines={1} style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{s}</Text>\n </View>\n <Text style={{ fontSize: 12, fontWeight: '400', color: colors.mutedForeground }}>{m}</Text>\n </View>\n ))}\n </View>\n {children}\n </View>\n );\n}\n\nconst ROWS: Record<string, Row[]> = {\n home: [\n ['Maya Chen', 'Shipped the onboarding redesign', '2m'],\n ['Leo Park', 'Booked the team offsite for June', '1h'],\n ],\n explore: [\n ['Design Systems Weekly', 'Motion tokens that scale', 'New'],\n ['Product Notes', 'How Linear plans a cycle', '4m read'],\n ],\n search: [\n ['Recent: invoices', 'Q2 invoices · 14 results', 'Today'],\n ['Recent: Maya', 'People · Design team', 'Mon'],\n ],\n projects: [\n ['Mobile app 2.0', '18 of 24 tasks done', '75%'],\n ['Brand refresh', 'Review on Thursday', '40%'],\n ],\n saved: [\n ['Kyoto travel guide', 'Saved from Atlas', 'Apr 2'],\n ['Sourdough basics', 'Saved from Recipes', 'Mar 28'],\n ],\n inbox: [\n ['Priya Nair', 'Can you review the pricing page?', '9:41'],\n ['GitHub', 'PR #482 was approved', '9:12'],\n ],\n analytics: [\n ['Weekly active users', '12,480 · up 8.2%', '7d'],\n ['Conversion', '3.4% · up 0.6 pts', '7d'],\n ],\n activity: [\n ['Leo Park', 'Commented on Launch plan', '5m'],\n ['Ana Ruiz', 'Moved Pricing to Done', '22m'],\n ],\n profile: [\n ['Karan Kumar', 'Product designer · Berlin', 'Edit'],\n ['Notifications', 'Mentions and replies only', 'On'],\n ],\n home2: [\n ['Late Night Drive', 'Playlist · 42 songs', '2h 31m'],\n ['Midnight City', 'M83 · Hurry Up, We\\'re Dreaming', '4:03'],\n ],\n browse: [\n ['New this week', 'Fresh releases picked for you', '24'],\n ['Focus', 'Instrumental, low tempo', '60'],\n ],\n find: [\n ['Recent: Tycho', 'Artist · Ambient', 'Yesterday'],\n ['Recent: lo-fi beats', 'Playlist · 120 songs', 'Sun'],\n ],\n library: [\n ['Liked songs', '1,204 songs', 'Pinned'],\n ['Downloaded', '86 songs · 412 MB', 'Offline'],\n ],\n};\n\nconst titleOf = (items: BottomNavItem[], key: string) => items.find((i) => i.key === key)?.label ?? '';\n\nexport default function Floating() {\n const [tab, setTab] = useState('home');\n const [unread, setUnread] = useState(3);\n const [drafts, setDrafts] = useState(0);\n const items: BottomNavItem[] = [\n { key: 'home', label: 'Home', icon: 'home' },\n { key: 'search', label: 'Search', icon: 'search' },\n { key: 'create', label: 'New note', icon: 'create', action: true },\n { key: 'inbox', label: 'Inbox', icon: 'inbox', badge: unread || undefined },\n { key: 'profile', label: 'Profile', icon: 'profile' },\n ];\n const open = (k: string) => {\n setTab(k);\n if (k === 'inbox') setUnread(0);\n };\n return (\n <Screen title={drafts ? `${drafts} new ${drafts === 1 ? 'draft' : 'drafts'}` : titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav variant=\"floating\" items={items} value={tab} onValueChange={open} onAction={() => setDrafts((n) => n + 1)} />\n </Screen>\n );\n}\n"
97
+ },
98
+ {
99
+ "name": "PillHighlight",
100
+ "title": "Pill Highlight",
101
+ "code": "import React, { useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { BottomNav, type BottomNavItem } from '@/components/ui/bottom-nav';\nimport { alpha, useKit } from '@/lib/kit';\n\ntype Row = [title: string, subtitle: string, meta: string];\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, children }: { title: string; rows: Row[]; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={{\n height: 300,\n alignSelf: 'stretch',\n borderRadius: radius.scale(28),\n overflow: 'hidden',\n borderWidth: 1,\n borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06),\n backgroundColor: colors.background,\n justifyContent: 'space-between',\n }}\n >\n <View style={{ paddingHorizontal: 18, paddingTop: 18, gap: 14 }}>\n <Text style={{ fontSize: 22, fontWeight: '400', letterSpacing: -0.5, color: colors.foreground }}>{title}</Text>\n {rows.map(([t, s, m]) => (\n <View key={t} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View\n style={{\n width: 36,\n height: 36,\n borderRadius: radius.capsule(36),\n backgroundColor: alpha(colors.accent, dark ? 0.22 : 0.12),\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{t.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text numberOfLines={1} style={{ fontSize: 14.5, fontWeight: '400', color: colors.foreground }}>{t}</Text>\n <Text numberOfLines={1} style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{s}</Text>\n </View>\n <Text style={{ fontSize: 12, fontWeight: '400', color: colors.mutedForeground }}>{m}</Text>\n </View>\n ))}\n </View>\n {children}\n </View>\n );\n}\n\nconst ROWS: Record<string, Row[]> = {\n home: [\n ['Maya Chen', 'Shipped the onboarding redesign', '2m'],\n ['Leo Park', 'Booked the team offsite for June', '1h'],\n ],\n explore: [\n ['Design Systems Weekly', 'Motion tokens that scale', 'New'],\n ['Product Notes', 'How Linear plans a cycle', '4m read'],\n ],\n search: [\n ['Recent: invoices', 'Q2 invoices · 14 results', 'Today'],\n ['Recent: Maya', 'People · Design team', 'Mon'],\n ],\n projects: [\n ['Mobile app 2.0', '18 of 24 tasks done', '75%'],\n ['Brand refresh', 'Review on Thursday', '40%'],\n ],\n saved: [\n ['Kyoto travel guide', 'Saved from Atlas', 'Apr 2'],\n ['Sourdough basics', 'Saved from Recipes', 'Mar 28'],\n ],\n inbox: [\n ['Priya Nair', 'Can you review the pricing page?', '9:41'],\n ['GitHub', 'PR #482 was approved', '9:12'],\n ],\n analytics: [\n ['Weekly active users', '12,480 · up 8.2%', '7d'],\n ['Conversion', '3.4% · up 0.6 pts', '7d'],\n ],\n activity: [\n ['Leo Park', 'Commented on Launch plan', '5m'],\n ['Ana Ruiz', 'Moved Pricing to Done', '22m'],\n ],\n profile: [\n ['Karan Kumar', 'Product designer · Berlin', 'Edit'],\n ['Notifications', 'Mentions and replies only', 'On'],\n ],\n home2: [\n ['Late Night Drive', 'Playlist · 42 songs', '2h 31m'],\n ['Midnight City', 'M83 · Hurry Up, We\\'re Dreaming', '4:03'],\n ],\n browse: [\n ['New this week', 'Fresh releases picked for you', '24'],\n ['Focus', 'Instrumental, low tempo', '60'],\n ],\n find: [\n ['Recent: Tycho', 'Artist · Ambient', 'Yesterday'],\n ['Recent: lo-fi beats', 'Playlist · 120 songs', 'Sun'],\n ],\n library: [\n ['Liked songs', '1,204 songs', 'Pinned'],\n ['Downloaded', '86 songs · 412 MB', 'Offline'],\n ],\n};\n\nconst titleOf = (items: BottomNavItem[], key: string) => items.find((i) => i.key === key)?.label ?? '';\n\nexport default function PillHighlight() {\n const items: BottomNavItem[] = [\n { key: 'home', label: 'Home', icon: 'home' },\n { key: 'projects', label: 'Projects', icon: 'projects' },\n { key: 'inbox', label: 'Inbox', icon: 'inbox', badge: true },\n { key: 'profile', label: 'Profile', icon: 'profile' },\n ];\n const [tab, setTab] = useState('home');\n return (\n <Screen title={titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav variant=\"pill\" items={items} value={tab} onValueChange={setTab} />\n </Screen>\n );\n}\n"
102
+ },
103
+ {
104
+ "name": "CenterFab",
105
+ "title": "Center Fab",
106
+ "code": "import React, { useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { BottomNav, type BottomNavItem } from '@/components/ui/bottom-nav';\nimport { alpha, useKit } from '@/lib/kit';\n\ntype Row = [title: string, subtitle: string, meta: string];\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, children }: { title: string; rows: Row[]; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={{\n height: 300,\n alignSelf: 'stretch',\n borderRadius: radius.scale(28),\n overflow: 'hidden',\n borderWidth: 1,\n borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06),\n backgroundColor: colors.background,\n justifyContent: 'space-between',\n }}\n >\n <View style={{ paddingHorizontal: 18, paddingTop: 18, gap: 14 }}>\n <Text style={{ fontSize: 22, fontWeight: '400', letterSpacing: -0.5, color: colors.foreground }}>{title}</Text>\n {rows.map(([t, s, m]) => (\n <View key={t} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View\n style={{\n width: 36,\n height: 36,\n borderRadius: radius.capsule(36),\n backgroundColor: alpha(colors.accent, dark ? 0.22 : 0.12),\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{t.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text numberOfLines={1} style={{ fontSize: 14.5, fontWeight: '400', color: colors.foreground }}>{t}</Text>\n <Text numberOfLines={1} style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{s}</Text>\n </View>\n <Text style={{ fontSize: 12, fontWeight: '400', color: colors.mutedForeground }}>{m}</Text>\n </View>\n ))}\n </View>\n {children}\n </View>\n );\n}\n\nconst ROWS: Record<string, Row[]> = {\n home: [\n ['Maya Chen', 'Shipped the onboarding redesign', '2m'],\n ['Leo Park', 'Booked the team offsite for June', '1h'],\n ],\n explore: [\n ['Design Systems Weekly', 'Motion tokens that scale', 'New'],\n ['Product Notes', 'How Linear plans a cycle', '4m read'],\n ],\n search: [\n ['Recent: invoices', 'Q2 invoices · 14 results', 'Today'],\n ['Recent: Maya', 'People · Design team', 'Mon'],\n ],\n projects: [\n ['Mobile app 2.0', '18 of 24 tasks done', '75%'],\n ['Brand refresh', 'Review on Thursday', '40%'],\n ],\n saved: [\n ['Kyoto travel guide', 'Saved from Atlas', 'Apr 2'],\n ['Sourdough basics', 'Saved from Recipes', 'Mar 28'],\n ],\n inbox: [\n ['Priya Nair', 'Can you review the pricing page?', '9:41'],\n ['GitHub', 'PR #482 was approved', '9:12'],\n ],\n analytics: [\n ['Weekly active users', '12,480 · up 8.2%', '7d'],\n ['Conversion', '3.4% · up 0.6 pts', '7d'],\n ],\n activity: [\n ['Leo Park', 'Commented on Launch plan', '5m'],\n ['Ana Ruiz', 'Moved Pricing to Done', '22m'],\n ],\n profile: [\n ['Karan Kumar', 'Product designer · Berlin', 'Edit'],\n ['Notifications', 'Mentions and replies only', 'On'],\n ],\n home2: [\n ['Late Night Drive', 'Playlist · 42 songs', '2h 31m'],\n ['Midnight City', 'M83 · Hurry Up, We\\'re Dreaming', '4:03'],\n ],\n browse: [\n ['New this week', 'Fresh releases picked for you', '24'],\n ['Focus', 'Instrumental, low tempo', '60'],\n ],\n find: [\n ['Recent: Tycho', 'Artist · Ambient', 'Yesterday'],\n ['Recent: lo-fi beats', 'Playlist · 120 songs', 'Sun'],\n ],\n library: [\n ['Liked songs', '1,204 songs', 'Pinned'],\n ['Downloaded', '86 songs · 412 MB', 'Offline'],\n ],\n};\n\nconst titleOf = (items: BottomNavItem[], key: string) => items.find((i) => i.key === key)?.label ?? '';\n\nexport default function CenterFab() {\n const [tab, setTab] = useState('home');\n const [posts, setPosts] = useState(0);\n const items: BottomNavItem[] = [\n { key: 'home', label: 'Home', icon: 'home' },\n { key: 'explore', label: 'Explore', icon: 'explore' },\n { key: 'create', label: 'Post', icon: 'create', action: true },\n { key: 'inbox', label: 'Inbox', icon: 'inbox', badge: 12 + posts },\n { key: 'profile', label: 'Profile', icon: 'profile' },\n ];\n return (\n <Screen title={posts ? `Posted ${posts} ${posts === 1 ? 'time' : 'times'}` : titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav variant=\"center-fab\" items={items} value={tab} onValueChange={setTab} onAction={() => setPosts((n) => n + 1)} />\n </Screen>\n );\n}\n"
107
+ },
108
+ {
109
+ "name": "TabWithIndicator",
110
+ "title": "Tab With Indicator",
111
+ "code": "import React, { useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { BottomNav, type BottomNavItem } from '@/components/ui/bottom-nav';\nimport { alpha, useKit } from '@/lib/kit';\n\ntype Row = [title: string, subtitle: string, meta: string];\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, children }: { title: string; rows: Row[]; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={{\n height: 300,\n alignSelf: 'stretch',\n borderRadius: radius.scale(28),\n overflow: 'hidden',\n borderWidth: 1,\n borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06),\n backgroundColor: colors.background,\n justifyContent: 'space-between',\n }}\n >\n <View style={{ paddingHorizontal: 18, paddingTop: 18, gap: 14 }}>\n <Text style={{ fontSize: 22, fontWeight: '400', letterSpacing: -0.5, color: colors.foreground }}>{title}</Text>\n {rows.map(([t, s, m]) => (\n <View key={t} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View\n style={{\n width: 36,\n height: 36,\n borderRadius: radius.capsule(36),\n backgroundColor: alpha(colors.accent, dark ? 0.22 : 0.12),\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{t.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text numberOfLines={1} style={{ fontSize: 14.5, fontWeight: '400', color: colors.foreground }}>{t}</Text>\n <Text numberOfLines={1} style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{s}</Text>\n </View>\n <Text style={{ fontSize: 12, fontWeight: '400', color: colors.mutedForeground }}>{m}</Text>\n </View>\n ))}\n </View>\n {children}\n </View>\n );\n}\n\nconst ROWS: Record<string, Row[]> = {\n home: [\n ['Maya Chen', 'Shipped the onboarding redesign', '2m'],\n ['Leo Park', 'Booked the team offsite for June', '1h'],\n ],\n explore: [\n ['Design Systems Weekly', 'Motion tokens that scale', 'New'],\n ['Product Notes', 'How Linear plans a cycle', '4m read'],\n ],\n search: [\n ['Recent: invoices', 'Q2 invoices · 14 results', 'Today'],\n ['Recent: Maya', 'People · Design team', 'Mon'],\n ],\n projects: [\n ['Mobile app 2.0', '18 of 24 tasks done', '75%'],\n ['Brand refresh', 'Review on Thursday', '40%'],\n ],\n saved: [\n ['Kyoto travel guide', 'Saved from Atlas', 'Apr 2'],\n ['Sourdough basics', 'Saved from Recipes', 'Mar 28'],\n ],\n inbox: [\n ['Priya Nair', 'Can you review the pricing page?', '9:41'],\n ['GitHub', 'PR #482 was approved', '9:12'],\n ],\n analytics: [\n ['Weekly active users', '12,480 · up 8.2%', '7d'],\n ['Conversion', '3.4% · up 0.6 pts', '7d'],\n ],\n activity: [\n ['Leo Park', 'Commented on Launch plan', '5m'],\n ['Ana Ruiz', 'Moved Pricing to Done', '22m'],\n ],\n profile: [\n ['Karan Kumar', 'Product designer · Berlin', 'Edit'],\n ['Notifications', 'Mentions and replies only', 'On'],\n ],\n home2: [\n ['Late Night Drive', 'Playlist · 42 songs', '2h 31m'],\n ['Midnight City', 'M83 · Hurry Up, We\\'re Dreaming', '4:03'],\n ],\n browse: [\n ['New this week', 'Fresh releases picked for you', '24'],\n ['Focus', 'Instrumental, low tempo', '60'],\n ],\n find: [\n ['Recent: Tycho', 'Artist · Ambient', 'Yesterday'],\n ['Recent: lo-fi beats', 'Playlist · 120 songs', 'Sun'],\n ],\n library: [\n ['Liked songs', '1,204 songs', 'Pinned'],\n ['Downloaded', '86 songs · 412 MB', 'Offline'],\n ],\n};\n\nconst titleOf = (items: BottomNavItem[], key: string) => items.find((i) => i.key === key)?.label ?? '';\n\nexport default function TabWithIndicator() {\n const items: BottomNavItem[] = [\n { key: 'home', label: 'Home', icon: 'home' },\n { key: 'analytics', label: 'Insights', icon: 'analytics' },\n { key: 'activity', label: 'Activity', icon: 'activity' },\n { key: 'inbox', label: 'Inbox', icon: 'inbox', badge: 5 },\n { key: 'profile', label: 'Profile', icon: 'profile' },\n ];\n const [tab, setTab] = useState('analytics');\n return (\n <Screen title={titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav variant=\"indicator\" items={items} value={tab} onValueChange={setTab} />\n </Screen>\n );\n}\n"
112
+ },
113
+ {
114
+ "name": "MusicIconsOnly",
115
+ "title": "Music Icons Only",
116
+ "code": "import React, { useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { BottomNav, type BottomNavItem } from '@/components/ui/bottom-nav';\nimport { alpha, useKit } from '@/lib/kit';\n\ntype Row = [title: string, subtitle: string, meta: string];\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, children }: { title: string; rows: Row[]; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={{\n height: 300,\n alignSelf: 'stretch',\n borderRadius: radius.scale(28),\n overflow: 'hidden',\n borderWidth: 1,\n borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06),\n backgroundColor: colors.background,\n justifyContent: 'space-between',\n }}\n >\n <View style={{ paddingHorizontal: 18, paddingTop: 18, gap: 14 }}>\n <Text style={{ fontSize: 22, fontWeight: '400', letterSpacing: -0.5, color: colors.foreground }}>{title}</Text>\n {rows.map(([t, s, m]) => (\n <View key={t} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View\n style={{\n width: 36,\n height: 36,\n borderRadius: radius.capsule(36),\n backgroundColor: alpha(colors.accent, dark ? 0.22 : 0.12),\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{t.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text numberOfLines={1} style={{ fontSize: 14.5, fontWeight: '400', color: colors.foreground }}>{t}</Text>\n <Text numberOfLines={1} style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{s}</Text>\n </View>\n <Text style={{ fontSize: 12, fontWeight: '400', color: colors.mutedForeground }}>{m}</Text>\n </View>\n ))}\n </View>\n {children}\n </View>\n );\n}\n\nconst ROWS: Record<string, Row[]> = {\n home: [\n ['Maya Chen', 'Shipped the onboarding redesign', '2m'],\n ['Leo Park', 'Booked the team offsite for June', '1h'],\n ],\n explore: [\n ['Design Systems Weekly', 'Motion tokens that scale', 'New'],\n ['Product Notes', 'How Linear plans a cycle', '4m read'],\n ],\n search: [\n ['Recent: invoices', 'Q2 invoices · 14 results', 'Today'],\n ['Recent: Maya', 'People · Design team', 'Mon'],\n ],\n projects: [\n ['Mobile app 2.0', '18 of 24 tasks done', '75%'],\n ['Brand refresh', 'Review on Thursday', '40%'],\n ],\n saved: [\n ['Kyoto travel guide', 'Saved from Atlas', 'Apr 2'],\n ['Sourdough basics', 'Saved from Recipes', 'Mar 28'],\n ],\n inbox: [\n ['Priya Nair', 'Can you review the pricing page?', '9:41'],\n ['GitHub', 'PR #482 was approved', '9:12'],\n ],\n analytics: [\n ['Weekly active users', '12,480 · up 8.2%', '7d'],\n ['Conversion', '3.4% · up 0.6 pts', '7d'],\n ],\n activity: [\n ['Leo Park', 'Commented on Launch plan', '5m'],\n ['Ana Ruiz', 'Moved Pricing to Done', '22m'],\n ],\n profile: [\n ['Karan Kumar', 'Product designer · Berlin', 'Edit'],\n ['Notifications', 'Mentions and replies only', 'On'],\n ],\n home2: [\n ['Late Night Drive', 'Playlist · 42 songs', '2h 31m'],\n ['Midnight City', 'M83 · Hurry Up, We\\'re Dreaming', '4:03'],\n ],\n browse: [\n ['New this week', 'Fresh releases picked for you', '24'],\n ['Focus', 'Instrumental, low tempo', '60'],\n ],\n find: [\n ['Recent: Tycho', 'Artist · Ambient', 'Yesterday'],\n ['Recent: lo-fi beats', 'Playlist · 120 songs', 'Sun'],\n ],\n library: [\n ['Liked songs', '1,204 songs', 'Pinned'],\n ['Downloaded', '86 songs · 412 MB', 'Offline'],\n ],\n};\n\nconst titleOf = (items: BottomNavItem[], key: string) => items.find((i) => i.key === key)?.label ?? '';\n\nexport default function MusicIconsOnly() {\n const items: BottomNavItem[] = [\n { key: 'home2', label: 'Listen now', icon: 'music' },\n { key: 'browse', label: 'Browse', icon: 'browse' },\n { key: 'library', label: 'Library', icon: 'library' },\n { key: 'find', label: 'Search', icon: 'search' },\n ];\n const [tab, setTab] = useState('home2');\n return (\n <Screen title={titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav variant=\"floating\" showLabels={false} items={items} value={tab} onValueChange={setTab} />\n </Screen>\n );\n}\n"
117
+ }
118
+ ]
119
+ }
@@ -0,0 +1,158 @@
1
+ {
2
+ "name": "bottom-sheet",
3
+ "category": "Overlays",
4
+ "aliases": [],
5
+ "exportName": "BottomSheet",
6
+ "files": [
7
+ "src/components/ui/bottom-sheet.tsx"
8
+ ],
9
+ "props": [
10
+ [
11
+ "open",
12
+ "boolean",
13
+ "—",
14
+ "Show the sheet."
15
+ ],
16
+ [
17
+ "onClose",
18
+ "() => void",
19
+ "—",
20
+ "Called after it is dragged, flung, tapped or back-buttoned closed."
21
+ ],
22
+ [
23
+ "snapPoints",
24
+ "(number | \"NN%\")[]",
25
+ "content height",
26
+ "Resting heights, in points or % of the screen. Omit to size the sheet to its content (taller content scrolls)."
27
+ ],
28
+ [
29
+ "initialSnap",
30
+ "number",
31
+ "0",
32
+ "Snap index it opens at."
33
+ ],
34
+ [
35
+ "snapIndex",
36
+ "number",
37
+ "—",
38
+ "Controlled snap index — change it while open to move the sheet."
39
+ ],
40
+ [
41
+ "onSnapChange",
42
+ "(index: number) => void",
43
+ "—",
44
+ "Fires when it settles on another snap."
45
+ ],
46
+ [
47
+ "dismissible",
48
+ "boolean",
49
+ "true",
50
+ "Allow drag-down / scrim / back to close; otherwise it rubber-bands at the lowest snap."
51
+ ],
52
+ [
53
+ "title / subtitle",
54
+ "string",
55
+ "—",
56
+ "Left-aligned heading and muted line under the grabber."
57
+ ],
58
+ [
59
+ "headerRight",
60
+ "ReactNode",
61
+ "—",
62
+ "Trailing node in the title row, e.g. a Reset link."
63
+ ],
64
+ [
65
+ "showClose",
66
+ "boolean",
67
+ "with a title",
68
+ "Round ✕ in the header (shown when dismissible, there's a title and no headerRight)."
69
+ ],
70
+ [
71
+ "handle",
72
+ "boolean",
73
+ "true",
74
+ "Grabber pill (also an accessibility adjustable)."
75
+ ],
76
+ [
77
+ "scrollable",
78
+ "boolean",
79
+ "false",
80
+ "Body is a ScrollView: below the top snap a drag resizes the sheet, at the top snap it scrolls, and pulling down from the top collapses it."
81
+ ],
82
+ [
83
+ "dragFromContent",
84
+ "boolean",
85
+ "= scrollable",
86
+ "Let vertical drags on the body move the sheet. Leave false if children contain their own ScrollView."
87
+ ],
88
+ [
89
+ "footer",
90
+ "ReactNode",
91
+ "—",
92
+ "Pinned to the bottom of the visible sheet at every snap point."
93
+ ],
94
+ [
95
+ "avoidKeyboard",
96
+ "boolean",
97
+ "true",
98
+ "Lift the sheet above the keyboard (capped below the status bar; the rest becomes scroll padding)."
99
+ ],
100
+ [
101
+ "haptics",
102
+ "boolean",
103
+ "true",
104
+ "Light impact on open, selection tick on each snap."
105
+ ],
106
+ [
107
+ "backdropOpacity",
108
+ "number",
109
+ "1",
110
+ "Scrim dim at the top snap, 0–1."
111
+ ],
112
+ [
113
+ "bottomInset",
114
+ "number",
115
+ "platform safe inset",
116
+ "Extra padding above the home indicator; pass useSafeAreaInsets().bottom for the device."
117
+ ],
118
+ [
119
+ "accessibilityLabel",
120
+ "string",
121
+ "title",
122
+ "Label for the modal sheet."
123
+ ],
124
+ [
125
+ "style / contentStyle",
126
+ "StyleProp<ViewStyle>",
127
+ "—",
128
+ "Sheet and content styles."
129
+ ]
130
+ ],
131
+ "examples": [
132
+ {
133
+ "name": "Basic",
134
+ "title": "Basic",
135
+ "code": "import React, { useState } from 'react';\nimport { Pressable, ScrollView, Text, TextInput, View } from 'react-native';\nimport { Avatar } from '@/components/ui/avatar';\nimport { BottomSheet } from '@/components/ui/bottom-sheet';\nimport { Button } from '@/components/ui/button';\nimport { ListIcon, ListItem, ListSection } from '@/components/ui/list-item';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\nexport default function Basic() {\n const { colors, radius } = useKit();\n const [open, setOpen] = useState(false);\n const apps: [string, 'mail' | 'phone' | 'globe' | 'heart', string][] = [\n ['Mail', 'mail', colors.accent],\n ['Messages', 'phone', colors.success],\n ['Browser', 'globe', colors.warning],\n ['Favorites', 'heart', colors.danger],\n ];\n return (\n <View style={{ alignItems: 'center' }}>\n <Button variant=\"accent\" onPress={() => setOpen(true)}>\n Share album\n </Button>\n <BottomSheet\n open={open}\n onClose={() => setOpen(false)}\n title=\"Share “Lisbon, May 12–16”\"\n subtitle=\"24 photos · 3 videos\"\n contentStyle={{ paddingHorizontal: 0 }}\n >\n <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{ gap: 18, paddingHorizontal: 20, paddingVertical: 8 }}>\n {['Ana Costa', 'Luis Moreno', 'Priya Shah', 'Tom Becker', 'Mei Lin'].map((name) => (\n <Pressable key={name} accessibilityRole=\"button\" accessibilityLabel={`Send to ${name}`} style={{ alignItems: 'center', gap: 6, width: 58 }}>\n <Avatar name={name} size={54} />\n <Text numberOfLines={1} style={{ fontSize: 12, color: colors.foreground }}>\n {name.split(' ')[0]}\n </Text>\n </Pressable>\n ))}\n </ScrollView>\n <View style={{ flexDirection: 'row', justifyContent: 'space-between', paddingHorizontal: 20, paddingVertical: 14 }}>\n {apps.map(([label, icon, color]) => (\n <Pressable key={label} accessibilityRole=\"button\" accessibilityLabel={label} style={{ alignItems: 'center', gap: 6, flex: 1 }}>\n <View\n style={{\n width: 54,\n height: 54,\n borderRadius: radius.scale(16),\n backgroundColor: color,\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <ListIcon name={icon} color={readableOn(color)} size={24} />\n </View>\n <Text style={{ fontSize: 12, color: colors.mutedForeground }}>{label}</Text>\n </Pressable>\n ))}\n </View>\n <ListSection>\n <ListItem title=\"Copy link\" icon=\"globe\" iconVariant=\"tinted\" chevron={false} size=\"sm\" onPress={() => setOpen(false)} />\n <ListItem title=\"Add to shared album\" icon=\"user\" iconVariant=\"tinted\" chevron={false} size=\"sm\" onPress={() => setOpen(false)} />\n <ListItem title=\"Mark as favorite\" icon=\"star\" iconVariant=\"tinted\" chevron={false} size=\"sm\" onPress={() => setOpen(false)} />\n </ListSection>\n </BottomSheet>\n </View>\n );\n}\n"
136
+ },
137
+ {
138
+ "name": "SnapPoints",
139
+ "title": "Snap Points",
140
+ "code": "import React, { useState } from 'react';\nimport { Pressable, ScrollView, Text, TextInput, View } from 'react-native';\nimport { Avatar } from '@/components/ui/avatar';\nimport { BottomSheet } from '@/components/ui/bottom-sheet';\nimport { Button } from '@/components/ui/button';\nimport { ListIcon, ListItem, ListSection } from '@/components/ui/list-item';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\nfunction useMuted() {\n const { colors } = useKit();\n return { fontSize: 14, lineHeight: 20, color: colors.mutedForeground };\n}\n\nexport default function SnapPoints() {\n const { colors, radius } = useKit();\n const muted = useMuted();\n const [open, setOpen] = useState(false);\n const [snap, setSnap] = useState(0);\n return (\n <View style={{ alignItems: 'center' }}>\n <Button variant=\"accent\" onPress={() => setOpen(true)}>\n Track your ride\n </Button>\n <BottomSheet\n open={open}\n onClose={() => {\n setOpen(false);\n setSnap(0);\n }}\n snapPoints={['40%', '90%']}\n snapIndex={snap}\n onSnapChange={setSnap}\n scrollable\n accessibilityLabel=\"Ride details\"\n >\n <View style={{ flexDirection: 'row', alignItems: 'flex-start', justifyContent: 'space-between' }}>\n <View>\n <Text style={{ fontSize: 24, letterSpacing: -0.5, color: colors.foreground }}>Arriving in 4 min</Text>\n <Text style={[muted, { marginTop: 2 }]}>Silver Toyota Prius · 7KJ 482</Text>\n </View>\n <View\n style={{\n paddingHorizontal: 10,\n height: 28,\n justifyContent: 'center',\n borderRadius: radius.capsule(28),\n backgroundColor: alpha(colors.success, 0.14),\n }}\n >\n <Text style={{ fontSize: 13, color: colors.success }}>On time</Text>\n </View>\n </View>\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12, marginTop: 18 }}>\n <Avatar name=\"Daniel Ortiz\" size={48} />\n <View style={{ flex: 1 }}>\n <Text style={{ fontSize: 16, color: colors.foreground }}>Daniel Ortiz</Text>\n <Text style={muted}>★ 4.92 · 2,184 trips</Text>\n </View>\n </View>\n <View style={{ flexDirection: 'row', gap: 10, marginTop: 18 }}>\n <Button variant=\"secondary\" style={{ flex: 1 }}>\n Message\n </Button>\n <Button variant=\"secondary\" style={{ flex: 1 }}>\n Call\n </Button>\n <Button variant=\"secondary\" style={{ flex: 1 }} onPress={() => setSnap(1)}>\n {snap === 0 ? 'Details' : 'Share'}\n </Button>\n </View>\n\n <Text style={[muted, { marginTop: 28, marginBottom: 6, fontSize: 12, letterSpacing: 0.8 }]}>TRIP</Text>\n {[\n ['Pickup', 'Rua Augusta 24, Baixa', colors.accent],\n ['Drop-off', 'Lisbon Airport, Terminal 1', colors.foreground],\n ].map(([label, place, color]) => (\n <View key={label} style={{ flexDirection: 'row', gap: 12, paddingVertical: 10 }}>\n <View style={{ width: 10, height: 10, marginTop: 5, borderRadius: radius.capsule(10), backgroundColor: color }} />\n <View>\n <Text style={muted}>{label}</Text>\n <Text style={{ fontSize: 15, color: colors.foreground }}>{place}</Text>\n </View>\n </View>\n ))}\n <Text style={[muted, { marginTop: 22, marginBottom: 6, fontSize: 12, letterSpacing: 0.8 }]}>FARE</Text>\n {[\n ['Comfort · 14.2 km', '₹333'],\n ['Airport fee', '₹50'],\n ['Promo SPRING10', '−₹35'],\n ['Total · Visa ••42', '₹348'],\n ].map(([item, price], i, all) => (\n <View key={item} style={{ flexDirection: 'row', justifyContent: 'space-between', paddingVertical: 10 }}>\n <Text style={[muted, i === all.length - 1 && { color: colors.foreground }]}>{item}</Text>\n <Text style={{ fontSize: 14, color: colors.foreground }}>{price}</Text>\n </View>\n ))}\n <Text style={[muted, { marginTop: 22, marginBottom: 6, fontSize: 12, letterSpacing: 0.8 }]}>SAFETY</Text>\n <ListSection>\n <ListItem title=\"Share trip status\" icon=\"user\" iconVariant=\"tinted\" size=\"sm\" onPress={() => {}} />\n <ListItem title=\"Emergency assistance\" icon=\"shield\" iconVariant=\"tinted\" size=\"sm\" onPress={() => {}} />\n <ListItem title=\"Cancel ride\" icon=\"trash\" destructive size=\"sm\" chevron={false} onPress={() => setOpen(false)} />\n </ListSection>\n </BottomSheet>\n </View>\n );\n}\n"
141
+ },
142
+ {
143
+ "name": "Filters",
144
+ "title": "Filters",
145
+ "code": "import React, { useState } from 'react';\nimport { Pressable, ScrollView, Text, TextInput, View } from 'react-native';\nimport { BottomSheet } from '@/components/ui/bottom-sheet';\nimport { Button } from '@/components/ui/button';\nimport { ChipGroup } from '@/components/ui/chip-group';\nimport { Switch } from '@/components/ui/switch';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\nexport default function Filters() {\n const { colors } = useKit();\n const [open, setOpen] = useState(false);\n const [type, setType] = useState<string[]>(['Entire home']);\n const [price, setPrice] = useState<string[]>(['₹5,000 – ₹10,000']);\n const [rooms, setRooms] = useState<string[]>(['2']);\n const [amenities, setAmenities] = useState<string[]>(['Wi-Fi', 'Kitchen']);\n const [instant, setInstant] = useState(true);\n const [flexible, setFlexible] = useState(false);\n const count = 214 - amenities.length * 23 - (instant ? 31 : 0) - (flexible ? 18 : 0);\n const reset = () => {\n setType([]);\n setPrice([]);\n setRooms([]);\n setAmenities([]);\n setInstant(false);\n setFlexible(false);\n };\n const heading = (t: string) => <Text style={{ fontSize: 16, color: colors.foreground, marginTop: 20, marginBottom: 10 }}>{t}</Text>;\n return (\n <View style={{ alignItems: 'center' }}>\n <Button variant=\"outline\" onPress={() => setOpen(true)}>\n Filters · Porto\n </Button>\n <BottomSheet\n open={open}\n onClose={() => setOpen(false)}\n snapPoints={['62%', '92%']}\n scrollable\n title=\"Filters\"\n headerRight={\n <Pressable onPress={reset} accessibilityRole=\"button\" hitSlop={12}>\n <Text style={{ fontSize: 15, color: colors.accent }}>Reset</Text>\n </Pressable>\n }\n footer={\n <Button variant=\"accent\" block onPress={() => setOpen(false)}>\n {`Show ${count} homes`}\n </Button>\n }\n >\n {heading('Type of place')}\n <ChipGroup options={['Entire home', 'Private room', 'Shared room']} value={type} onValueChange={setType} />\n {heading('Price per night')}\n <ChipGroup options={['Under ₹5,000', '₹5,000 – ₹10,000', '₹10,000+']} value={price} onValueChange={setPrice} />\n {heading('Bedrooms')}\n <ChipGroup options={['Any', '1', '2', '3', '4+']} value={rooms} onValueChange={setRooms} />\n {heading('Amenities')}\n <ChipGroup\n multiple\n options={['Wi-Fi', 'Kitchen', 'Washer', 'Air conditioning', 'Free parking', 'Workspace', 'Pool']}\n value={amenities}\n onValueChange={setAmenities}\n />\n {heading('Booking options')}\n <View style={{ gap: 14 }}>\n <Switch label=\"Instant book\" description=\"Book without waiting for host approval\" value={instant} onValueChange={setInstant} />\n <Switch label=\"Free cancellation\" description=\"Cancel up to 48 h before check-in\" value={flexible} onValueChange={setFlexible} />\n </View>\n </BottomSheet>\n </View>\n );\n}\n"
146
+ },
147
+ {
148
+ "name": "KeyboardAware",
149
+ "title": "Keyboard Aware",
150
+ "code": "import React, { useState } from 'react';\nimport { Pressable, ScrollView, Text, TextInput, View } from 'react-native';\nimport { BottomSheet } from '@/components/ui/bottom-sheet';\nimport { Button } from '@/components/ui/button';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\nexport default function KeyboardAware() {\n const { colors, radius } = useKit();\n const [open, setOpen] = useState(false);\n const [text, setText] = useState('');\n return (\n <View style={{ alignItems: 'center' }}>\n <Button variant=\"secondary\" onPress={() => setOpen(true)}>\n Reply to Lena\n </Button>\n <BottomSheet\n open={open}\n onClose={() => setOpen(false)}\n snapPoints={[300]}\n title=\"Reply to Lena Fischer\"\n subtitle=\"Re: Offsite agenda\"\n >\n <TextInput\n value={text}\n onChangeText={setText}\n placeholder=\"Thursday works for me — I’ll bring the prototypes.\"\n placeholderTextColor={colors.mutedForeground}\n multiline\n accessibilityLabel=\"Reply\"\n style={{\n minHeight: 110,\n padding: 14,\n fontSize: 15,\n textAlignVertical: 'top',\n color: colors.foreground,\n borderRadius: radius.lg,\n borderWidth: 1,\n borderColor: alpha(colors.foreground, 0.08),\n backgroundColor: colors.muted,\n }}\n />\n <View style={{ flexDirection: 'row', justifyContent: 'flex-end', gap: 10, marginTop: 12 }}>\n <Button variant=\"ghost\" onPress={() => setOpen(false)}>\n Discard\n </Button>\n <Button\n variant=\"accent\"\n disabled={!text.trim()}\n onPress={() => {\n setText('');\n setOpen(false);\n }}\n >\n Send\n </Button>\n </View>\n </BottomSheet>\n </View>\n );\n}\n"
151
+ },
152
+ {
153
+ "name": "NonDismissible",
154
+ "title": "Non Dismissible",
155
+ "code": "import React, { useState } from 'react';\nimport { Pressable, ScrollView, Text, TextInput, View } from 'react-native';\nimport { BottomSheet } from '@/components/ui/bottom-sheet';\nimport { Button } from '@/components/ui/button';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\nfunction useMuted() {\n const { colors } = useKit();\n return { fontSize: 14, lineHeight: 20, color: colors.mutedForeground };\n}\n\nexport default function NonDismissible() {\n const muted = useMuted();\n const [open, setOpen] = useState(false);\n return (\n <View style={{ alignItems: 'center' }}>\n <Button variant=\"secondary\" onPress={() => setOpen(true)}>\n Review terms\n </Button>\n <BottomSheet\n open={open}\n onClose={() => setOpen(false)}\n snapPoints={['40%', '70%']}\n dismissible={false}\n title=\"We updated our terms\"\n subtitle=\"Effective November 1, 2026\"\n >\n <Text style={muted}>\n Your files stay encrypted at rest, and we never sell personal information. We now keep deleted items for 30 days so you\n can restore them. Please review and accept to keep using your account.\n </Text>\n <View style={{ flexDirection: 'row', gap: 10, marginTop: 20 }}>\n <Button variant=\"outline\" style={{ flex: 1 }} block onPress={() => setOpen(false)}>\n Later\n </Button>\n <Button variant=\"accent\" style={{ flex: 1 }} block onPress={() => setOpen(false)}>\n Accept\n </Button>\n </View>\n </BottomSheet>\n </View>\n );\n}\n"
156
+ }
157
+ ]
158
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "bullet",
3
+ "category": "Charts",
4
+ "aliases": [],
5
+ "exportName": "BulletChart",
6
+ "files": [
7
+ "src/components/charts/bullet.tsx"
8
+ ],
9
+ "props": [
10
+ [
11
+ "data",
12
+ "{ label, value, target?, ranges? }[]",
13
+ "—",
14
+ "KPI rows."
15
+ ],
16
+ [
17
+ "rowHeight / barHeight",
18
+ "number",
19
+ "52 / 14",
20
+ "Sizing."
21
+ ]
22
+ ],
23
+ "examples": []
24
+ }