@expokit/cli 0.4.0 → 0.4.2
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.
- package/README.md +17 -0
- package/bin/lib.mjs +1 -1
- package/bin/liquidglass.mjs +281 -0
- package/package.json +17 -3
- package/registry/_docs/accordion.json +1 -1
- package/registry/_docs/avatar.json +6 -6
- package/registry/_docs/badge.json +3 -3
- package/registry/_docs/bottom-nav.json +6 -6
- package/registry/_docs/bottom-sheet.json +3 -3
- package/registry/_docs/bullet.json +6 -0
- package/registry/_docs/button-3d.json +2 -2
- package/registry/_docs/button.json +2 -2
- package/registry/_docs/calendar.json +5 -5
- package/registry/_docs/card.json +1 -1
- package/registry/_docs/chip-group.json +1 -1
- package/registry/_docs/confetti.json +3 -3
- package/registry/_docs/date-picker.json +4 -4
- package/registry/_docs/drawer.json +4 -4
- package/registry/_docs/flip-card.json +4 -4
- package/registry/_docs/floating-input.json +1 -1
- package/registry/_docs/grain.json +2 -2
- package/registry/_docs/isometric-bar.json +6 -0
- package/registry/_docs/liquidglass-tab.json +2 -1
- package/registry/_docs/otp-input.json +1 -1
- package/registry/_docs/payment-card.json +3 -3
- package/registry/_docs/pixel-bar.json +6 -0
- package/registry/_docs/polar-area.json +6 -0
- package/registry/_docs/progress-steps.json +4 -4
- package/registry/_docs/progress.json +2 -2
- package/registry/_docs/radio-group.json +1 -1
- package/registry/_docs/search-bar.json +3 -3
- package/registry/_docs/segmented-control.json +1 -1
- package/registry/_docs/select.json +6 -6
- package/registry/_docs/skeleton.json +1 -1
- package/registry/_docs/slider.json +6 -6
- package/registry/_docs/spinner.json +2 -2
- package/registry/_docs/stateful-button.json +1 -1
- package/registry/_docs/stepper.json +3 -3
- package/registry/_docs/tabs.json +2 -2
- package/registry/_docs/text-animation.json +4 -4
- package/registry/_docs/textarea.json +1 -1
- package/registry/_docs/tilt-card.json +3 -3
- package/registry/_docs/time-picker.json +6 -6
- package/registry/_docs/timeline-chart.json +6 -0
- package/registry/_docs/timeline.json +4 -4
- package/registry/_docs/treemap.json +6 -0
- package/registry/_docs/waterfall.json +6 -0
- package/registry/accordion/files/src/components/ui/accordion.tsx +31 -3
- package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +4 -1
- package/registry/alert/files/src/components/ui/alert.tsx +5 -2
- package/registry/alert/files/src/components/ui/button.tsx +32 -4
- package/registry/badge/files/src/components/ui/badge.tsx +2 -2
- package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +11 -5
- package/registry/button/files/src/components/ui/button.tsx +32 -4
- package/registry/button-3d/files/src/components/ui/button-3d.tsx +33 -6
- package/registry/calendar/files/src/components/ui/calendar.tsx +62 -17
- package/registry/card/files/src/components/ui/card.tsx +25 -5
- package/registry/checkbox/files/src/components/ui/checkbox.tsx +26 -4
- package/registry/chip-group/files/src/components/ui/chip-group.tsx +35 -5
- package/registry/coupon/files/src/components/ui/coupon.tsx +45 -21
- package/registry/coupon/files/src/components/ui/flip-card.tsx +22 -3
- package/registry/coupon/files/src/components/ui/tilt-card.tsx +57 -16
- package/registry/date-picker/files/src/components/ui/calendar.tsx +62 -17
- package/registry/date-picker/files/src/components/ui/date-picker.tsx +96 -42
- package/registry/dialog/files/src/components/ui/dialog.tsx +9 -5
- package/registry/drawer/files/src/components/ui/drawer.tsx +8 -4
- package/registry/flip-card/files/src/components/ui/flip-card.tsx +22 -3
- package/registry/floating-input/files/src/components/ui/floating-input.tsx +75 -26
- package/registry/liquidglass-tab/files/src/app/(tabs)/_layout.tsx +3 -94
- package/registry/liquidglass-tab/files/src/app/(tabs)/alerts.tsx +2 -1
- package/registry/liquidglass-tab/files/src/app/_layout.tsx +2 -2
- package/registry/liquidglass-tab/files/src/components/GlassTabBar.tsx +2 -1
- package/registry/liquidglass-tab/files/src/components/LiquidGlassTabs.tsx +110 -0
- package/registry/liquidglass-tab/files/src/components/TabScreen.tsx +31 -6
- package/registry/liquidglass-tab/files/src/lib/tab-bar.ts +12 -4
- package/registry/liquidglass-tab/files/src/lib/tab-icons.ts +6 -3
- package/registry/liquidglass-tab/files/src/lib/tabs.ts +4 -2
- package/registry/list-item/files/src/components/ui/list-item.tsx +10 -3
- package/registry/otp-input/files/src/components/ui/otp-input.tsx +50 -19
- package/registry/password-input/files/src/components/ui/floating-input.tsx +75 -26
- package/registry/password-input/files/src/components/ui/password-input.tsx +53 -19
- package/registry/payment-card/files/src/components/ui/flip-card.tsx +22 -3
- package/registry/payment-card/files/src/components/ui/payment-card.tsx +19 -15
- package/registry/payment-card/files/src/components/ui/tilt-card.tsx +57 -16
- package/registry/progress/files/src/components/ui/progress.tsx +9 -2
- package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +11 -4
- package/registry/radio-group/files/src/components/ui/radio-group.tsx +39 -9
- package/registry/search-bar/files/src/components/ui/search-bar.tsx +70 -17
- package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +37 -9
- package/registry/select/files/src/components/ui/select.tsx +158 -83
- package/registry/slider/files/src/components/ui/slider.tsx +53 -25
- package/registry/stateful-button/files/src/components/ui/stateful-button.tsx +12 -4
- package/registry/stepper/files/src/components/ui/stepper.tsx +76 -26
- package/registry/switch/files/src/components/ui/switch.tsx +56 -19
- package/registry/tabs/files/src/components/ui/tabs.tsx +7 -4
- package/registry/text-animation/files/src/components/ui/text-animation.tsx +7 -2
- package/registry/textarea/files/src/components/ui/textarea.tsx +104 -36
- package/registry/tilt-card/files/src/components/ui/tilt-card.tsx +57 -16
- package/registry/time-picker/files/src/components/ui/time-picker.tsx +154 -73
- package/registry/timeline/files/src/components/ui/timeline.tsx +9 -4
- package/registry/toast/files/src/components/ui/button.tsx +32 -4
- package/registry/toast/files/src/components/ui/toast.tsx +15 -6
|
@@ -112,32 +112,32 @@
|
|
|
112
112
|
{
|
|
113
113
|
"name": "MinimalClean",
|
|
114
114
|
"title": "Minimal Clean",
|
|
115
|
-
"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/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, screen, children }: { title: string; rows: Row[]; screen?: boolean; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={[\n { alignSelf: 'stretch', overflow: 'hidden', backgroundColor: colors.background, justifyContent: 'space-between' },\n screen\n ? { flex: 1 }\n : { height: 300, borderRadius: radius.scale(28), borderWidth: 1, borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06) },\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({ screen, bottomInset }: DemoProps) {\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 screen={screen} title={titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav bottomInset={bottomInset} variant=\"minimal\" items={items} value={tab} onValueChange={setTab} />\n </Screen>\n );\n}\n"
|
|
115
|
+
"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\n/** Control text stops at iOS's largest standard size. */\nconst FONT_CAP = 1.35;\n\ntype Row = [title: string, subtitle: string, meta: string];\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, screen, children }: { title: string; rows: Row[]; screen?: boolean; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={[\n { alignSelf: 'stretch', overflow: 'hidden', backgroundColor: colors.background, justifyContent: 'space-between' },\n screen\n ? { flex: 1 }\n : { height: 300, borderRadius: radius.scale(28), borderWidth: 1, borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06) },\n ]}\n >\n <View style={{ paddingHorizontal: 18, paddingTop: 18, gap: 14 }}>\n <Text maxFontSizeMultiplier={FONT_CAP} 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 maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{t.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text maxFontSizeMultiplier={FONT_CAP} numberOfLines={1} style={{ fontSize: 14.5, fontWeight: '400', color: colors.foreground }}>{t}</Text>\n <Text maxFontSizeMultiplier={FONT_CAP} numberOfLines={1} style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{s}</Text>\n </View>\n <Text maxFontSizeMultiplier={FONT_CAP} 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({ screen, bottomInset }: DemoProps) {\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 screen={screen} title={titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav bottomInset={bottomInset} variant=\"minimal\" items={items} value={tab} onValueChange={setTab} />\n </Screen>\n );\n}\n"
|
|
116
116
|
},
|
|
117
117
|
{
|
|
118
118
|
"name": "Floating",
|
|
119
119
|
"title": "Floating",
|
|
120
|
-
"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/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, screen, children }: { title: string; rows: Row[]; screen?: boolean; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={[\n { alignSelf: 'stretch', overflow: 'hidden', backgroundColor: colors.background, justifyContent: 'space-between' },\n screen\n ? { flex: 1 }\n : { height: 300, borderRadius: radius.scale(28), borderWidth: 1, borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06) },\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({ screen, bottomInset }: DemoProps) {\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 screen={screen} title={drafts ? `${drafts} new ${drafts === 1 ? 'draft' : 'drafts'}` : titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav bottomInset={bottomInset} variant=\"floating\" items={items} value={tab} onValueChange={open} onAction={() => setDrafts((n) => n + 1)} />\n </Screen>\n );\n}\n"
|
|
120
|
+
"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\n/** Control text stops at iOS's largest standard size. */\nconst FONT_CAP = 1.35;\n\ntype Row = [title: string, subtitle: string, meta: string];\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, screen, children }: { title: string; rows: Row[]; screen?: boolean; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={[\n { alignSelf: 'stretch', overflow: 'hidden', backgroundColor: colors.background, justifyContent: 'space-between' },\n screen\n ? { flex: 1 }\n : { height: 300, borderRadius: radius.scale(28), borderWidth: 1, borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06) },\n ]}\n >\n <View style={{ paddingHorizontal: 18, paddingTop: 18, gap: 14 }}>\n <Text maxFontSizeMultiplier={FONT_CAP} 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 maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{t.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text maxFontSizeMultiplier={FONT_CAP} numberOfLines={1} style={{ fontSize: 14.5, fontWeight: '400', color: colors.foreground }}>{t}</Text>\n <Text maxFontSizeMultiplier={FONT_CAP} numberOfLines={1} style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{s}</Text>\n </View>\n <Text maxFontSizeMultiplier={FONT_CAP} 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({ screen, bottomInset }: DemoProps) {\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 screen={screen} title={drafts ? `${drafts} new ${drafts === 1 ? 'draft' : 'drafts'}` : titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav bottomInset={bottomInset} variant=\"floating\" items={items} value={tab} onValueChange={open} onAction={() => setDrafts((n) => n + 1)} />\n </Screen>\n );\n}\n"
|
|
121
121
|
},
|
|
122
122
|
{
|
|
123
123
|
"name": "PillHighlight",
|
|
124
124
|
"title": "Pill Highlight",
|
|
125
|
-
"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/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, screen, children }: { title: string; rows: Row[]; screen?: boolean; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={[\n { alignSelf: 'stretch', overflow: 'hidden', backgroundColor: colors.background, justifyContent: 'space-between' },\n screen\n ? { flex: 1 }\n : { height: 300, borderRadius: radius.scale(28), borderWidth: 1, borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06) },\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({ screen, bottomInset }: DemoProps) {\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 screen={screen} title={titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav bottomInset={bottomInset} variant=\"pill\" items={items} value={tab} onValueChange={setTab} />\n </Screen>\n );\n}\n"
|
|
125
|
+
"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\n/** Control text stops at iOS's largest standard size. */\nconst FONT_CAP = 1.35;\n\ntype Row = [title: string, subtitle: string, meta: string];\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, screen, children }: { title: string; rows: Row[]; screen?: boolean; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={[\n { alignSelf: 'stretch', overflow: 'hidden', backgroundColor: colors.background, justifyContent: 'space-between' },\n screen\n ? { flex: 1 }\n : { height: 300, borderRadius: radius.scale(28), borderWidth: 1, borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06) },\n ]}\n >\n <View style={{ paddingHorizontal: 18, paddingTop: 18, gap: 14 }}>\n <Text maxFontSizeMultiplier={FONT_CAP} 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 maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{t.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text maxFontSizeMultiplier={FONT_CAP} numberOfLines={1} style={{ fontSize: 14.5, fontWeight: '400', color: colors.foreground }}>{t}</Text>\n <Text maxFontSizeMultiplier={FONT_CAP} numberOfLines={1} style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{s}</Text>\n </View>\n <Text maxFontSizeMultiplier={FONT_CAP} 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({ screen, bottomInset }: DemoProps) {\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 screen={screen} title={titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav bottomInset={bottomInset} variant=\"pill\" items={items} value={tab} onValueChange={setTab} />\n </Screen>\n );\n}\n"
|
|
126
126
|
},
|
|
127
127
|
{
|
|
128
128
|
"name": "CenterFab",
|
|
129
129
|
"title": "Center Fab",
|
|
130
|
-
"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/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, screen, children }: { title: string; rows: Row[]; screen?: boolean; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={[\n { alignSelf: 'stretch', overflow: 'hidden', backgroundColor: colors.background, justifyContent: 'space-between' },\n screen\n ? { flex: 1 }\n : { height: 300, borderRadius: radius.scale(28), borderWidth: 1, borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06) },\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({ screen, bottomInset }: DemoProps) {\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 screen={screen} title={posts ? `Posted ${posts} ${posts === 1 ? 'time' : 'times'}` : titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav bottomInset={bottomInset} variant=\"center-fab\" items={items} value={tab} onValueChange={setTab} onAction={() => setPosts((n) => n + 1)} />\n </Screen>\n );\n}\n"
|
|
130
|
+
"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\n/** Control text stops at iOS's largest standard size. */\nconst FONT_CAP = 1.35;\n\ntype Row = [title: string, subtitle: string, meta: string];\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, screen, children }: { title: string; rows: Row[]; screen?: boolean; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={[\n { alignSelf: 'stretch', overflow: 'hidden', backgroundColor: colors.background, justifyContent: 'space-between' },\n screen\n ? { flex: 1 }\n : { height: 300, borderRadius: radius.scale(28), borderWidth: 1, borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06) },\n ]}\n >\n <View style={{ paddingHorizontal: 18, paddingTop: 18, gap: 14 }}>\n <Text maxFontSizeMultiplier={FONT_CAP} 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 maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{t.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text maxFontSizeMultiplier={FONT_CAP} numberOfLines={1} style={{ fontSize: 14.5, fontWeight: '400', color: colors.foreground }}>{t}</Text>\n <Text maxFontSizeMultiplier={FONT_CAP} numberOfLines={1} style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{s}</Text>\n </View>\n <Text maxFontSizeMultiplier={FONT_CAP} 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({ screen, bottomInset }: DemoProps) {\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 screen={screen} title={posts ? `Posted ${posts} ${posts === 1 ? 'time' : 'times'}` : titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav bottomInset={bottomInset} variant=\"center-fab\" items={items} value={tab} onValueChange={setTab} onAction={() => setPosts((n) => n + 1)} />\n </Screen>\n );\n}\n"
|
|
131
131
|
},
|
|
132
132
|
{
|
|
133
133
|
"name": "TabWithIndicator",
|
|
134
134
|
"title": "Tab With Indicator",
|
|
135
|
-
"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/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, screen, children }: { title: string; rows: Row[]; screen?: boolean; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={[\n { alignSelf: 'stretch', overflow: 'hidden', backgroundColor: colors.background, justifyContent: 'space-between' },\n screen\n ? { flex: 1 }\n : { height: 300, borderRadius: radius.scale(28), borderWidth: 1, borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06) },\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({ screen, bottomInset }: DemoProps) {\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 screen={screen} title={titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav bottomInset={bottomInset} variant=\"indicator\" items={items} value={tab} onValueChange={setTab} />\n </Screen>\n );\n}\n"
|
|
135
|
+
"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\n/** Control text stops at iOS's largest standard size. */\nconst FONT_CAP = 1.35;\n\ntype Row = [title: string, subtitle: string, meta: string];\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, screen, children }: { title: string; rows: Row[]; screen?: boolean; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={[\n { alignSelf: 'stretch', overflow: 'hidden', backgroundColor: colors.background, justifyContent: 'space-between' },\n screen\n ? { flex: 1 }\n : { height: 300, borderRadius: radius.scale(28), borderWidth: 1, borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06) },\n ]}\n >\n <View style={{ paddingHorizontal: 18, paddingTop: 18, gap: 14 }}>\n <Text maxFontSizeMultiplier={FONT_CAP} 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 maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{t.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text maxFontSizeMultiplier={FONT_CAP} numberOfLines={1} style={{ fontSize: 14.5, fontWeight: '400', color: colors.foreground }}>{t}</Text>\n <Text maxFontSizeMultiplier={FONT_CAP} numberOfLines={1} style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{s}</Text>\n </View>\n <Text maxFontSizeMultiplier={FONT_CAP} 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({ screen, bottomInset }: DemoProps) {\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 screen={screen} title={titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav bottomInset={bottomInset} variant=\"indicator\" items={items} value={tab} onValueChange={setTab} />\n </Screen>\n );\n}\n"
|
|
136
136
|
},
|
|
137
137
|
{
|
|
138
138
|
"name": "MusicIconsOnly",
|
|
139
139
|
"title": "Music Icons Only",
|
|
140
|
-
"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/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, screen, children }: { title: string; rows: Row[]; screen?: boolean; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={[\n { alignSelf: 'stretch', overflow: 'hidden', backgroundColor: colors.background, justifyContent: 'space-between' },\n screen\n ? { flex: 1 }\n : { height: 300, borderRadius: radius.scale(28), borderWidth: 1, borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06) },\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({ screen, bottomInset }: DemoProps) {\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 screen={screen} title={titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav bottomInset={bottomInset} variant=\"floating\" showLabels={false} items={items} value={tab} onValueChange={setTab} />\n </Screen>\n );\n}\n"
|
|
140
|
+
"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\n/** Control text stops at iOS's largest standard size. */\nconst FONT_CAP = 1.35;\n\ntype Row = [title: string, subtitle: string, meta: string];\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, screen, children }: { title: string; rows: Row[]; screen?: boolean; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={[\n { alignSelf: 'stretch', overflow: 'hidden', backgroundColor: colors.background, justifyContent: 'space-between' },\n screen\n ? { flex: 1 }\n : { height: 300, borderRadius: radius.scale(28), borderWidth: 1, borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06) },\n ]}\n >\n <View style={{ paddingHorizontal: 18, paddingTop: 18, gap: 14 }}>\n <Text maxFontSizeMultiplier={FONT_CAP} 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 maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{t.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text maxFontSizeMultiplier={FONT_CAP} numberOfLines={1} style={{ fontSize: 14.5, fontWeight: '400', color: colors.foreground }}>{t}</Text>\n <Text maxFontSizeMultiplier={FONT_CAP} numberOfLines={1} style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{s}</Text>\n </View>\n <Text maxFontSizeMultiplier={FONT_CAP} 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({ screen, bottomInset }: DemoProps) {\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 screen={screen} title={titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav bottomInset={bottomInset} variant=\"floating\" showLabels={false} items={items} value={tab} onValueChange={setTab} />\n </Screen>\n );\n}\n"
|
|
141
141
|
}
|
|
142
142
|
]
|
|
143
143
|
}
|
|
@@ -160,17 +160,17 @@
|
|
|
160
160
|
{
|
|
161
161
|
"name": "Basic",
|
|
162
162
|
"title": "Basic",
|
|
163
|
-
"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"
|
|
163
|
+
"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\n/** Demo control text (buttons, chips, labels on fixed shapes) stops at iOS's largest standard text size. */\nconst FONT_CAP = 1.35;\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 maxFontSizeMultiplier={FONT_CAP} 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 maxFontSizeMultiplier={FONT_CAP} 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"
|
|
164
164
|
},
|
|
165
165
|
{
|
|
166
166
|
"name": "SnapPoints",
|
|
167
167
|
"title": "Snap Points",
|
|
168
|
-
"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
|
|
168
|
+
"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\n/** Demo control text (buttons, chips, labels on fixed shapes) stops at iOS's largest standard text size. */\nconst FONT_CAP = 1.35;\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 style={{ flexShrink: 1 }}>\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 minHeight: 28,\n justifyContent: 'center',\n borderRadius: radius.capsule(28),\n backgroundColor: alpha(colors.success, 0.14),\n }}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} 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 style={{ flexShrink: 1 }}>\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', gap: 12, paddingVertical: 10 }}>\n <Text style={[muted, { flexShrink: 1 }, 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"
|
|
169
169
|
},
|
|
170
170
|
{
|
|
171
171
|
"name": "Filters",
|
|
172
172
|
"title": "Filters",
|
|
173
|
-
"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"
|
|
173
|
+
"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\n/** Demo control text (buttons, chips, labels on fixed shapes) stops at iOS's largest standard text size. */\nconst FONT_CAP = 1.35;\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 maxFontSizeMultiplier={FONT_CAP} 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"
|
|
174
174
|
},
|
|
175
175
|
{
|
|
176
176
|
"name": "KeyboardAware",
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
{
|
|
113
113
|
"name": "OnPhoto",
|
|
114
114
|
"title": "On Photo",
|
|
115
|
-
"code": "import React, { useState } from 'react';\nimport { Image, Text, View } from 'react-native';\nimport { Button3D } from '@/components/ui/button-3d';\nimport { GrainGradient } from '@/components/ui/grain';\nimport { useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** A screen title, as it sits above the content in your app. */\nfunction Header({ title, sub, right }: { title: string; sub?: string; right?: React.ReactNode }) {\n const { colors } = useKit();\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ flex: 1 }}>\n <Text style={{ color: colors.foreground, fontSize: 22, fontWeight: '700', letterSpacing: -0.4 }}>{title}</Text>\n {sub ? <Text style={{ color: colors.mutedForeground, fontSize: 13, marginTop: 2 }}>{sub}</Text> : null}\n </View>\n {right}\n </View>\n );\n}\n\nexport default function OnPhoto({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <View style={[{ gap: 18 }, screen && { flex: 1 }]}>\n <Header\n title=\"Good morning, Maya\"\n sub=\"Your trip starts in 3 days\"\n right={\n <View style={{ width: 40, height: 40, borderRadius: 20, backgroundColor: colors.muted, alignItems: 'center', justifyContent: 'center' }}>\n <Text style={{ color: colors.foreground, fontSize: 14, fontWeight: '700' }}>MP</Text>\n </View>\n }\n />\n <View style={[{ borderRadius: 28, overflow: 'hidden', backgroundColor: '#1a2a22' }, screen ? { flex: 1, minHeight: 440 } : { height: 440 }]}>\n <GrainGradient palette=\"aurora\" seed={2} style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }} />\n <View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.28)' }} />\n <View style={{ flex: 1, justifyContent: 'flex-end', padding: 22, gap: 16 }}>\n <View>\n <Text style={{ color: 'rgba(255,255,255,0.85)', fontSize: 12, fontWeight: '700', letterSpacing: 1.2 }}>NORTHERN LIGHTS · 4 NIGHTS</Text>\n <Text style={{ color: '#fff', fontSize: 30, fontWeight: '800', letterSpacing: -0.8 }}>Aurora Weekend</Text>\n <Text style={{ color: 'rgba(255,255,255,0.8)', fontSize: 13, marginTop: 2 }}>Glass cabins, hot springs and skies that glow green</Text>\n </View>\n <View style={{ flexDirection: 'row', gap: 10, alignItems: 'center' }}>\n <Button3D appearance=\"glass\" size=\"lg\" shape=\"pill\" style={{ flex: 1 }} block>\n Start the trip\n </Button3D>\n <Button3D appearance=\"glass\" size=\"lg\" shape=\"circle\" accessibilityLabel=\"Save\">\n ♡\n </Button3D>\n <Button3D appearance=\"glass\" size=\"lg\" shape=\"circle\" accessibilityLabel=\"Share\">\n ↗\n </Button3D>\n </View>\n </View>\n </View>\n </View>\n );\n}\n"
|
|
115
|
+
"code": "import React, { useState } from 'react';\nimport { Image, Text, View } from 'react-native';\nimport { Button3D } from '@/components/ui/button-3d';\nimport { GrainGradient } from '@/components/ui/grain';\nimport { useKit } from '@/lib/kit';\n\n/** Demo control text (buttons, chips, labels on fixed shapes) stops at iOS's largest standard text size. */\nconst FONT_CAP = 1.35;\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** A screen title, as it sits above the content in your app. */\nfunction Header({ title, sub, right }: { title: string; sub?: string; right?: React.ReactNode }) {\n const { colors } = useKit();\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ flex: 1 }}>\n <Text style={{ color: colors.foreground, fontSize: 22, fontWeight: '700', letterSpacing: -0.4 }}>{title}</Text>\n {sub ? <Text style={{ color: colors.mutedForeground, fontSize: 13, marginTop: 2 }}>{sub}</Text> : null}\n </View>\n {right}\n </View>\n );\n}\n\nexport default function OnPhoto({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <View style={[{ gap: 18 }, screen && { flex: 1 }]}>\n <Header\n title=\"Good morning, Maya\"\n sub=\"Your trip starts in 3 days\"\n right={\n <View style={{ width: 40, height: 40, borderRadius: 20, backgroundColor: colors.muted, alignItems: 'center', justifyContent: 'center' }}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.foreground, fontSize: 14, fontWeight: '700' }}>MP</Text>\n </View>\n }\n />\n <View style={[{ borderRadius: 28, overflow: 'hidden', backgroundColor: '#1a2a22' }, screen ? { flex: 1, minHeight: 440 } : { height: 440 }]}>\n <GrainGradient palette=\"aurora\" seed={2} style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }} />\n <View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.28)' }} />\n <View style={{ flex: 1, justifyContent: 'flex-end', padding: 22, gap: 16 }}>\n <View>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: 'rgba(255,255,255,0.85)', fontSize: 12, fontWeight: '700', letterSpacing: 1.2 }}>NORTHERN LIGHTS · 4 NIGHTS</Text>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: '#fff', fontSize: 30, fontWeight: '800', letterSpacing: -0.8 }}>Aurora Weekend</Text>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: 'rgba(255,255,255,0.8)', fontSize: 13, marginTop: 2 }}>Glass cabins, hot springs and skies that glow green</Text>\n </View>\n <View style={{ flexDirection: 'row', gap: 10, alignItems: 'center' }}>\n <Button3D appearance=\"glass\" size=\"lg\" shape=\"pill\" style={{ flex: 1 }} block>\n Start the trip\n </Button3D>\n <Button3D appearance=\"glass\" size=\"lg\" shape=\"circle\" accessibilityLabel=\"Save\">\n ♡\n </Button3D>\n <Button3D appearance=\"glass\" size=\"lg\" shape=\"circle\" accessibilityLabel=\"Share\">\n ↗\n </Button3D>\n </View>\n </View>\n </View>\n </View>\n );\n}\n"
|
|
116
116
|
},
|
|
117
117
|
{
|
|
118
118
|
"name": "Keypad",
|
|
@@ -122,7 +122,7 @@
|
|
|
122
122
|
{
|
|
123
123
|
"name": "Checkout",
|
|
124
124
|
"title": "Checkout",
|
|
125
|
-
"code": "import React, { useState } from 'react';\nimport { Image, Text, View } from 'react-native';\nimport { Button3D } from '@/components/ui/button-3d';\nimport { useKit } from '@/lib/kit';\n\n/** Any photo works (`require()` or a URL). `blur` gives soft, abstract art. */\nconst photo = (id: number, w: number, h: number, blur = false) => ({ uri: `https://picsum.photos/id/${id}/${w}/${h}${blur ? '?blur=10' : ''}` });\n\n/** A screen title, as it sits above the content in your app. */\nfunction Header({ title, sub, right }: { title: string; sub?: string; right?: React.ReactNode }) {\n const { colors } = useKit();\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ flex: 1 }}>\n <Text style={{ color: colors.foreground, fontSize: 22, fontWeight: '700', letterSpacing: -0.4 }}>{title}</Text>\n {sub ? <Text style={{ color: colors.mutedForeground, fontSize: 13, marginTop: 2 }}>{sub}</Text> : null}\n </View>\n {right}\n </View>\n );\n}\n\nexport default function Checkout() {\n const { colors } = useKit();\n const [busy, setBusy] = useState(false);\n const items = [\n { img: 766, name: 'House blend beans', detail: '250 g · ×2', price: '₹880' },\n { img: 1060, name: 'Pour-over filters', detail: 'Pack of 100', price: '₹120' },\n ];\n const line = (label: string, value: string, strong = false) => (\n <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>\n <Text style={{ color: strong ? colors.foreground : colors.mutedForeground, fontSize: strong ? 16 : 14, fontWeight: strong ? '700' : '400' }}>{label}</Text>\n <Text style={{ color: colors.foreground, fontSize: strong ? 16 : 14, fontWeight: strong ? '700' : '500' }}>{value}</Text>\n </View>\n );\n return (\n <View style={{ gap: 18 }}>\n <Header title=\"Checkout\" sub=\"2 items · arrives tomorrow\" />\n <View style={{ gap: 12 }}>\n {items.map((it) => (\n <View key={it.name} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <Image alt=\"\" source={photo(it.img, 120, 120)} style={{ width: 52, height: 52, borderRadius: 12, backgroundColor: colors.muted }} />\n <View style={{ flex: 1 }}>\n <Text style={{ color: colors.foreground, fontSize: 15, fontWeight: '600' }}>{it.name}</Text>\n <Text style={{ color: colors.mutedForeground, fontSize: 12 }}>{it.detail}</Text>\n </View>\n <Text style={{ color: colors.foreground, fontSize: 14, fontWeight: '600' }}>{it.price}</Text>\n </View>\n ))}\n </View>\n <View style={{ gap: 8, paddingTop: 14, borderTopWidth: 1, borderTopColor: colors.border }}>\n {line('Subtotal', '₹1,000')}\n {line('Express delivery', '₹240')}\n {line('Total', '₹1,240', true)}\n </View>\n <View style={{ gap: 12 }}>\n <Button3D\n block\n size=\"lg\"\n loading={busy}\n onPress={() => {\n setBusy(true);\n setTimeout(() => setBusy(false), 1500);\n }}\n >\n Pay ₹1,240\n </Button3D>\n <Button3D block size=\"md\" color={colors.muted}>\n Add a promo code\n </Button3D>\n </View>\n </View>\n );\n}\n"
|
|
125
|
+
"code": "import React, { useState } from 'react';\nimport { Image, Text, View } from 'react-native';\nimport { Button3D } from '@/components/ui/button-3d';\nimport { useKit } from '@/lib/kit';\n\n/** Any photo works (`require()` or a URL). `blur` gives soft, abstract art. */\nconst photo = (id: number, w: number, h: number, blur = false) => ({ uri: `https://picsum.photos/id/${id}/${w}/${h}${blur ? '?blur=10' : ''}` });\n\n/** A screen title, as it sits above the content in your app. */\nfunction Header({ title, sub, right }: { title: string; sub?: string; right?: React.ReactNode }) {\n const { colors } = useKit();\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ flex: 1 }}>\n <Text style={{ color: colors.foreground, fontSize: 22, fontWeight: '700', letterSpacing: -0.4 }}>{title}</Text>\n {sub ? <Text style={{ color: colors.mutedForeground, fontSize: 13, marginTop: 2 }}>{sub}</Text> : null}\n </View>\n {right}\n </View>\n );\n}\n\nexport default function Checkout() {\n const { colors } = useKit();\n const [busy, setBusy] = useState(false);\n const items = [\n { img: 766, name: 'House blend beans', detail: '250 g · ×2', price: '₹880' },\n { img: 1060, name: 'Pour-over filters', detail: 'Pack of 100', price: '₹120' },\n ];\n const line = (label: string, value: string, strong = false) => (\n <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>\n <Text style={{ flexShrink: 1, color: strong ? colors.foreground : colors.mutedForeground, fontSize: strong ? 16 : 14, fontWeight: strong ? '700' : '400' }}>{label}</Text>\n <Text style={{ color: colors.foreground, fontSize: strong ? 16 : 14, fontWeight: strong ? '700' : '500' }}>{value}</Text>\n </View>\n );\n return (\n <View style={{ gap: 18 }}>\n <Header title=\"Checkout\" sub=\"2 items · arrives tomorrow\" />\n <View style={{ gap: 12 }}>\n {items.map((it) => (\n <View key={it.name} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <Image alt=\"\" source={photo(it.img, 120, 120)} style={{ width: 52, height: 52, borderRadius: 12, backgroundColor: colors.muted }} />\n <View style={{ flex: 1 }}>\n <Text style={{ color: colors.foreground, fontSize: 15, fontWeight: '600' }}>{it.name}</Text>\n <Text style={{ color: colors.mutedForeground, fontSize: 12 }}>{it.detail}</Text>\n </View>\n <Text style={{ color: colors.foreground, fontSize: 14, fontWeight: '600' }}>{it.price}</Text>\n </View>\n ))}\n </View>\n <View style={{ gap: 8, paddingTop: 14, borderTopWidth: 1, borderTopColor: colors.border }}>\n {line('Subtotal', '₹1,000')}\n {line('Express delivery', '₹240')}\n {line('Total', '₹1,240', true)}\n </View>\n <View style={{ gap: 12 }}>\n <Button3D\n block\n size=\"lg\"\n loading={busy}\n onPress={() => {\n setBusy(true);\n setTimeout(() => setBusy(false), 1500);\n }}\n >\n Pay ₹1,240\n </Button3D>\n <Button3D block size=\"md\" color={colors.muted}>\n Add a promo code\n </Button3D>\n </View>\n </View>\n );\n}\n"
|
|
126
126
|
},
|
|
127
127
|
{
|
|
128
128
|
"name": "Keycaps",
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
{
|
|
106
106
|
"name": "GradientAndGlow",
|
|
107
107
|
"title": "Gradient And Glow",
|
|
108
|
-
"code": "import { Pressable, Text, View } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { readableOn, useKit } from '@/lib/kit';\n\nexport default function GradientAndGlow() {\n const { colors } = useKit();\n return (\n <View style={{ gap: 12 }}>\n <Button\n variant=\"gradient\"\n size=\"lg\"\n block\n pill\n right={<Text style={{ color: readableOn(colors.accent), fontSize: 18 }}>→</Text>}\n >\n Get started\n </Button>\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Button variant=\"glow\" pill style={{ flex: 1 }}>\n Go live\n </Button>\n <Button variant=\"gradient\" pill style={{ flex: 1 }}>\n Boost\n </Button>\n </View>\n </View>\n );\n}\n"
|
|
108
|
+
"code": "import { Pressable, Text, View } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { readableOn, useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\nexport default function GradientAndGlow() {\n const { colors } = useKit();\n return (\n <View style={{ gap: 12 }}>\n <Button\n variant=\"gradient\"\n size=\"lg\"\n block\n pill\n right={<Text maxFontSizeMultiplier={FONT_CAP} style={{ color: readableOn(colors.accent), fontSize: 18 }}>→</Text>}\n >\n Get started\n </Button>\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Button variant=\"glow\" pill style={{ flex: 1 }}>\n Go live\n </Button>\n <Button variant=\"gradient\" pill style={{ flex: 1 }}>\n Boost\n </Button>\n </View>\n </View>\n );\n}\n"
|
|
109
109
|
},
|
|
110
110
|
{
|
|
111
111
|
"name": "DynamicColor",
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
{
|
|
116
116
|
"name": "Sizes",
|
|
117
117
|
"title": "Sizes",
|
|
118
|
-
"code": "import { Pressable, Text, View } from 'react-native';\nimport { Button } from '@/components/ui/button';\n\nexport default function Sizes() {\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>\n <Button size=\"sm\">Small</Button>\n <Button size=\"md\">Medium</Button>\n <Button size=\"lg\">Large</Button>\n <Button size=\"icon\" variant=\"outline\">\n <Text style={{ fontSize: 18 }}>♥</Text>\n </Button>\n </View>\n );\n}\n"
|
|
118
|
+
"code": "import { Pressable, Text, View } from 'react-native';\nimport { Button } from '@/components/ui/button';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\nexport default function Sizes() {\n return (\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', alignItems: 'center', gap: 10 }}>\n <Button size=\"sm\">Small</Button>\n <Button size=\"md\">Medium</Button>\n <Button size=\"lg\">Large</Button>\n <Button size=\"icon\" variant=\"outline\">\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 18 }}>♥</Text>\n </Button>\n </View>\n );\n}\n"
|
|
119
119
|
},
|
|
120
120
|
{
|
|
121
121
|
"name": "Loading",
|
|
@@ -172,27 +172,27 @@
|
|
|
172
172
|
{
|
|
173
173
|
"name": "Single",
|
|
174
174
|
"title": "Single",
|
|
175
|
-
"code": "import React, { useMemo, useState } from 'react';\nimport { StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Calendar, addDays, startOfDay, toDateKey, type DateRange } from '@/components/ui/calendar';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\nconst today = startOfDay(new Date());\n\nconst day = (d: Date | null) => (d ? d.toLocaleDateString(undefined, { weekday: 'short', day: 'numeric', month: 'short' }) : '—');\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. In `screen` mode it is the page itself. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n if (screen) return <View style={[s.screen, { backgroundColor: colors.background }, style]} {...rest} />;\n return <View style={[s.card, { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl }, style]} {...rest} />;\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n screen: { flex: 1, justifyContent: 'center', padding: 16, gap: 12 },\n title: { fontSize: 15, fontWeight: '400' },\n summary: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },\n row: { flexDirection: 'row', alignItems: 'center', gap: 12 },\n col: { flex: 1, gap: 2 },\n muted: { fontSize: 13 },\n strong: { fontSize: 15, fontWeight: '400' },\n pill: { paddingHorizontal: 10, paddingVertical: 6 },\n pillText: { fontSize: 13, fontWeight: '400' },\n legend: { flexDirection: 'row', flexWrap: 'wrap', columnGap: 14, rowGap: 6, justifyContent: 'center' },\n legendItem: { flexDirection: 'row', alignItems: 'center', gap: 6 },\n dot: { width: 8, height: 8, borderRadius: 4 },\n});\n\nexport default function Single({ screen }: DemoProps) {\n const [date, setDate] = useState<Date>(addDays(today, 3));\n return (\n <Card screen={screen}>\n <Ink style={s.title}>Delivery day</Ink>\n <Calendar value={date} onChange={setDate} minDate={today} />\n <View style={s.summary}>\n <Ink tone=\"muted\" style={s.muted}>Arriving</Ink>\n <Ink style={s.strong}>{day(date)}</Ink>\n </View>\n </Card>\n );\n}\n"
|
|
175
|
+
"code": "import React, { useMemo, useState } from 'react';\nimport { StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Calendar, addDays, startOfDay, toDateKey, type DateRange } from '@/components/ui/calendar';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\nconst today = startOfDay(new Date());\n\nconst day = (d: Date | null) => (d ? d.toLocaleDateString(undefined, { weekday: 'short', day: 'numeric', month: 'short' }) : '—');\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. In `screen` mode it is the page itself. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n if (screen) return <View style={[s.screen, { backgroundColor: colors.background }, style]} {...rest} />;\n return <View style={[s.card, { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl }, style]} {...rest} />;\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n screen: { flex: 1, justifyContent: 'center', padding: 16, gap: 12 },\n title: { fontSize: 15, fontWeight: '400' },\n summary: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-between', alignItems: 'center', columnGap: 12, rowGap: 2 },\n row: { flexDirection: 'row', alignItems: 'center', gap: 12 },\n col: { flex: 1, gap: 2 },\n muted: { fontSize: 13 },\n strong: { fontSize: 15, fontWeight: '400' },\n pill: { paddingHorizontal: 10, paddingVertical: 6 },\n pillText: { fontSize: 13, fontWeight: '400' },\n legend: { flexDirection: 'row', flexWrap: 'wrap', columnGap: 14, rowGap: 6, justifyContent: 'center' },\n legendItem: { flexDirection: 'row', alignItems: 'center', gap: 6 },\n dot: { width: 8, height: 8, borderRadius: 4 },\n});\n\nexport default function Single({ screen }: DemoProps) {\n const [date, setDate] = useState<Date>(addDays(today, 3));\n return (\n <Card screen={screen}>\n <Ink style={s.title}>Delivery day</Ink>\n <Calendar value={date} onChange={setDate} minDate={today} />\n <View style={s.summary}>\n <Ink tone=\"muted\" style={s.muted}>Arriving</Ink>\n <Ink style={[s.strong, { flexShrink: 1 }]}>{day(date)}</Ink>\n </View>\n </Card>\n );\n}\n"
|
|
176
176
|
},
|
|
177
177
|
{
|
|
178
178
|
"name": "Range",
|
|
179
179
|
"title": "Range",
|
|
180
|
-
"code": "import React, { useMemo, useState } from 'react';\nimport { StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Calendar, addDays, startOfDay, toDateKey, type DateRange } from '@/components/ui/calendar';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\nconst today = startOfDay(new Date());\n\nconst day = (d: Date | null) => (d ? d.toLocaleDateString(undefined, { weekday: 'short', day: 'numeric', month: 'short' }) : '—');\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. In `screen` mode it is the page itself. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n if (screen) return <View style={[s.screen, { backgroundColor: colors.background }, style]} {...rest} />;\n return <View style={[s.card, { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl }, style]} {...rest} />;\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n screen: { flex: 1, justifyContent: 'center', padding: 16, gap: 12 },\n title: { fontSize: 15, fontWeight: '400' },\n summary: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },\n row: { flexDirection: 'row', alignItems: 'center', gap: 12 },\n col: { flex: 1, gap: 2 },\n muted: { fontSize: 13 },\n strong: { fontSize: 15, fontWeight: '400' },\n pill: { paddingHorizontal: 10, paddingVertical: 6 },\n pillText: { fontSize: 13, fontWeight: '400' },\n legend: { flexDirection: 'row', flexWrap: 'wrap', columnGap: 14, rowGap: 6, justifyContent: 'center' },\n legendItem: { flexDirection: 'row', alignItems: 'center', gap: 6 },\n dot: { width: 8, height: 8, borderRadius: 4 },\n});\n\nexport default function Range({ screen }: DemoProps) {\n const [stay, setStay] = useState<DateRange>({ start: addDays(today, 4), end: addDays(today, 8) });\n const { colors, radius } = useKit();\n const nights = stay.start && stay.end ? Math.round((stay.end.getTime() - stay.start.getTime()) / 86400000) : 0;\n return (\n <Card screen={screen}>\n <Ink style={s.title}>Your stay · Lisbon</Ink>\n <Calendar mode=\"range\" value={stay} onChange={setStay} minDate={today} />\n <View style={s.row}>\n <View style={s.col}>\n <Ink tone=\"muted\" style={s.muted}>Check-in</Ink>\n <Ink style={s.strong}>{day(stay.start)}</Ink>\n </View>\n <View style={s.col}>\n <Ink tone=\"muted\" style={s.muted}>Check-out</Ink>\n <Ink style={s.strong}>{stay.end ? day(stay.end) : 'Select a date'}</Ink>\n </View>\n <View style={[s.pill, { opacity: nights ? 1 : 0.4, backgroundColor: alpha(colors.accent, 0.14), borderRadius: radius.capsule(30) }]}>\n <Text style={[s.pillText, { color: colors.accent }]}>{nights ? `${nights} night${nights > 1 ? 's' : ''}` : '0 nights'}</Text>\n </View>\n </View>\n </Card>\n );\n}\n"
|
|
180
|
+
"code": "import React, { useMemo, useState } from 'react';\nimport { StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Calendar, addDays, startOfDay, toDateKey, type DateRange } from '@/components/ui/calendar';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\nconst today = startOfDay(new Date());\n\nconst day = (d: Date | null) => (d ? d.toLocaleDateString(undefined, { weekday: 'short', day: 'numeric', month: 'short' }) : '—');\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. In `screen` mode it is the page itself. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n if (screen) return <View style={[s.screen, { backgroundColor: colors.background }, style]} {...rest} />;\n return <View style={[s.card, { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl }, style]} {...rest} />;\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n screen: { flex: 1, justifyContent: 'center', padding: 16, gap: 12 },\n title: { fontSize: 15, fontWeight: '400' },\n summary: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-between', alignItems: 'center', columnGap: 12, rowGap: 2 },\n row: { flexDirection: 'row', alignItems: 'center', gap: 12 },\n col: { flex: 1, gap: 2 },\n muted: { fontSize: 13 },\n strong: { fontSize: 15, fontWeight: '400' },\n pill: { paddingHorizontal: 10, paddingVertical: 6 },\n pillText: { fontSize: 13, fontWeight: '400' },\n legend: { flexDirection: 'row', flexWrap: 'wrap', columnGap: 14, rowGap: 6, justifyContent: 'center' },\n legendItem: { flexDirection: 'row', alignItems: 'center', gap: 6 },\n dot: { width: 8, height: 8, borderRadius: 4 },\n});\n\nexport default function Range({ screen }: DemoProps) {\n const [stay, setStay] = useState<DateRange>({ start: addDays(today, 4), end: addDays(today, 8) });\n const { colors, radius } = useKit();\n const nights = stay.start && stay.end ? Math.round((stay.end.getTime() - stay.start.getTime()) / 86400000) : 0;\n return (\n <Card screen={screen}>\n <Ink style={s.title}>Your stay · Lisbon</Ink>\n <Calendar mode=\"range\" value={stay} onChange={setStay} minDate={today} />\n <View style={s.row}>\n <View style={s.col}>\n <Ink tone=\"muted\" style={s.muted}>Check-in</Ink>\n <Ink style={s.strong}>{day(stay.start)}</Ink>\n </View>\n <View style={s.col}>\n <Ink tone=\"muted\" style={s.muted}>Check-out</Ink>\n <Ink style={s.strong}>{stay.end ? day(stay.end) : 'Select a date'}</Ink>\n </View>\n <View style={[s.pill, { opacity: nights ? 1 : 0.4, backgroundColor: alpha(colors.accent, 0.14), borderRadius: radius.capsule(30) }]}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={[s.pillText, { color: colors.accent }]}>{nights ? `${nights} night${nights > 1 ? 's' : ''}` : '0 nights'}</Text>\n </View>\n </View>\n </Card>\n );\n}\n"
|
|
181
181
|
},
|
|
182
182
|
{
|
|
183
183
|
"name": "Marked",
|
|
184
184
|
"title": "Marked",
|
|
185
|
-
"code": "import React, { useMemo, useState } from 'react';\nimport { StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Calendar, addDays, startOfDay, toDateKey, type DateRange } from '@/components/ui/calendar';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\nconst today = startOfDay(new Date());\n\nconst day = (d: Date | null) => (d ? d.toLocaleDateString(undefined, { weekday: 'short', day: 'numeric', month: 'short' }) : '—');\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. In `screen` mode it is the page itself. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n if (screen) return <View style={[s.screen, { backgroundColor: colors.background }, style]} {...rest} />;\n return <View style={[s.card, { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl }, style]} {...rest} />;\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nconst MEETING = '#38bdf8';\n\nconst BIRTHDAY = '#f472b6';\n\nconst TRIP = '#f59e0b';\n\nconst DONE = '#22c55e';\n\nconst DUE = '#ef4444';\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n screen: { flex: 1, justifyContent: 'center', padding: 16, gap: 12 },\n title: { fontSize: 15, fontWeight: '400' },\n summary: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },\n row: { flexDirection: 'row', alignItems: 'center', gap: 12 },\n col: { flex: 1, gap: 2 },\n muted: { fontSize: 13 },\n strong: { fontSize: 15, fontWeight: '400' },\n pill: { paddingHorizontal: 10, paddingVertical: 6 },\n pillText: { fontSize: 13, fontWeight: '400' },\n legend: { flexDirection: 'row', flexWrap: 'wrap', columnGap: 14, rowGap: 6, justifyContent: 'center' },\n legendItem: { flexDirection: 'row', alignItems: 'center', gap: 6 },\n dot: { width: 8, height: 8, borderRadius: 4 },\n});\n\nexport default function Marked({ screen }: DemoProps) {\n const [date, setDate] = useState<Date>(today);\n const { highlights, marks } = useMemo(() => {\n const y = today.getFullYear();\n const m = today.getMonth();\n const k = (d: number) => toDateKey(new Date(y, m, d));\n return {\n // Whole-day fills: who / what owns the day.\n highlights: { [k(2)]: MEETING, [k(14)]: BIRTHDAY, [k(18)]: TRIP, [k(21)]: MEETING, [k(31)]: BIRTHDAY },\n // Dots underneath: smaller things on that day.\n marks: { [k(3)]: DONE, [k(8)]: DUE, [k(12)]: DONE, [k(17)]: DONE, [k(22)]: [DONE, DUE], [k(26)]: DUE },\n };\n }, []);\n return (\n <Card screen={screen}>\n <Ink style={s.title}>Schedule</Ink>\n <Calendar value={date} onChange={setDate} highlightedDates={highlights} markedDates={marks} weekStartsOn={1} />\n <View style={s.legend}>\n {[\n ['Meeting', MEETING],\n ['Birthday', BIRTHDAY],\n ['Trip', TRIP],\n ['Done', DONE],\n ['Due', DUE],\n ].map(([label, color]) => (\n <View key={label} style={s.legendItem}>\n <View style={[s.dot, { backgroundColor: color }]} />\n <Ink tone=\"muted\" style={s.muted}>{label}</Ink>\n </View>\n ))}\n </View>\n </Card>\n );\n}\n"
|
|
185
|
+
"code": "import React, { useMemo, useState } from 'react';\nimport { StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Calendar, addDays, startOfDay, toDateKey, type DateRange } from '@/components/ui/calendar';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\nconst today = startOfDay(new Date());\n\nconst day = (d: Date | null) => (d ? d.toLocaleDateString(undefined, { weekday: 'short', day: 'numeric', month: 'short' }) : '—');\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. In `screen` mode it is the page itself. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n if (screen) return <View style={[s.screen, { backgroundColor: colors.background }, style]} {...rest} />;\n return <View style={[s.card, { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl }, style]} {...rest} />;\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nconst MEETING = '#38bdf8';\n\nconst BIRTHDAY = '#f472b6';\n\nconst TRIP = '#f59e0b';\n\nconst DONE = '#22c55e';\n\nconst DUE = '#ef4444';\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n screen: { flex: 1, justifyContent: 'center', padding: 16, gap: 12 },\n title: { fontSize: 15, fontWeight: '400' },\n summary: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-between', alignItems: 'center', columnGap: 12, rowGap: 2 },\n row: { flexDirection: 'row', alignItems: 'center', gap: 12 },\n col: { flex: 1, gap: 2 },\n muted: { fontSize: 13 },\n strong: { fontSize: 15, fontWeight: '400' },\n pill: { paddingHorizontal: 10, paddingVertical: 6 },\n pillText: { fontSize: 13, fontWeight: '400' },\n legend: { flexDirection: 'row', flexWrap: 'wrap', columnGap: 14, rowGap: 6, justifyContent: 'center' },\n legendItem: { flexDirection: 'row', alignItems: 'center', gap: 6 },\n dot: { width: 8, height: 8, borderRadius: 4 },\n});\n\nexport default function Marked({ screen }: DemoProps) {\n const [date, setDate] = useState<Date>(today);\n const { highlights, marks } = useMemo(() => {\n const y = today.getFullYear();\n const m = today.getMonth();\n const k = (d: number) => toDateKey(new Date(y, m, d));\n return {\n // Whole-day fills: who / what owns the day.\n highlights: { [k(2)]: MEETING, [k(14)]: BIRTHDAY, [k(18)]: TRIP, [k(21)]: MEETING, [k(31)]: BIRTHDAY },\n // Dots underneath: smaller things on that day.\n marks: { [k(3)]: DONE, [k(8)]: DUE, [k(12)]: DONE, [k(17)]: DONE, [k(22)]: [DONE, DUE], [k(26)]: DUE },\n };\n }, []);\n return (\n <Card screen={screen}>\n <Ink style={s.title}>Schedule</Ink>\n <Calendar value={date} onChange={setDate} highlightedDates={highlights} markedDates={marks} weekStartsOn={1} />\n <View style={s.legend}>\n {[\n ['Meeting', MEETING],\n ['Birthday', BIRTHDAY],\n ['Trip', TRIP],\n ['Done', DONE],\n ['Due', DUE],\n ].map(([label, color]) => (\n <View key={label} style={s.legendItem}>\n <View style={[s.dot, { backgroundColor: color }]} />\n <Ink tone=\"muted\" style={s.muted}>{label}</Ink>\n </View>\n ))}\n </View>\n </Card>\n );\n}\n"
|
|
186
186
|
},
|
|
187
187
|
{
|
|
188
188
|
"name": "Booking",
|
|
189
189
|
"title": "Booking",
|
|
190
|
-
"code": "import React, { useMemo, useState } from 'react';\nimport { StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Calendar, addDays, startOfDay, toDateKey, type DateRange } from '@/components/ui/calendar';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\nconst today = startOfDay(new Date());\n\nconst day = (d: Date | null) => (d ? d.toLocaleDateString(undefined, { weekday: 'short', day: 'numeric', month: 'short' }) : '—');\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. In `screen` mode it is the page itself. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n if (screen) return <View style={[s.screen, { backgroundColor: colors.background }, style]} {...rest} />;\n return <View style={[s.card, { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl }, style]} {...rest} />;\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n screen: { flex: 1, justifyContent: 'center', padding: 16, gap: 12 },\n title: { fontSize: 15, fontWeight: '400' },\n summary: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },\n row: { flexDirection: 'row', alignItems: 'center', gap: 12 },\n col: { flex: 1, gap: 2 },\n muted: { fontSize: 13 },\n strong: { fontSize: 15, fontWeight: '400' },\n pill: { paddingHorizontal: 10, paddingVertical: 6 },\n pillText: { fontSize: 13, fontWeight: '400' },\n legend: { flexDirection: 'row', flexWrap: 'wrap', columnGap: 14, rowGap: 6, justifyContent: 'center' },\n legendItem: { flexDirection: 'row', alignItems: 'center', gap: 6 },\n dot: { width: 8, height: 8, borderRadius: 4 },\n});\n\nexport default function Booking({ screen }: DemoProps) {\n const booked = useMemo(() => new Set([2, 5, 6, 11, 19].map((n) => toDateKey(addDays(today, n)))), []);\n const [date, setDate] = useState<Date | null>(null);\n return (\n <Card screen={screen}>\n <Ink style={s.title}>Book a consultation</Ink>\n <Calendar\n value={date}\n onChange={setDate}\n minDate={today}\n maxDate={addDays(today, 45)}\n isDateDisabled={(d) => d.getDay() === 0 || booked.has(toDateKey(d))}\n showOutsideDays\n fixedWeeks\n />\n <Ink tone=\"muted\" style={s.muted}>\n {date ? `Next: choose a time on ${day(date)}` : 'Faded days are fully booked or closed on Sundays.'}\n </Ink>\n </Card>\n );\n}\n"
|
|
190
|
+
"code": "import React, { useMemo, useState } from 'react';\nimport { StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Calendar, addDays, startOfDay, toDateKey, type DateRange } from '@/components/ui/calendar';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\nconst today = startOfDay(new Date());\n\nconst day = (d: Date | null) => (d ? d.toLocaleDateString(undefined, { weekday: 'short', day: 'numeric', month: 'short' }) : '—');\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. In `screen` mode it is the page itself. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n if (screen) return <View style={[s.screen, { backgroundColor: colors.background }, style]} {...rest} />;\n return <View style={[s.card, { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl }, style]} {...rest} />;\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n screen: { flex: 1, justifyContent: 'center', padding: 16, gap: 12 },\n title: { fontSize: 15, fontWeight: '400' },\n summary: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-between', alignItems: 'center', columnGap: 12, rowGap: 2 },\n row: { flexDirection: 'row', alignItems: 'center', gap: 12 },\n col: { flex: 1, gap: 2 },\n muted: { fontSize: 13 },\n strong: { fontSize: 15, fontWeight: '400' },\n pill: { paddingHorizontal: 10, paddingVertical: 6 },\n pillText: { fontSize: 13, fontWeight: '400' },\n legend: { flexDirection: 'row', flexWrap: 'wrap', columnGap: 14, rowGap: 6, justifyContent: 'center' },\n legendItem: { flexDirection: 'row', alignItems: 'center', gap: 6 },\n dot: { width: 8, height: 8, borderRadius: 4 },\n});\n\nexport default function Booking({ screen }: DemoProps) {\n const booked = useMemo(() => new Set([2, 5, 6, 11, 19].map((n) => toDateKey(addDays(today, n)))), []);\n const [date, setDate] = useState<Date | null>(null);\n return (\n <Card screen={screen}>\n <Ink style={s.title}>Book a consultation</Ink>\n <Calendar\n value={date}\n onChange={setDate}\n minDate={today}\n maxDate={addDays(today, 45)}\n isDateDisabled={(d) => d.getDay() === 0 || booked.has(toDateKey(d))}\n showOutsideDays\n fixedWeeks\n />\n <Ink tone=\"muted\" style={s.muted}>\n {date ? `Next: choose a time on ${day(date)}` : 'Faded days are fully booked or closed on Sundays.'}\n </Ink>\n </Card>\n );\n}\n"
|
|
191
191
|
},
|
|
192
192
|
{
|
|
193
193
|
"name": "WeekStartsMonday",
|
|
194
194
|
"title": "Week Starts Monday",
|
|
195
|
-
"code": "import React, { useMemo, useState } from 'react';\nimport { StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Calendar, addDays, startOfDay, toDateKey, type DateRange } from '@/components/ui/calendar';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\nconst today = startOfDay(new Date());\n\nconst day = (d: Date | null) => (d ? d.toLocaleDateString(undefined, { weekday: 'short', day: 'numeric', month: 'short' }) : '—');\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. In `screen` mode it is the page itself. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n if (screen) return <View style={[s.screen, { backgroundColor: colors.background }, style]} {...rest} />;\n return <View style={[s.card, { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl }, style]} {...rest} />;\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n screen: { flex: 1, justifyContent: 'center', padding: 16, gap: 12 },\n title: { fontSize: 15, fontWeight: '400' },\n summary: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },\n row: { flexDirection: 'row', alignItems: 'center', gap: 12 },\n col: { flex: 1, gap: 2 },\n muted: { fontSize: 13 },\n strong: { fontSize: 15, fontWeight: '400' },\n pill: { paddingHorizontal: 10, paddingVertical: 6 },\n pillText: { fontSize: 13, fontWeight: '400' },\n legend: { flexDirection: 'row', flexWrap: 'wrap', columnGap: 14, rowGap: 6, justifyContent: 'center' },\n legendItem: { flexDirection: 'row', alignItems: 'center', gap: 6 },\n dot: { width: 8, height: 8, borderRadius: 4 },\n});\n\nexport default function WeekStartsMonday({ screen }: DemoProps) {\n const [days, setDays] = useState<Date[]>([addDays(today, 1), addDays(today, 3)]);\n return (\n <Card screen={screen}>\n <Ink style={s.title}>Training days · pick up to 3</Ink>\n <Calendar mode=\"multiple\" value={days} onChange={setDays} max={3} weekStartsOn={1} locale=\"en-GB\" showOutsideDays />\n <Ink tone=\"muted\" style={s.muted}>{days.length ? days.map((d) => day(d)).join(' · ') : 'No sessions planned'}</Ink>\n </Card>\n );\n}\n"
|
|
195
|
+
"code": "import React, { useMemo, useState } from 'react';\nimport { StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Calendar, addDays, startOfDay, toDateKey, type DateRange } from '@/components/ui/calendar';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\nconst today = startOfDay(new Date());\n\nconst day = (d: Date | null) => (d ? d.toLocaleDateString(undefined, { weekday: 'short', day: 'numeric', month: 'short' }) : '—');\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. In `screen` mode it is the page itself. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n if (screen) return <View style={[s.screen, { backgroundColor: colors.background }, style]} {...rest} />;\n return <View style={[s.card, { backgroundColor: colors.card, borderColor: colors.border, borderRadius: radius.xl }, style]} {...rest} />;\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n screen: { flex: 1, justifyContent: 'center', padding: 16, gap: 12 },\n title: { fontSize: 15, fontWeight: '400' },\n summary: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-between', alignItems: 'center', columnGap: 12, rowGap: 2 },\n row: { flexDirection: 'row', alignItems: 'center', gap: 12 },\n col: { flex: 1, gap: 2 },\n muted: { fontSize: 13 },\n strong: { fontSize: 15, fontWeight: '400' },\n pill: { paddingHorizontal: 10, paddingVertical: 6 },\n pillText: { fontSize: 13, fontWeight: '400' },\n legend: { flexDirection: 'row', flexWrap: 'wrap', columnGap: 14, rowGap: 6, justifyContent: 'center' },\n legendItem: { flexDirection: 'row', alignItems: 'center', gap: 6 },\n dot: { width: 8, height: 8, borderRadius: 4 },\n});\n\nexport default function WeekStartsMonday({ screen }: DemoProps) {\n const [days, setDays] = useState<Date[]>([addDays(today, 1), addDays(today, 3)]);\n return (\n <Card screen={screen}>\n <Ink style={s.title}>Training days · pick up to 3</Ink>\n <Calendar mode=\"multiple\" value={days} onChange={setDays} max={3} weekStartsOn={1} locale=\"en-GB\" showOutsideDays />\n <Ink tone=\"muted\" style={s.muted}>{days.length ? days.map((d) => day(d)).join(' · ') : 'No sessions planned'}</Ink>\n </Card>\n );\n}\n"
|
|
196
196
|
}
|
|
197
197
|
]
|
|
198
198
|
}
|
package/registry/_docs/card.json
CHANGED
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
{
|
|
111
111
|
"name": "Default",
|
|
112
112
|
"title": "Default",
|
|
113
|
-
"code": "import { Text, View } from 'react-native';\nimport { Card } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { useKit } from '@/lib/kit';\n\nexport default function Default() {\n const { colors } = useKit();\n return (\n <Card>\n <Card.Header action={<Text style={{ fontSize: 18, color: colors.mutedForeground }}>⋯</Text>}>\n <Card.Title>Invite your team</Card.Title>\n <Card.Description>Collaborators can edit boards, leave comments and see version history.</Card.Description>\n </Card.Header>\n <Card.Content>\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>3 of 10 seats used on the Starter plan</Text>\n </Card.Content>\n <Card.Footer>\n <Button variant=\"outline\" size=\"sm\">Copy link</Button>\n <Button variant=\"accent\" size=\"sm\">Send invites</Button>\n </Card.Footer>\n </Card>\n );\n}\n"
|
|
113
|
+
"code": "import { Text, View } from 'react-native';\nimport { Card } from '@/components/ui/card';\nimport { Button } from '@/components/ui/button';\nimport { useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\nexport default function Default() {\n const { colors } = useKit();\n return (\n <Card>\n <Card.Header action={<Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 18, color: colors.mutedForeground }}>⋯</Text>}>\n <Card.Title>Invite your team</Card.Title>\n <Card.Description>Collaborators can edit boards, leave comments and see version history.</Card.Description>\n </Card.Header>\n <Card.Content>\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>3 of 10 seats used on the Starter plan</Text>\n </Card.Content>\n <Card.Footer>\n <Button variant=\"outline\" size=\"sm\">Copy link</Button>\n <Button variant=\"accent\" size=\"sm\">Send invites</Button>\n </Card.Footer>\n </Card>\n );\n}\n"
|
|
114
114
|
},
|
|
115
115
|
{
|
|
116
116
|
"name": "Variants",
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
{
|
|
122
122
|
"name": "Scrollable",
|
|
123
123
|
"title": "Scrollable",
|
|
124
|
-
"code": "import React, { useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { ChipGroup } from '@/components/ui/chip-group';\n\nexport default function Scrollable() {\n const [picked, setPicked] = useState<string[]>(['all']);\n return (\n <ChipGroup\n scrollable\n deselectable={false}\n value={picked}\n onValueChange={setPicked}\n options={[\n { value: 'all', label: 'All' },\n { value: 'coffee', label: 'Coffee', icon: <Text>☕️</Text> },\n { value: 'bakery', label: 'Bakery', icon: <Text>🥐</Text> },\n { value: 'brunch', label: 'Brunch', icon: <Text>🍳</Text> },\n { value: 'pizza', label: 'Pizza', icon: <Text>🍕</Text> },\n { value: 'sushi', label: 'Sushi', icon: <Text>🍣</Text> },\n ]}\n />\n );\n}\n"
|
|
124
|
+
"code": "import React, { useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { ChipGroup } from '@/components/ui/chip-group';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\nexport default function Scrollable() {\n const [picked, setPicked] = useState<string[]>(['all']);\n return (\n <ChipGroup\n scrollable\n deselectable={false}\n value={picked}\n onValueChange={setPicked}\n options={[\n { value: 'all', label: 'All' },\n { value: 'coffee', label: 'Coffee', icon: <Text maxFontSizeMultiplier={FONT_CAP}>☕️</Text> },\n { value: 'bakery', label: 'Bakery', icon: <Text maxFontSizeMultiplier={FONT_CAP}>🥐</Text> },\n { value: 'brunch', label: 'Brunch', icon: <Text maxFontSizeMultiplier={FONT_CAP}>🍳</Text> },\n { value: 'pizza', label: 'Pizza', icon: <Text maxFontSizeMultiplier={FONT_CAP}>🍕</Text> },\n { value: 'sushi', label: 'Sushi', icon: <Text maxFontSizeMultiplier={FONT_CAP}>🍣</Text> },\n ]}\n />\n );\n}\n"
|
|
125
125
|
},
|
|
126
126
|
{
|
|
127
127
|
"name": "Sizes",
|
|
@@ -86,17 +86,17 @@
|
|
|
86
86
|
{
|
|
87
87
|
"name": "Celebrate",
|
|
88
88
|
"title": "Celebrate",
|
|
89
|
-
"code": "import React, { useRef, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { Confetti, type ConfettiHandle } from '@/components/ui/confetti';\nimport { useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\nexport default function Celebrate({ screen }: DemoProps) {\n const { colors, radius } = useKit();\n const confetti = useRef<ConfettiHandle>(null);\n return (\n <View\n style={[\n { overflow: 'hidden', alignItems: 'center', justifyContent: 'center', gap: 10, padding: 24 },\n screen ? { flex: 1, backgroundColor: colors.background } : { height: 420, borderRadius: radius.xl, backgroundColor: colors.muted },\n ]}\n >\n <Text style={{ fontSize: 44 }}>🎉</Text>\n <Text style={{ color: colors.foreground, fontSize: 24, fontWeight: '800', letterSpacing: -0.5 }}>Order placed!</Text>\n <Text style={{ color: colors.mutedForeground, fontSize: 14, textAlign: 'center' }}>You saved ₹240 with coupon GLOW50. Arriving Thursday.</Text>\n <Pressable\n onPress={() => confetti.current?.burst()}\n accessibilityRole=\"button\"\n style={{ marginTop: 12, paddingHorizontal: 22, paddingVertical: 13, borderRadius: 999, backgroundColor: colors.primary }}\n >\n <Text style={{ color: colors.primaryForeground, fontSize: 15, fontWeight: '700' }}>Celebrate again</Text>\n </Pressable>\n <Confetti ref={confetti} autoplay />\n </View>\n );\n}\n"
|
|
89
|
+
"code": "import React, { useRef, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { Confetti, type ConfettiHandle } from '@/components/ui/confetti';\nimport { useKit } from '@/lib/kit';\n\n/** Control text stops at iOS's largest standard size. */\nconst FONT_CAP = 1.35;\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\nexport default function Celebrate({ screen }: DemoProps) {\n const { colors, radius } = useKit();\n const confetti = useRef<ConfettiHandle>(null);\n return (\n <View\n style={[\n { overflow: 'hidden', alignItems: 'center', justifyContent: 'center', gap: 10, padding: 24 },\n screen ? { flex: 1, backgroundColor: colors.background } : { height: 420, borderRadius: radius.xl, backgroundColor: colors.muted },\n ]}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 44 }}>🎉</Text>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.foreground, fontSize: 24, fontWeight: '800', letterSpacing: -0.5 }}>Order placed!</Text>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.mutedForeground, fontSize: 14, textAlign: 'center' }}>You saved ₹240 with coupon GLOW50. Arriving Thursday.</Text>\n <Pressable\n onPress={() => confetti.current?.burst()}\n accessibilityRole=\"button\"\n style={{ marginTop: 12, paddingHorizontal: 22, paddingVertical: 13, borderRadius: 999, backgroundColor: colors.primary }}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.primaryForeground, fontSize: 15, fontWeight: '700' }}>Celebrate again</Text>\n </Pressable>\n <Confetti ref={confetti} autoplay />\n </View>\n );\n}\n"
|
|
90
90
|
},
|
|
91
91
|
{
|
|
92
92
|
"name": "Rain",
|
|
93
93
|
"title": "Rain",
|
|
94
|
-
"code": "import React, { useRef, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { Confetti, type ConfettiHandle } from '@/components/ui/confetti';\nimport { useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\nexport default function Rain({ screen }: DemoProps) {\n const { colors, radius } = useKit();\n const [shower, setShower] = useState(0);\n return (\n <View\n style={[\n { overflow: 'hidden', alignItems: 'center', justifyContent: 'center' },\n screen ? { flex: 1, backgroundColor: colors.background } : { height: 360, borderRadius: radius.xl, backgroundColor: colors.muted },\n ]}\n >\n <Pressable onPress={() => setShower((n) => n + 1)} accessibilityRole=\"button\" style={{ paddingHorizontal: 22, paddingVertical: 13, borderRadius: 999, borderWidth: 1.5, borderColor: colors.foreground }}>\n <Text style={{ color: colors.foreground, fontSize: 15, fontWeight: '700' }}>Make it rain</Text>\n </Pressable>\n <Confetti origin=\"top\" count={120} trigger={shower} colors={['#ffd23f', '#ffb000', '#fff3b0', '#ffffff']} />\n </View>\n );\n}\n"
|
|
94
|
+
"code": "import React, { useRef, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { Confetti, type ConfettiHandle } from '@/components/ui/confetti';\nimport { useKit } from '@/lib/kit';\n\n/** Control text stops at iOS's largest standard size. */\nconst FONT_CAP = 1.35;\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\nexport default function Rain({ screen }: DemoProps) {\n const { colors, radius } = useKit();\n const [shower, setShower] = useState(0);\n return (\n <View\n style={[\n { overflow: 'hidden', alignItems: 'center', justifyContent: 'center' },\n screen ? { flex: 1, backgroundColor: colors.background } : { height: 360, borderRadius: radius.xl, backgroundColor: colors.muted },\n ]}\n >\n <Pressable onPress={() => setShower((n) => n + 1)} accessibilityRole=\"button\" style={{ paddingHorizontal: 22, paddingVertical: 13, borderRadius: 999, borderWidth: 1.5, borderColor: colors.foreground }}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.foreground, fontSize: 15, fontWeight: '700' }}>Make it rain</Text>\n </Pressable>\n <Confetti origin=\"top\" count={120} trigger={shower} colors={['#ffd23f', '#ffb000', '#fff3b0', '#ffffff']} />\n </View>\n );\n}\n"
|
|
95
95
|
},
|
|
96
96
|
{
|
|
97
97
|
"name": "TapToPop",
|
|
98
98
|
"title": "Tap To Pop",
|
|
99
|
-
"code": "import React, { useRef, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { Confetti, type ConfettiHandle } from '@/components/ui/confetti';\nimport { useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\nexport default function TapToPop({ screen }: DemoProps) {\n const { colors, radius } = useKit();\n const confetti = useRef<ConfettiHandle>(null);\n return (\n <Pressable\n onPress={(e) => confetti.current?.burst({ origin: 'center', x: e.nativeEvent.locationX, y: e.nativeEvent.locationY, count: 50 })}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Tap to pop confetti\"\n style={[\n { overflow: 'hidden', alignItems: 'center', justifyContent: 'center' },\n screen ? { flex: 1, backgroundColor: colors.background } : { height: 360, borderRadius: radius.xl, backgroundColor: colors.muted },\n ]}\n >\n {/* Not touchable, so locationX/Y are always relative to the panel. */}\n <View pointerEvents=\"none\">\n <Text style={{ color: colors.mutedForeground, fontSize: 15, fontWeight: '600' }}>Tap anywhere</Text>\n </View>\n <Confetti ref={confetti} />\n </Pressable>\n );\n}\n"
|
|
99
|
+
"code": "import React, { useRef, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { Confetti, type ConfettiHandle } from '@/components/ui/confetti';\nimport { useKit } from '@/lib/kit';\n\n/** Control text stops at iOS's largest standard size. */\nconst FONT_CAP = 1.35;\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\nexport default function TapToPop({ screen }: DemoProps) {\n const { colors, radius } = useKit();\n const confetti = useRef<ConfettiHandle>(null);\n return (\n <Pressable\n onPress={(e) => confetti.current?.burst({ origin: 'center', x: e.nativeEvent.locationX, y: e.nativeEvent.locationY, count: 50 })}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Tap to pop confetti\"\n style={[\n { overflow: 'hidden', alignItems: 'center', justifyContent: 'center' },\n screen ? { flex: 1, backgroundColor: colors.background } : { height: 360, borderRadius: radius.xl, backgroundColor: colors.muted },\n ]}\n >\n {/* Not touchable, so locationX/Y are always relative to the panel. */}\n <View pointerEvents=\"none\">\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.mutedForeground, fontSize: 15, fontWeight: '600' }}>Tap anywhere</Text>\n </View>\n <Confetti ref={confetti} />\n </Pressable>\n );\n}\n"
|
|
100
100
|
}
|
|
101
101
|
]
|
|
102
102
|
}
|