@expokit/cli 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -2
- package/bin/expo-kit.js +219 -44
- package/bin/lib.mjs +110 -1
- package/bin/mcp.mjs +48 -19
- package/package.json +9 -4
- package/registry/_docs/accordion.json +16 -0
- package/registry/_docs/action-sheet.json +28 -0
- package/registry/_docs/alert.json +28 -0
- package/registry/_docs/assistant-app.json +47 -0
- package/registry/_docs/avatar.json +25 -1
- package/registry/_docs/badge.json +36 -5
- package/registry/_docs/bottom-nav.json +30 -6
- package/registry/_docs/bottom-sheet.json +28 -0
- package/registry/_docs/bullet.json +3 -0
- package/registry/_docs/button-3d.json +18 -2
- package/registry/_docs/button.json +16 -0
- package/registry/_docs/calendar.json +21 -5
- package/registry/_docs/card-stack.json +16 -0
- package/registry/_docs/card.json +20 -0
- package/registry/_docs/carousel.json +20 -0
- package/registry/_docs/chat.json +184 -0
- package/registry/_docs/checkbox.json +16 -0
- package/registry/_docs/checkout.json +10 -1
- package/registry/_docs/chip-group.json +16 -0
- package/registry/_docs/confetti.json +23 -3
- package/registry/_docs/coupon.json +16 -0
- package/registry/_docs/cube-pager.json +16 -0
- package/registry/_docs/dashboard.json +3 -0
- package/registry/_docs/date-picker.json +24 -4
- package/registry/_docs/dialog.json +24 -0
- package/registry/_docs/donut-shop.json +88 -0
- package/registry/_docs/drawer.json +32 -4
- package/registry/_docs/fab.json +24 -0
- package/registry/_docs/fintech-app.json +49 -0
- package/registry/_docs/flip-card.json +16 -0
- package/registry/_docs/floating-input.json +25 -5
- package/registry/_docs/food-app.json +47 -0
- package/registry/_docs/gift-card.json +16 -0
- package/registry/_docs/glass-button.json +24 -0
- package/registry/_docs/glass-nav-bar.json +24 -0
- package/registry/_docs/glass-search-bar.json +24 -0
- package/registry/_docs/glass-segmented.json +28 -0
- package/registry/_docs/glass-sheet.json +28 -0
- package/registry/_docs/glass-tab-bar.json +24 -0
- package/registry/_docs/glass.json +17 -1
- package/registry/_docs/grain.json +80 -0
- package/registry/_docs/inbox.json +3 -0
- package/registry/_docs/isometric-bar.json +3 -0
- package/registry/_docs/kit.json +3 -0
- package/registry/_docs/liquidglass-tab.json +3 -0
- package/registry/_docs/list-item.json +16 -0
- package/registry/_docs/loyalty-card.json +16 -0
- package/registry/_docs/morphing-button-group.json +96 -0
- package/registry/_docs/number-ticker.json +16 -0
- package/registry/_docs/offers.json +3 -0
- package/registry/_docs/onboarding.json +3 -0
- package/registry/_docs/orb-reveal.json +132 -0
- package/registry/_docs/otp-input.json +28 -4
- package/registry/_docs/parallax-layers.json +20 -0
- package/registry/_docs/parallax-scroll.json +24 -0
- package/registry/_docs/password-input.json +23 -3
- package/registry/_docs/payment-card.json +16 -0
- package/registry/_docs/pixel-bar.json +3 -0
- package/registry/_docs/polar-area.json +3 -0
- package/registry/_docs/profile.json +3 -0
- package/registry/_docs/progress-steps.json +24 -0
- package/registry/_docs/progress.json +20 -0
- package/registry/_docs/qr-code-button.json +122 -0
- package/registry/_docs/radio-group.json +20 -0
- package/registry/_docs/rating.json +24 -0
- package/registry/_docs/ring-carousel.json +20 -0
- package/registry/_docs/scratch-card.json +20 -0
- package/registry/_docs/search-bar.json +23 -3
- package/registry/_docs/search-results.json +3 -0
- package/registry/_docs/segmented-control.json +20 -0
- package/registry/_docs/select.json +33 -8
- package/registry/_docs/settings.json +9 -0
- package/registry/_docs/sign-in.json +3 -0
- package/registry/_docs/skeleton.json +22 -2
- package/registry/_docs/slide-to-confirm.json +24 -0
- package/registry/_docs/slider.json +27 -7
- package/registry/_docs/spin-wheel.json +24 -0
- package/registry/_docs/spinner.json +41 -11
- package/registry/_docs/stateful-button.json +184 -0
- package/registry/_docs/stepper.json +23 -7
- package/registry/_docs/swipe-deck.json +16 -0
- package/registry/_docs/swipeable-row.json +24 -1
- package/registry/_docs/switch.json +20 -0
- package/registry/_docs/tabs.json +24 -0
- package/registry/_docs/text-animation.json +141 -0
- package/registry/_docs/textarea.json +18 -2
- package/registry/_docs/ticket-card.json +20 -0
- package/registry/_docs/ticket-shape.json +4 -1
- package/registry/_docs/tilt-card.json +20 -4
- package/registry/_docs/time-picker.json +22 -6
- package/registry/_docs/timeline-chart.json +3 -0
- package/registry/_docs/timeline.json +18 -2
- package/registry/_docs/toast.json +32 -8
- package/registry/_docs/treemap.json +3 -0
- package/registry/_docs/waterfall.json +3 -0
- package/registry/_docs/wheel-list.json +20 -0
- package/registry/accordion/files/src/components/ui/accordion.tsx +5 -4
- package/registry/accordion/files/src/lib/kit.tsx +8 -5
- package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +9 -8
- package/registry/action-sheet/files/src/lib/kit.tsx +8 -5
- package/registry/alert/files/src/components/ui/alert.tsx +7 -4
- package/registry/alert/files/src/components/ui/button.tsx +4 -3
- package/registry/alert/files/src/lib/kit.tsx +8 -5
- package/registry/assistant-app.json +49 -0
- package/registry/avatar/files/src/components/ui/avatar.tsx +7 -4
- package/registry/avatar/files/src/lib/kit.tsx +8 -5
- package/registry/badge/files/src/components/ui/badge.tsx +345 -21
- package/registry/badge/files/src/lib/kit.tsx +8 -5
- package/registry/badge.json +1 -1
- package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +14 -7
- package/registry/bottom-nav/files/src/lib/kit.tsx +8 -5
- package/registry/bottom-sheet/files/src/components/ui/bottom-sheet.tsx +7 -6
- package/registry/bottom-sheet/files/src/lib/kit.tsx +8 -5
- package/registry/button/files/src/components/ui/button.tsx +4 -3
- package/registry/button/files/src/lib/kit.tsx +8 -5
- package/registry/button-3d/files/src/components/ui/button-3d.tsx +3 -2
- package/registry/button-3d/files/src/lib/kit.tsx +8 -5
- package/registry/calendar/files/src/components/ui/calendar.tsx +9 -5
- package/registry/calendar/files/src/lib/kit.tsx +8 -5
- package/registry/card/files/src/components/ui/card.tsx +8 -6
- package/registry/card/files/src/lib/kit.tsx +8 -5
- package/registry/chat.json +18 -0
- package/registry/checkbox/files/src/components/ui/checkbox.tsx +6 -5
- package/registry/checkbox/files/src/lib/kit.tsx +8 -5
- package/registry/chip-group/files/src/components/ui/chip-group.tsx +4 -2
- package/registry/chip-group/files/src/lib/kit.tsx +8 -5
- package/registry/confetti/files/src/lib/kit.tsx +8 -5
- package/registry/coupon/files/src/components/ui/coupon.tsx +23 -20
- package/registry/coupon/files/src/components/ui/flip-card.tsx +1 -1
- package/registry/coupon/files/src/components/ui/tilt-card.tsx +2 -2
- package/registry/coupon/files/src/lib/kit.tsx +8 -5
- package/registry/date-picker/files/src/components/ui/calendar.tsx +9 -5
- package/registry/date-picker/files/src/components/ui/date-picker.tsx +2 -2
- package/registry/date-picker/files/src/lib/kit.tsx +8 -5
- package/registry/dialog/files/src/components/ui/dialog.tsx +12 -6
- package/registry/dialog/files/src/lib/kit.tsx +8 -5
- package/registry/donut-shop.json +18 -0
- package/registry/drawer/files/src/components/ui/drawer.tsx +25 -19
- package/registry/drawer/files/src/lib/kit.tsx +8 -5
- package/registry/fintech-app.json +57 -0
- package/registry/flip-card/files/src/components/ui/flip-card.tsx +1 -1
- package/registry/flip-card/files/src/lib/kit.tsx +8 -5
- package/registry/floating-input/files/src/components/ui/floating-input.tsx +16 -6
- package/registry/floating-input/files/src/lib/kit.tsx +8 -5
- package/registry/food-app.json +55 -0
- package/registry/grain/files/src/components/ui/grain.tsx +113 -0
- package/registry/{tag-input → grain}/files/src/lib/kit.tsx +8 -5
- package/registry/grain.json +17 -0
- package/registry/kit/files/src/lib/kit.tsx +8 -5
- package/registry/liquidglass-tab/files/src/components/GlassTabBar.tsx +1 -1
- package/registry/liquidglass-tab/files/src/lib/tab-bar.ts +1 -1
- package/registry/list-item/files/src/components/ui/list-item.tsx +20 -16
- package/registry/list-item/files/src/lib/kit.tsx +8 -5
- package/registry/morphing-button-group.json +18 -0
- package/registry/orb-reveal.json +20 -0
- package/registry/otp-input/files/src/components/ui/otp-input.tsx +8 -6
- package/registry/otp-input/files/src/lib/kit.tsx +8 -5
- package/registry/password-input/files/src/components/ui/floating-input.tsx +16 -6
- package/registry/password-input/files/src/components/ui/password-input.tsx +2 -2
- package/registry/password-input/files/src/lib/kit.tsx +8 -5
- package/registry/payment-card/files/src/components/ui/flip-card.tsx +1 -1
- package/registry/payment-card/files/src/components/ui/payment-card.tsx +27 -14
- package/registry/payment-card/files/src/components/ui/tilt-card.tsx +2 -2
- package/registry/payment-card/files/src/lib/kit.tsx +8 -5
- package/registry/progress/files/src/components/ui/progress.tsx +8 -5
- package/registry/progress/files/src/lib/kit.tsx +8 -5
- package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +15 -11
- package/registry/progress-steps/files/src/lib/kit.tsx +8 -5
- package/registry/qr-code-button.json +18 -0
- package/registry/radio-group/files/src/components/ui/radio-group.tsx +10 -9
- package/registry/radio-group/files/src/lib/kit.tsx +8 -5
- package/registry/search-bar/files/src/components/ui/search-bar.tsx +13 -11
- package/registry/search-bar/files/src/lib/kit.tsx +8 -5
- package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +4 -2
- package/registry/segmented-control/files/src/lib/kit.tsx +8 -5
- package/registry/select/files/src/components/ui/select.tsx +3 -3
- package/registry/select/files/src/lib/kit.tsx +8 -5
- package/registry/skeleton/files/src/components/ui/skeleton.tsx +4 -4
- package/registry/skeleton/files/src/lib/kit.tsx +8 -5
- package/registry/slider/files/src/components/ui/slider.tsx +5 -4
- package/registry/slider/files/src/lib/kit.tsx +8 -5
- package/registry/spinner/files/src/components/ui/spinner.tsx +452 -34
- package/registry/spinner/files/src/lib/kit.tsx +8 -5
- package/registry/spinner.json +1 -1
- package/registry/stateful-button/files/src/components/ui/stateful-button.tsx +532 -0
- package/registry/{pagination-dots → stateful-button}/files/src/lib/kit.tsx +8 -5
- package/registry/stateful-button.json +18 -0
- package/registry/stepper/files/src/components/ui/stepper.tsx +8 -4
- package/registry/stepper/files/src/lib/kit.tsx +8 -5
- package/registry/switch/files/src/components/ui/switch.tsx +4 -3
- package/registry/switch/files/src/lib/kit.tsx +8 -5
- package/registry/tabs/files/src/components/ui/tabs.tsx +10 -8
- package/registry/tabs/files/src/lib/kit.tsx +8 -5
- package/registry/text-animation/files/src/components/ui/text-animation.tsx +650 -0
- package/registry/text-animation/files/src/lib/kit.tsx +222 -0
- package/registry/text-animation.json +15 -0
- package/registry/textarea/files/src/components/ui/textarea.tsx +11 -9
- package/registry/textarea/files/src/lib/kit.tsx +8 -5
- package/registry/ticket-shape/files/src/lib/kit.tsx +8 -5
- package/registry/tilt-card/files/src/components/ui/tilt-card.tsx +2 -2
- package/registry/tilt-card/files/src/lib/kit.tsx +8 -5
- package/registry/time-picker/files/src/components/ui/time-picker.tsx +2 -2
- package/registry/time-picker/files/src/lib/kit.tsx +8 -5
- package/registry/timeline/files/src/components/ui/avatar.tsx +7 -4
- package/registry/timeline/files/src/components/ui/timeline.tsx +12 -10
- package/registry/timeline/files/src/lib/kit.tsx +8 -5
- package/registry/toast/files/src/components/ui/button.tsx +4 -3
- package/registry/toast/files/src/components/ui/toast.tsx +13 -11
- package/registry/toast/files/src/lib/kit.tsx +8 -5
- package/schema/components.schema.json +1 -1
- package/registry/_docs/color-swatch.json +0 -66
- package/registry/_docs/kpi-card.json +0 -11
- package/registry/_docs/like-button.json +0 -72
- package/registry/_docs/morph-button.json +0 -108
- package/registry/_docs/pagination-dots.json +0 -88
- package/registry/_docs/shimmer-button.json +0 -78
- package/registry/_docs/tag-input.json +0 -128
- package/registry/color-swatch.json +0 -17
- package/registry/kpi-card/files/src/components/charts/kpi-card.tsx +0 -45
- package/registry/kpi-card.json +0 -14
- package/registry/like-button.json +0 -18
- package/registry/morph-button.json +0 -18
- package/registry/pagination-dots/files/src/components/ui/pagination-dots.tsx +0 -262
- package/registry/pagination-dots.json +0 -15
- package/registry/shimmer-button.json +0 -17
- package/registry/tag-input/files/src/components/ui/tag-input.tsx +0 -720
- package/registry/tag-input.json +0 -17
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"files": [
|
|
7
7
|
"src/components/ui/toast.tsx"
|
|
8
8
|
],
|
|
9
|
+
"summary": "Sonner-style toasts whose tone icon draws itself while a hairline drains away.",
|
|
9
10
|
"props": [
|
|
10
11
|
[
|
|
11
12
|
"toast(title, options?)",
|
|
@@ -116,46 +117,69 @@
|
|
|
116
117
|
"Style for the <Toaster> host that fills the screen and positions the stack."
|
|
117
118
|
]
|
|
118
119
|
],
|
|
120
|
+
"behavior": [
|
|
121
|
+
[
|
|
122
|
+
"Touch",
|
|
123
|
+
"Tap the stack to fan it out, swipe a toast sideways or toward the edge to dismiss it; timers and the drain line pause while you touch."
|
|
124
|
+
],
|
|
125
|
+
[
|
|
126
|
+
"Motion",
|
|
127
|
+
"Tone icons draw their outline inside a halo; toast.promise() closes the spinning arc into the success or error mark. variant=\"island\" morphs like a Dynamic Island."
|
|
128
|
+
],
|
|
129
|
+
[
|
|
130
|
+
"Reduced motion",
|
|
131
|
+
"Tone icons appear fully drawn without the spin or pop, and the island skips its squash and staggered morph."
|
|
132
|
+
],
|
|
133
|
+
[
|
|
134
|
+
"Haptics",
|
|
135
|
+
"A success, warning or error notification when a toast of that type appears, landing as the loading circle closes; turn it off with haptics={false}."
|
|
136
|
+
],
|
|
137
|
+
[
|
|
138
|
+
"Screen readers",
|
|
139
|
+
"Each toast is an alert in a polite live region, labelled by its title and description, with action and Dismiss buttons."
|
|
140
|
+
]
|
|
141
|
+
],
|
|
142
|
+
"usage": "import { toast, Toaster } from '@/components/ui/toast';\n\n// once, at the root of your app\n<Toaster />\n\n// anywhere\ntoast.success('Invoice INV-0042 sent', { description: 'Maya will get it by email.' });",
|
|
119
143
|
"examples": [
|
|
120
144
|
{
|
|
121
145
|
"name": "Default",
|
|
122
146
|
"title": "Default",
|
|
123
|
-
"code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Frame({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n height: 300,\n
|
|
147
|
+
"code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\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/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. In screen mode it is the screen. */\nfunction Frame({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { padding: 16 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : {\n height: 300,\n overflow: 'hidden',\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst row = { flexDirection: 'row' as const, flexWrap: 'wrap' as const, gap: 8 };\n\nexport default function Default({ screen }: DemoProps) {\n const to = { toaster: 'demo-default' };\n return (\n <Frame screen={screen}>\n <View style={row}>\n <Button\n size=\"sm\"\n variant=\"accent\"\n onPress={() => toast('Event created', { ...to, description: 'Friday, June 14 at 9:00 AM' })}\n >\n Default\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onPress={() => toast.success('Changes saved', { ...to, description: 'Synced to all your devices.' })}\n >\n Success\n </Button>\n <Button size=\"sm\" variant=\"outline\" onPress={() => toast.warning('Storage almost full', { ...to, description: '92% of 15 GB used.' })}>\n Warning\n </Button>\n <Button size=\"sm\" variant=\"outline\" onPress={() => toast.error('Could not reach server', to)}>\n Error\n </Button>\n </View>\n <Toaster id=\"demo-default\" />\n </Frame>\n );\n}\n"
|
|
124
148
|
},
|
|
125
149
|
{
|
|
126
150
|
"name": "RichColors",
|
|
127
151
|
"title": "Rich Colors",
|
|
128
|
-
"code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Frame({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n height: 300,\n
|
|
152
|
+
"code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\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/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. In screen mode it is the screen. */\nfunction Frame({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { padding: 16 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : {\n height: 300,\n overflow: 'hidden',\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst row = { flexDirection: 'row' as const, flexWrap: 'wrap' as const, gap: 8 };\n\nexport default function RichColors({ screen }: DemoProps) {\n const { colors } = useKit();\n const to = { toaster: 'demo-rich' };\n return (\n <Frame screen={screen}>\n <View style={row}>\n <Button\n size=\"sm\"\n variant=\"soft\"\n color={colors.success}\n onPress={() => toast.success('Payment received', { ...to, description: '₹18,500 from Acme Inc.' })}\n >\n Success\n </Button>\n <Button\n size=\"sm\"\n variant=\"soft\"\n color={colors.danger}\n onPress={() => toast.error('Card declined', { ...to, description: 'Try another payment method.' })}\n >\n Error\n </Button>\n <Button size=\"sm\" variant=\"soft\" color={colors.warning} onPress={() => toast.warning('You are offline', { ...to, description: 'Changes will sync when you reconnect.' })}>\n Warning\n </Button>\n <Button size=\"sm\" variant=\"soft\" onPress={() => toast.info('New version available', { ...to, description: 'Restart to update to 4.2.' })}>\n Info\n </Button>\n </View>\n <Toaster id=\"demo-rich\" variant=\"rich\" />\n </Frame>\n );\n}\n"
|
|
129
153
|
},
|
|
130
154
|
{
|
|
131
155
|
"name": "Island",
|
|
132
156
|
"title": "Island",
|
|
133
|
-
"code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Frame({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n height: 300,\n
|
|
157
|
+
"code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\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/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. In screen mode it is the screen. */\nfunction Frame({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { padding: 16 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : {\n height: 300,\n overflow: 'hidden',\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst row = { flexDirection: 'row' as const, flexWrap: 'wrap' as const, gap: 8 };\n\nconst wait = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\n\nexport default function Island({ screen }: DemoProps) {\n const to = { toaster: 'demo-island' };\n return (\n <Frame screen={screen} style={{ justifyContent: 'flex-end' }}>\n <View style={row}>\n <Button\n size=\"sm\"\n variant=\"secondary\"\n onPress={() => toast.success('AirPods connected', { ...to, description: 'Battery 84% · Case 100%' })}\n >\n Success\n </Button>\n <Button\n size=\"sm\"\n variant=\"secondary\"\n onPress={() =>\n toast.error('Payment failed', {\n ...to,\n description: 'Your card ending 4242 was declined by the bank. Update your billing details to keep Pro.',\n action: { label: 'Update', onPress: () => toast.info('Opening billing…', to) },\n })\n }\n >\n Error\n </Button>\n <Button\n size=\"sm\"\n variant=\"secondary\"\n onPress={() =>\n toast.promise(wait(2200), { loading: 'Sending to Maya…', success: 'Photo sent', error: 'Could not send' }, to)\n }\n >\n Promise\n </Button>\n <Button\n size=\"sm\"\n variant=\"secondary\"\n onPress={() => toast('Maya Chen', { ...to, description: 'Running 5 min late, grab us a table?' })}\n >\n Message\n </Button>\n </View>\n <Toaster id=\"demo-island\" variant=\"island\" />\n </Frame>\n );\n}\n"
|
|
134
158
|
},
|
|
135
159
|
{
|
|
136
160
|
"name": "PromiseMorph",
|
|
137
161
|
"title": "Promise Morph",
|
|
138
|
-
"code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Frame({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n height: 300,\n
|
|
162
|
+
"code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\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/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. In screen mode it is the screen. */\nfunction Frame({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { padding: 16 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : {\n height: 300,\n overflow: 'hidden',\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst row = { flexDirection: 'row' as const, flexWrap: 'wrap' as const, gap: 8 };\n\nconst wait = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\n\nconst fail = (ms: number) => new Promise<void>((_, reject) => setTimeout(() => reject(new Error('timeout')), ms));\n\nexport default function PromiseMorph({ screen }: DemoProps) {\n const to = { toaster: 'demo-promise' };\n return (\n <Frame screen={screen}>\n <View style={row}>\n <Button\n size=\"sm\"\n variant=\"accent\"\n onPress={() =>\n toast.promise(\n wait(1800),\n { loading: 'Uploading report.pdf…', success: 'Report uploaded', error: 'Upload failed' },\n { ...to, description: 'Q3 board pack · 4.2 MB' },\n )\n }\n >\n Upload (succeeds)\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onPress={() =>\n toast\n .promise(\n fail(1800),\n { loading: 'Publishing release…', success: 'Release published', error: 'Publish failed: build timed out' },\n to,\n )\n .catch(() => {})\n }\n >\n Publish (fails)\n </Button>\n </View>\n <Toaster id=\"demo-promise\" />\n </Frame>\n );\n}\n"
|
|
139
163
|
},
|
|
140
164
|
{
|
|
141
165
|
"name": "Minimal",
|
|
142
166
|
"title": "Minimal",
|
|
143
|
-
"code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Frame({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n height: 300,\n
|
|
167
|
+
"code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\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/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. In screen mode it is the screen. */\nfunction Frame({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { padding: 16 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : {\n height: 300,\n overflow: 'hidden',\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst row = { flexDirection: 'row' as const, flexWrap: 'wrap' as const, gap: 8 };\n\nexport default function Minimal({ screen }: DemoProps) {\n const to = { toaster: 'demo-minimal' };\n return (\n <Frame screen={screen} style={{ justifyContent: 'flex-end' }}>\n <View style={row}>\n <Button size=\"sm\" variant=\"secondary\" onPress={() => toast('Copied to clipboard', to)}>\n Copy link\n </Button>\n <Button size=\"sm\" variant=\"secondary\" onPress={() => toast.success('Added to favourites', to)}>\n Favourite\n </Button>\n <Button size=\"sm\" variant=\"secondary\" onPress={() => toast.loading('Syncing…', { ...to, id: 'sync', duration: 1500 })}>\n Sync\n </Button>\n </View>\n <Toaster id=\"demo-minimal\" variant=\"minimal\" position=\"top\" />\n </Frame>\n );\n}\n"
|
|
144
168
|
},
|
|
145
169
|
{
|
|
146
170
|
"name": "WithAction",
|
|
147
171
|
"title": "With Action",
|
|
148
|
-
"code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Frame({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n height: 300,\n
|
|
172
|
+
"code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\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/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. In screen mode it is the screen. */\nfunction Frame({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { padding: 16 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : {\n height: 300,\n overflow: 'hidden',\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function WithAction({ screen }: DemoProps) {\n const to = { toaster: 'demo-action' };\n return (\n <Frame screen={screen}>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n style={{ alignSelf: 'flex-start' }}\n onPress={() =>\n toast('Conversation archived', {\n ...to,\n description: 'Moved 12 messages to Archive.',\n action: { label: 'Undo', onPress: () => toast.success('Conversation restored', to) },\n })\n }\n >\n Archive chat\n </Button>\n <Toaster id=\"demo-action\" closeButton duration={6000} />\n </Frame>\n );\n}\n"
|
|
149
173
|
},
|
|
150
174
|
{
|
|
151
175
|
"name": "Expanded",
|
|
152
176
|
"title": "Expanded",
|
|
153
|
-
"code": "import React, { useRef } from 'react';\nimport { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Frame({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n height: 300,\n
|
|
177
|
+
"code": "import React, { useRef } from 'react';\nimport { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\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/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. In screen mode it is the screen. */\nfunction Frame({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { padding: 16 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : {\n height: 300,\n overflow: 'hidden',\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Expanded({ screen }: DemoProps) {\n const to = { toaster: 'demo-expanded' };\n const messages = [\n () => toast.info('Maya joined #design', to),\n () => toast.success('Build #1824 passed', { ...to, description: 'main · 3m 12s' }),\n () => toast('Invoice INV-0042 sent', to),\n () => toast.warning('Disk usage at 85%', to),\n ];\n const n = useRef(0);\n return (\n <Frame screen={screen} style={{ justifyContent: 'flex-end' }}>\n <Button size=\"sm\" style={{ alignSelf: 'flex-start' }} onPress={() => messages[n.current++ % messages.length]()}>\n Send notification\n </Button>\n <Toaster id=\"demo-expanded\" position=\"top\" expand visibleToasts={3} gap={8} />\n </Frame>\n );\n}\n"
|
|
154
178
|
},
|
|
155
179
|
{
|
|
156
180
|
"name": "WithProvider",
|
|
157
181
|
"title": "With Provider",
|
|
158
|
-
"code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\nimport { useKit } from '@/lib/kit';\n\n/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Frame({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n {\n height: 300,\n
|
|
182
|
+
"code": "import { StyleSheet, View, type ViewProps } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { toast, Toaster, ToastProvider, useToast } from '@/components/ui/toast';\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/** Demo frame that follows the kit theme, so the docs radius / accent picker reshapes it too. In screen mode it is the screen. */\nfunction Frame({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { padding: 16 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : {\n height: 300,\n overflow: 'hidden',\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst row = { flexDirection: 'row' as const, flexWrap: 'wrap' as const, gap: 8 };\n\nfunction ProviderDemo({ screen }: { screen?: boolean }) {\n const toast = useToast();\n return (\n <Frame screen={screen}>\n <View style={row}>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onPress={() => toast.success('Draft saved', { description: 'Only this Toaster sees it.' })}\n >\n Save draft\n </Button>\n <Button size=\"sm\" variant=\"outline\" onPress={() => toast('Reminder set for 6:00 PM')}>\n Remind me\n </Button>\n </View>\n <Toaster />\n </Frame>\n );\n}\n\nexport default function WithProvider({ screen }: DemoProps) {\n return (\n <ToastProvider>\n <ProviderDemo screen={screen} />\n </ToastProvider>\n );\n}\n"
|
|
159
183
|
}
|
|
160
184
|
]
|
|
161
185
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"files": [
|
|
7
7
|
"src/components/ui/wheel-list.tsx"
|
|
8
8
|
],
|
|
9
|
+
"summary": "An iOS alarm-style picker whose rows curve away on a 3D cylinder.",
|
|
9
10
|
"props": [
|
|
10
11
|
[
|
|
11
12
|
"data",
|
|
@@ -98,5 +99,24 @@
|
|
|
98
99
|
"Style for the root (width)."
|
|
99
100
|
]
|
|
100
101
|
],
|
|
102
|
+
"behavior": [
|
|
103
|
+
[
|
|
104
|
+
"Touch",
|
|
105
|
+
"Flick to spin and it snaps to a row; tap a row to scroll it into the band."
|
|
106
|
+
],
|
|
107
|
+
[
|
|
108
|
+
"Motion",
|
|
109
|
+
"Each row rotates, scales and fades by its distance from the band, interpolated from the scroll position on the native driver."
|
|
110
|
+
],
|
|
111
|
+
[
|
|
112
|
+
"Screen readers",
|
|
113
|
+
"One adjustable element with increment / decrement actions; its value is the selected label, or \"3 / 12\" for custom rows."
|
|
114
|
+
],
|
|
115
|
+
[
|
|
116
|
+
"Web",
|
|
117
|
+
"Browsers lack snap momentum, so it settles on the nearest row shortly after scrolling stops."
|
|
118
|
+
]
|
|
119
|
+
],
|
|
120
|
+
"usage": null,
|
|
101
121
|
"examples": []
|
|
102
122
|
}
|
|
@@ -119,7 +119,8 @@ export function AccordionItem({ value, title, subtitle, icon, children, disabled
|
|
|
119
119
|
if (!ctx) throw new Error('AccordionItem must be used inside <Accordion>');
|
|
120
120
|
const { open: openValues, toggle, variant, tint } = ctx;
|
|
121
121
|
const { last } = useContext(PositionContext);
|
|
122
|
-
const { colors, radius, dark } = useKit();
|
|
122
|
+
const { colors, radius, dark, font } = useKit();
|
|
123
|
+
const ff = font.family ? { fontFamily: font.family } : null;
|
|
123
124
|
const open = openValues.includes(value);
|
|
124
125
|
|
|
125
126
|
const [height, setHeight] = useState(0);
|
|
@@ -168,11 +169,11 @@ export function AccordionItem({ value, title, subtitle, icon, children, disabled
|
|
|
168
169
|
{icon ? <View style={styles.icon}>{icon}</View> : null}
|
|
169
170
|
<View style={styles.titles}>
|
|
170
171
|
{isTextNode(title) ? (
|
|
171
|
-
<Text style={[styles.title, { color: open && variant === 'default' ? tint : colors.foreground }]}>{title}</Text>
|
|
172
|
+
<Text style={[styles.title, ff, { color: open && variant === 'default' ? tint : colors.foreground }]}>{title}</Text>
|
|
172
173
|
) : (
|
|
173
174
|
title
|
|
174
175
|
)}
|
|
175
|
-
{subtitle ? <Text style={[styles.subtitle, { color: colors.mutedForeground }]}>{subtitle}</Text> : null}
|
|
176
|
+
{subtitle ? <Text style={[styles.subtitle, ff, { color: colors.mutedForeground }]}>{subtitle}</Text> : null}
|
|
176
177
|
</View>
|
|
177
178
|
<Animated.View
|
|
178
179
|
style={{ transform: [{ rotate: turn.interpolate({ inputRange: [0, 1], outputRange: ['0deg', '180deg'] }) }] }}
|
|
@@ -209,7 +210,7 @@ export function AccordionItem({ value, title, subtitle, icon, children, disabled
|
|
|
209
210
|
]}
|
|
210
211
|
>
|
|
211
212
|
{isTextNode(children) ? (
|
|
212
|
-
<Text style={[styles.text, { color: colors.mutedForeground }]}>{children}</Text>
|
|
213
|
+
<Text style={[styles.text, ff, { color: colors.mutedForeground }]}>{children}</Text>
|
|
213
214
|
) : (
|
|
214
215
|
children
|
|
215
216
|
)}
|
|
@@ -181,14 +181,17 @@ export function useKit(): Theme {
|
|
|
181
181
|
return useMemo(() => build(scheme, ctx?.overrides), [scheme, ctx?.overrides]);
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* Shared motion presets so the whole kit moves the same way. Every spring is critically damped
|
|
186
|
+
* (damping ≥ 2·√(stiffness·mass)): it settles smoothly with no overshoot or wobble.
|
|
187
|
+
*/
|
|
185
188
|
export const motion = {
|
|
186
189
|
/** Toggles, selection indicators. */
|
|
187
|
-
snappy: { damping:
|
|
188
|
-
/** Press feedback, pops. */
|
|
189
|
-
bouncy: { damping:
|
|
190
|
+
snappy: { damping: 30, stiffness: 260, mass: 0.8 },
|
|
191
|
+
/** Press feedback, pops. (Kept under its old name; it no longer bounces.) */
|
|
192
|
+
bouncy: { damping: 26, stiffness: 220, mass: 0.7 },
|
|
190
193
|
/** Sheets, layout changes. */
|
|
191
|
-
gentle: { damping:
|
|
194
|
+
gentle: { damping: 27, stiffness: 160, mass: 1 },
|
|
192
195
|
fast: 160,
|
|
193
196
|
normal: 240,
|
|
194
197
|
};
|
|
@@ -134,7 +134,8 @@ export function ActionSheet({
|
|
|
134
134
|
haptics = true,
|
|
135
135
|
style,
|
|
136
136
|
}: ActionSheetProps) {
|
|
137
|
-
const { colors, radius, dark } = useKit();
|
|
137
|
+
const { colors, radius, dark, font } = useKit();
|
|
138
|
+
const ff = font.family ? { fontFamily: font.family } : null;
|
|
138
139
|
const { height: winH } = useWindowDimensions();
|
|
139
140
|
const reduceMotion = useReduceMotion();
|
|
140
141
|
const tint = accent ?? colors.accent;
|
|
@@ -247,13 +248,13 @@ export function ActionSheet({
|
|
|
247
248
|
{title ? (
|
|
248
249
|
<Text
|
|
249
250
|
accessibilityRole="header"
|
|
250
|
-
style={[list ? styles.titleLarge : styles.title, { color: list ? colors.foreground : colors.mutedForeground }]}
|
|
251
|
+
style={[list ? styles.titleLarge : styles.title, ff, { color: list ? colors.foreground : colors.mutedForeground }]}
|
|
251
252
|
>
|
|
252
253
|
{title}
|
|
253
254
|
</Text>
|
|
254
255
|
) : null}
|
|
255
256
|
{message ? (
|
|
256
|
-
<Text style={[styles.message, { color: colors.mutedForeground, textAlign: list ? 'left' : 'center' }]}>
|
|
257
|
+
<Text style={[styles.message, ff, { color: colors.mutedForeground, textAlign: list ? 'left' : 'center' }]}>
|
|
257
258
|
{message}
|
|
258
259
|
</Text>
|
|
259
260
|
) : null}
|
|
@@ -265,7 +266,7 @@ export function ActionSheet({
|
|
|
265
266
|
cancelLabel !== null ? (
|
|
266
267
|
<Card corner={corner} bg={colors.popover} edge={edge} dark={dark}>
|
|
267
268
|
<Row onPress={dismiss} highlight={highlight} label={cancelLabel} style={styles.row}>
|
|
268
|
-
<Text style={[styles.rowLabel, styles.center, { color: variant === 'inset' ? tint : colors.foreground }]}>
|
|
269
|
+
<Text style={[styles.rowLabel, ff, styles.center, { color: variant === 'inset' ? tint : colors.foreground }]}>
|
|
269
270
|
{cancelLabel}
|
|
270
271
|
</Text>
|
|
271
272
|
</Row>
|
|
@@ -295,7 +296,7 @@ export function ActionSheet({
|
|
|
295
296
|
<View style={[styles.tileIcon, { backgroundColor: fill, borderRadius: radius.scale(16) }]}>
|
|
296
297
|
{icon(a, fg, 24)}
|
|
297
298
|
</View>
|
|
298
|
-
<Text numberOfLines={2} style={[styles.tileLabel, { color: a.destructive ? colors.danger : colors.foreground }]}>
|
|
299
|
+
<Text numberOfLines={2} style={[styles.tileLabel, ff, { color: a.destructive ? colors.danger : colors.foreground }]}>
|
|
299
300
|
{a.label}
|
|
300
301
|
</Text>
|
|
301
302
|
</Row>
|
|
@@ -337,7 +338,7 @@ export function ActionSheet({
|
|
|
337
338
|
{a.icon ? (
|
|
338
339
|
<View style={styles.listIcon}>{icon(a, a.destructive ? colors.danger : colors.mutedForeground, 22)}</View>
|
|
339
340
|
) : null}
|
|
340
|
-
<Text style={[styles.listLabel, { color }]} numberOfLines={1}>
|
|
341
|
+
<Text style={[styles.listLabel, ff, { color }]} numberOfLines={1}>
|
|
341
342
|
{a.label}
|
|
342
343
|
</Text>
|
|
343
344
|
</Row>
|
|
@@ -347,7 +348,7 @@ export function ActionSheet({
|
|
|
347
348
|
<>
|
|
348
349
|
<View style={[styles.divider, { backgroundColor: separator }]} />
|
|
349
350
|
<Row onPress={dismiss} highlight={highlight} corner={radius.md} label={cancelLabel} style={styles.listRow}>
|
|
350
|
-
<Text style={[styles.listLabel, styles.center, { color: colors.mutedForeground }]}>{cancelLabel}</Text>
|
|
351
|
+
<Text style={[styles.listLabel, ff, styles.center, { color: colors.mutedForeground }]}>{cancelLabel}</Text>
|
|
351
352
|
</Row>
|
|
352
353
|
</>
|
|
353
354
|
) : null}
|
|
@@ -379,7 +380,7 @@ export function ActionSheet({
|
|
|
379
380
|
(i > 0 || header) && { borderTopWidth: StyleSheet.hairlineWidth, borderTopColor: separator },
|
|
380
381
|
]}
|
|
381
382
|
>
|
|
382
|
-
<Text style={[styles.rowLabel, withIcons ? styles.rowLabelMenu : styles.center, { color }]} numberOfLines={1}>
|
|
383
|
+
<Text style={[styles.rowLabel, ff, withIcons ? styles.rowLabelMenu : styles.center, { color }]} numberOfLines={1}>
|
|
383
384
|
{a.label}
|
|
384
385
|
</Text>
|
|
385
386
|
{withIcons ? <View style={styles.rowIcon}>{a.icon ? icon(a, color, 22) : null}</View> : null}
|
|
@@ -181,14 +181,17 @@ export function useKit(): Theme {
|
|
|
181
181
|
return useMemo(() => build(scheme, ctx?.overrides), [scheme, ctx?.overrides]);
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* Shared motion presets so the whole kit moves the same way. Every spring is critically damped
|
|
186
|
+
* (damping ≥ 2·√(stiffness·mass)): it settles smoothly with no overshoot or wobble.
|
|
187
|
+
*/
|
|
185
188
|
export const motion = {
|
|
186
189
|
/** Toggles, selection indicators. */
|
|
187
|
-
snappy: { damping:
|
|
188
|
-
/** Press feedback, pops. */
|
|
189
|
-
bouncy: { damping:
|
|
190
|
+
snappy: { damping: 30, stiffness: 260, mass: 0.8 },
|
|
191
|
+
/** Press feedback, pops. (Kept under its old name; it no longer bounces.) */
|
|
192
|
+
bouncy: { damping: 26, stiffness: 220, mass: 0.7 },
|
|
190
193
|
/** Sheets, layout changes. */
|
|
191
|
-
gentle: { damping:
|
|
194
|
+
gentle: { damping: 27, stiffness: 160, mass: 1 },
|
|
192
195
|
fast: 160,
|
|
193
196
|
normal: 240,
|
|
194
197
|
};
|
|
@@ -89,7 +89,8 @@ export function Alert({
|
|
|
89
89
|
color,
|
|
90
90
|
style,
|
|
91
91
|
}: AlertProps) {
|
|
92
|
-
const { colors, radius, dark } = useKit();
|
|
92
|
+
const { colors, radius, dark, font } = useKit();
|
|
93
|
+
const ff = font.family ? { fontFamily: font.family } : null;
|
|
93
94
|
const uid = useId().replace(/[^a-zA-Z0-9]/g, '');
|
|
94
95
|
const reduceMotion = useReduceMotion();
|
|
95
96
|
const tones: Record<AlertVariant, string> = {
|
|
@@ -322,9 +323,9 @@ export function Alert({
|
|
|
322
323
|
<>
|
|
323
324
|
{iconNode}
|
|
324
325
|
<View style={[styles.body, { paddingTop: textTop }]}>
|
|
325
|
-
{title ? <Text style={[styles.title, { color: fg }]}>{title}</Text> : null}
|
|
326
|
+
{title ? <Text style={[styles.title, ff, { color: fg }]}>{title}</Text> : null}
|
|
326
327
|
{typeof description === 'string' ? (
|
|
327
|
-
<Text style={[styles.desc, { color: title ? sub : fg }]}>{description}</Text>
|
|
328
|
+
<Text style={[styles.desc, ff, { color: title ? sub : fg }]}>{description}</Text>
|
|
328
329
|
) : (
|
|
329
330
|
description
|
|
330
331
|
)}
|
|
@@ -443,6 +444,8 @@ function Pill({
|
|
|
443
444
|
bg: string;
|
|
444
445
|
fg: string;
|
|
445
446
|
}) {
|
|
447
|
+
const { font } = useKit();
|
|
448
|
+
const ff = font.family ? { fontFamily: font.family } : null;
|
|
446
449
|
return (
|
|
447
450
|
<Pressable
|
|
448
451
|
accessibilityRole="button"
|
|
@@ -455,7 +458,7 @@ function Pill({
|
|
|
455
458
|
pressed ? { opacity: 0.75, transform: [{ scale: 0.97 }] } : null,
|
|
456
459
|
]}
|
|
457
460
|
>
|
|
458
|
-
<Text style={[styles.pillText, { color: fg }]} numberOfLines={1}>
|
|
461
|
+
<Text style={[styles.pillText, ff, { color: fg }]} numberOfLines={1}>
|
|
459
462
|
{action.label}
|
|
460
463
|
</Text>
|
|
461
464
|
</Pressable>
|
|
@@ -116,7 +116,8 @@ export function Button({
|
|
|
116
116
|
onPressOut,
|
|
117
117
|
...props
|
|
118
118
|
}: ButtonProps) {
|
|
119
|
-
const { colors, radius, dark } = useKit();
|
|
119
|
+
const { colors, radius, dark, font } = useKit();
|
|
120
|
+
const ff = font.family ? { fontFamily: font.family } : null;
|
|
120
121
|
const gid = 'btn' + useId().replace(/[^a-zA-Z0-9]/g, '');
|
|
121
122
|
const scale = useRef(new Animated.Value(1)).current;
|
|
122
123
|
const isDisabled = disabled || loading;
|
|
@@ -149,7 +150,7 @@ export function Button({
|
|
|
149
150
|
const label = isTextNode(children) ? (
|
|
150
151
|
<Text
|
|
151
152
|
numberOfLines={1}
|
|
152
|
-
style={[styles.label, { color: fg, fontSize: s.font }, variant === 'link' && styles.link, textStyle]}
|
|
153
|
+
style={[styles.label, ff, { color: fg, fontSize: s.font }, variant === 'link' && styles.link, textStyle]}
|
|
153
154
|
>
|
|
154
155
|
{children}
|
|
155
156
|
</Text>
|
|
@@ -235,7 +236,7 @@ export function Button({
|
|
|
235
236
|
<View style={styles.row}>
|
|
236
237
|
<ActivityIndicator size="small" color={fg} />
|
|
237
238
|
{size !== 'icon' && isTextNode(children) ? (
|
|
238
|
-
<Text style={[styles.label, { color: fg, fontSize: s.font, opacity: 0.85 }]}>{children}</Text>
|
|
239
|
+
<Text style={[styles.label, ff, { color: fg, fontSize: s.font, opacity: 0.85 }]}>{children}</Text>
|
|
239
240
|
) : null}
|
|
240
241
|
</View>
|
|
241
242
|
) : (
|
|
@@ -181,14 +181,17 @@ export function useKit(): Theme {
|
|
|
181
181
|
return useMemo(() => build(scheme, ctx?.overrides), [scheme, ctx?.overrides]);
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* Shared motion presets so the whole kit moves the same way. Every spring is critically damped
|
|
186
|
+
* (damping ≥ 2·√(stiffness·mass)): it settles smoothly with no overshoot or wobble.
|
|
187
|
+
*/
|
|
185
188
|
export const motion = {
|
|
186
189
|
/** Toggles, selection indicators. */
|
|
187
|
-
snappy: { damping:
|
|
188
|
-
/** Press feedback, pops. */
|
|
189
|
-
bouncy: { damping:
|
|
190
|
+
snappy: { damping: 30, stiffness: 260, mass: 0.8 },
|
|
191
|
+
/** Press feedback, pops. (Kept under its old name; it no longer bounces.) */
|
|
192
|
+
bouncy: { damping: 26, stiffness: 220, mass: 0.7 },
|
|
190
193
|
/** Sheets, layout changes. */
|
|
191
|
-
gentle: { damping:
|
|
194
|
+
gentle: { damping: 27, stiffness: 160, mass: 1 },
|
|
192
195
|
fast: 160,
|
|
193
196
|
normal: 240,
|
|
194
197
|
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "assistant-app",
|
|
3
|
+
"type": "template",
|
|
4
|
+
"title": "AI assistant app",
|
|
5
|
+
"description": "A complete Expo Router app for an AI chat assistant: onboarding once, Apple / Google / email-code sign in, a streaming chat with suggestions and stop, conversation history, profile and settings.",
|
|
6
|
+
"tier": "pro",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"react-native-svg",
|
|
9
|
+
"expo-haptics",
|
|
10
|
+
"expo-router",
|
|
11
|
+
"expo-splash-screen",
|
|
12
|
+
"expo-status-bar",
|
|
13
|
+
"react-native-safe-area-context",
|
|
14
|
+
"react-native-screens",
|
|
15
|
+
"@react-native-async-storage/async-storage"
|
|
16
|
+
],
|
|
17
|
+
"registryDependencies": [
|
|
18
|
+
"kit",
|
|
19
|
+
"bottom-nav",
|
|
20
|
+
"onboarding",
|
|
21
|
+
"sign-in",
|
|
22
|
+
"chat",
|
|
23
|
+
"inbox",
|
|
24
|
+
"profile",
|
|
25
|
+
"settings"
|
|
26
|
+
],
|
|
27
|
+
"sharedFiles": [
|
|
28
|
+
"lib/kit.tsx",
|
|
29
|
+
"components/ui/bottom-nav.tsx",
|
|
30
|
+
"components/blocks/onboarding.tsx",
|
|
31
|
+
"components/ui/button.tsx",
|
|
32
|
+
"components/ui/floating-input.tsx",
|
|
33
|
+
"components/ui/password-input.tsx",
|
|
34
|
+
"components/ui/otp-input.tsx",
|
|
35
|
+
"components/blocks/sign-in.tsx",
|
|
36
|
+
"components/blocks/chat.tsx",
|
|
37
|
+
"components/ui/avatar.tsx",
|
|
38
|
+
"components/ui/badge.tsx",
|
|
39
|
+
"components/ui/chip-group.tsx",
|
|
40
|
+
"components/ui/search-bar.tsx",
|
|
41
|
+
"components/ui/swipeable-row.tsx",
|
|
42
|
+
"components/blocks/inbox.tsx",
|
|
43
|
+
"components/blocks/profile.tsx",
|
|
44
|
+
"components/ui/segmented-control.tsx",
|
|
45
|
+
"components/ui/switch.tsx",
|
|
46
|
+
"components/blocks/settings.tsx"
|
|
47
|
+
],
|
|
48
|
+
"filesDir": null
|
|
49
|
+
}
|
|
@@ -131,7 +131,8 @@ export function Avatar({
|
|
|
131
131
|
haptics = true,
|
|
132
132
|
style,
|
|
133
133
|
}: AvatarProps) {
|
|
134
|
-
const { colors, radius, dark } = useKit();
|
|
134
|
+
const { colors, radius, dark, font } = useKit();
|
|
135
|
+
const ff = font.family ? { fontFamily: font.family } : null;
|
|
135
136
|
const reduceMotion = useReduceMotion();
|
|
136
137
|
const uid = useId().replace(/[^a-zA-Z0-9]/g, '');
|
|
137
138
|
const px = resolveSize(size);
|
|
@@ -280,6 +281,7 @@ export function Avatar({
|
|
|
280
281
|
numberOfLines={1}
|
|
281
282
|
style={[
|
|
282
283
|
styles.initials,
|
|
284
|
+
ff,
|
|
283
285
|
{
|
|
284
286
|
color: ink,
|
|
285
287
|
fontSize: Math.max(9, px * (initials.length > 1 ? 0.37 : 0.42)),
|
|
@@ -448,7 +450,7 @@ export function Avatar({
|
|
|
448
450
|
{badgeText ? (
|
|
449
451
|
<Text
|
|
450
452
|
allowFontScaling={false}
|
|
451
|
-
style={[styles.badgeText, { color: colors.dangerForeground, fontSize: Math.max(9, badgeH * 0.6) }]}
|
|
453
|
+
style={[styles.badgeText, ff, { color: colors.dangerForeground, fontSize: Math.max(9, badgeH * 0.6) }]}
|
|
452
454
|
>
|
|
453
455
|
{badgeText}
|
|
454
456
|
</Text>
|
|
@@ -623,7 +625,8 @@ export function AvatarGroup({
|
|
|
623
625
|
onPressMore,
|
|
624
626
|
style,
|
|
625
627
|
}: AvatarGroupProps) {
|
|
626
|
-
const { colors, radius, dark } = useKit();
|
|
628
|
+
const { colors, radius, dark, font } = useKit();
|
|
629
|
+
const ff = font.family ? { fontFamily: font.family } : null;
|
|
627
630
|
const reduceMotion = useReduceMotion();
|
|
628
631
|
const px = resolveSize(size);
|
|
629
632
|
const bg = surface ?? colors.background;
|
|
@@ -699,7 +702,7 @@ export function AvatarGroup({
|
|
|
699
702
|
<Text
|
|
700
703
|
allowFontScaling={false}
|
|
701
704
|
numberOfLines={1}
|
|
702
|
-
style={[styles.moreText, { color: colors.foreground, fontSize: Math.max(8, px * (extraText.length <= 2 ? 0.34 : extraText.length === 3 ? 0.29 : 0.24)) }]}
|
|
705
|
+
style={[styles.moreText, ff, { color: colors.foreground, fontSize: Math.max(8, px * (extraText.length <= 2 ? 0.34 : extraText.length === 3 ? 0.29 : 0.24)) }]}
|
|
703
706
|
>
|
|
704
707
|
+{extraText}
|
|
705
708
|
</Text>
|
|
@@ -181,14 +181,17 @@ export function useKit(): Theme {
|
|
|
181
181
|
return useMemo(() => build(scheme, ctx?.overrides), [scheme, ctx?.overrides]);
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* Shared motion presets so the whole kit moves the same way. Every spring is critically damped
|
|
186
|
+
* (damping ≥ 2·√(stiffness·mass)): it settles smoothly with no overshoot or wobble.
|
|
187
|
+
*/
|
|
185
188
|
export const motion = {
|
|
186
189
|
/** Toggles, selection indicators. */
|
|
187
|
-
snappy: { damping:
|
|
188
|
-
/** Press feedback, pops. */
|
|
189
|
-
bouncy: { damping:
|
|
190
|
+
snappy: { damping: 30, stiffness: 260, mass: 0.8 },
|
|
191
|
+
/** Press feedback, pops. (Kept under its old name; it no longer bounces.) */
|
|
192
|
+
bouncy: { damping: 26, stiffness: 220, mass: 0.7 },
|
|
190
193
|
/** Sheets, layout changes. */
|
|
191
|
-
gentle: { damping:
|
|
194
|
+
gentle: { damping: 27, stiffness: 160, mass: 1 },
|
|
192
195
|
fast: 160,
|
|
193
196
|
normal: 240,
|
|
194
197
|
};
|