@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
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spinner",
|
|
3
3
|
"category": "Feedback",
|
|
4
|
-
"aliases": [
|
|
4
|
+
"aliases": [
|
|
5
|
+
"loader",
|
|
6
|
+
"loading",
|
|
7
|
+
"activity-indicator",
|
|
8
|
+
"progress-indicator"
|
|
9
|
+
],
|
|
5
10
|
"exportName": "Spinner",
|
|
6
11
|
"files": [
|
|
7
12
|
"src/components/ui/spinner.tsx"
|
|
8
13
|
],
|
|
14
|
+
"summary": "Twelve loaders — ring, dots, bars, pulse, orbit, typing, line and more — from one API.",
|
|
9
15
|
"props": [
|
|
10
16
|
[
|
|
11
17
|
"variant",
|
|
12
|
-
"\"ring\" | \"dots\"",
|
|
18
|
+
"\"ring\" | \"dots\" | \"bars\" | \"pulse\" | \"orbit\" | \"bounce\" | \"wave\" | \"grid\" | \"typing\" | \"arc\" | \"square\" | \"line\"",
|
|
13
19
|
"\"ring\"",
|
|
14
|
-
"Indicator style."
|
|
20
|
+
"Indicator style — 12 loaders. \"line\" is an indeterminate bar size × 4 wide (override with style.width)."
|
|
15
21
|
],
|
|
16
22
|
[
|
|
17
23
|
"size",
|
|
18
24
|
"\"sm\" | \"md\" | \"lg\" | \"xl\" | number",
|
|
19
25
|
"\"md\"",
|
|
20
|
-
"16 / 24 / 36 / 52pt, or any number."
|
|
26
|
+
"16 / 24 / 36 / 52pt, or any number. Every variant scales from it."
|
|
21
27
|
],
|
|
22
28
|
[
|
|
23
29
|
"color",
|
|
@@ -47,7 +53,7 @@
|
|
|
47
53
|
"track",
|
|
48
54
|
"boolean",
|
|
49
55
|
"false",
|
|
50
|
-
"Faint full circle
|
|
56
|
+
"Faint guide behind the motion: full circle (ring, orbit, arc), rail (line) or ghost bars (bars)."
|
|
51
57
|
],
|
|
52
58
|
[
|
|
53
59
|
"style",
|
|
@@ -56,36 +62,60 @@
|
|
|
56
62
|
"Style for the root view (the square, or the spinner + label row/column)."
|
|
57
63
|
]
|
|
58
64
|
],
|
|
65
|
+
"behavior": [
|
|
66
|
+
[
|
|
67
|
+
"Motion",
|
|
68
|
+
"Every variant loops on the native driver (rotation, scale, translate, opacity); the ring's arc-length breathing is the only SVG prop animation and runs on its own JS-driven node."
|
|
69
|
+
],
|
|
70
|
+
[
|
|
71
|
+
"Reduced motion",
|
|
72
|
+
"Loops stop and each loader holds a still, clearly-busy frame (staggered dots, a lit diagonal, a mid-line segment); the ring keeps a plain steady spin."
|
|
73
|
+
],
|
|
74
|
+
[
|
|
75
|
+
"Screen readers",
|
|
76
|
+
"Announced as a progress bar with state busy and your label (default \"Loading\"); the visual is one accessible element."
|
|
77
|
+
],
|
|
78
|
+
[
|
|
79
|
+
"Platforms",
|
|
80
|
+
"Pure React Native Animated plus react-native-svg — iOS, Android, Expo Go and web, no native setup."
|
|
81
|
+
]
|
|
82
|
+
],
|
|
83
|
+
"usage": "import { Spinner } from '@/components/ui/spinner';\n\nexport function Saving() {\n return <Spinner variant=\"bars\" size=\"lg\" label=\"Saving changes\" labelPosition=\"right\" />;\n}",
|
|
59
84
|
"examples": [
|
|
60
85
|
{
|
|
61
86
|
"name": "Variants",
|
|
62
87
|
"title": "Variants",
|
|
63
|
-
"code": "import { Pressable, Text, View } from 'react-native';\nimport { Spinner } from '@/components/ui/spinner';\n\nexport default function Variants() {\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 40 }}>\n <Spinner variant=\"ring\" size=\"lg\" track />\n <Spinner variant=\"ring\" size=\"lg\" />\n <Spinner variant=\"dots\" size=\"lg\" />\n </View>\n );\n}\n"
|
|
88
|
+
"code": "import { Pressable, Text, View } from 'react-native';\nimport { Spinner, type SpinnerVariant } from '@/components/ui/spinner';\n\nexport default function Variants() {\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 40 }}>\n <Spinner variant=\"ring\" size=\"lg\" track />\n <Spinner variant=\"ring\" size=\"lg\" />\n <Spinner variant=\"dots\" size=\"lg\" />\n </View>\n );\n}\n"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"name": "LoaderGallery",
|
|
92
|
+
"title": "Loader Gallery",
|
|
93
|
+
"code": "import { Pressable, Text, View } from 'react-native';\nimport { Spinner, type SpinnerVariant } from '@/components/ui/spinner';\nimport { readableOn, useKit } from '@/lib/kit';\n\nconst LOADERS: SpinnerVariant[] = ['ring', 'dots', 'bars', 'pulse', 'orbit', 'bounce', 'wave', 'grid', 'typing', 'arc', 'square', 'line'];\n\nexport default function LoaderGallery() {\n const { colors, radius } = useKit();\n return (\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', rowGap: 12 }}>\n {LOADERS.map((variant) => (\n <View key={variant} style={{ width: '33.33%', alignItems: 'center', gap: 8, paddingHorizontal: 4 }}>\n <View\n style={{\n alignSelf: 'stretch',\n height: 64,\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: radius.md,\n backgroundColor: colors.muted,\n }}\n >\n <Spinner\n variant={variant}\n size={variant === 'line' ? 'sm' : 'lg'}\n track={variant === 'line'}\n label={`${variant} loader`}\n style={variant === 'line' ? { width: '72%' } : undefined}\n />\n </View>\n <Text style={{ fontSize: 12, color: colors.mutedForeground }}>{variant}</Text>\n </View>\n ))}\n </View>\n );\n}\n"
|
|
64
94
|
},
|
|
65
95
|
{
|
|
66
96
|
"name": "InButton",
|
|
67
97
|
"title": "In Button",
|
|
68
|
-
"code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { Spinner } from '@/components/ui/spinner';\nimport { readableOn, useKit } from '@/lib/kit';\n\nexport default function InButton() {\n const { colors, radius } = useKit();\n const [busy, setBusy] = useState(false);\n const fg = readableOn(colors.accent);\n return (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityState={{ busy }}\n onPress={() => {\n setBusy(true);\n setTimeout(() => setBusy(false), 2200);\n }}\n style={{\n height: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: colors.accent,\n alignItems: 'center',\n justifyContent: 'center',\n alignSelf: 'stretch',\n }}\n >\n {busy ? (\n <Spinner size=\"sm\" color={fg} label=\"Placing order\" labelPosition=\"right\" />\n ) : (\n <Text style={{ color: fg, fontSize: 16, fontWeight: '400' }}>Place order · ₹749</Text>\n )}\n </Pressable>\n );\n}\n"
|
|
98
|
+
"code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { Spinner, type SpinnerVariant } from '@/components/ui/spinner';\nimport { readableOn, useKit } from '@/lib/kit';\n\nexport default function InButton() {\n const { colors, radius } = useKit();\n const [busy, setBusy] = useState(false);\n const fg = readableOn(colors.accent);\n return (\n <Pressable\n accessibilityRole=\"button\"\n accessibilityState={{ busy }}\n onPress={() => {\n setBusy(true);\n setTimeout(() => setBusy(false), 2200);\n }}\n style={{\n height: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: colors.accent,\n alignItems: 'center',\n justifyContent: 'center',\n alignSelf: 'stretch',\n }}\n >\n {busy ? (\n <Spinner size=\"sm\" color={fg} label=\"Placing order\" labelPosition=\"right\" />\n ) : (\n <Text style={{ color: fg, fontSize: 16, fontWeight: '400' }}>Place order · ₹749</Text>\n )}\n </Pressable>\n );\n}\n"
|
|
69
99
|
},
|
|
70
100
|
{
|
|
71
101
|
"name": "LoadingCard",
|
|
72
102
|
"title": "Loading Card",
|
|
73
|
-
"code": "import { Pressable, Text, View } from 'react-native';\nimport { Spinner } from '@/components/ui/spinner';\nimport { readableOn, useKit } from '@/lib/kit';\n\nexport default function LoadingCard() {\n const { colors, radius } = useKit();\n return (\n <View\n style={
|
|
103
|
+
"code": "import { Pressable, Text, View } from 'react-native';\nimport { Spinner, type SpinnerVariant } from '@/components/ui/spinner';\nimport { readableOn, 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 LoadingCard({ screen }: DemoProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { alignSelf: 'stretch', alignItems: 'center', gap: 6, paddingVertical: 32, paddingHorizontal: 20 },\n screen\n ? { flex: 1, justifyContent: 'center', backgroundColor: colors.background }\n : { borderRadius: radius.lg, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },\n ]}\n >\n <Spinner size=\"xl\" track label=\"Preparing your itinerary\" labelPosition=\"bottom\" />\n <Text style={{ fontSize: 13, color: colors.mutedForeground, textAlign: 'center' }}>\n Checking 3 flights and 2 hotels\n </Text>\n </View>\n );\n}\n"
|
|
74
104
|
},
|
|
75
105
|
{
|
|
76
106
|
"name": "InlineStatus",
|
|
77
107
|
"title": "Inline Status",
|
|
78
|
-
"code": "import { Pressable, Text, View } from 'react-native';\nimport { Spinner } from '@/components/ui/spinner';\n\nexport default function InlineStatus() {\n return (\n <View style={{ gap: 16 }}>\n <Spinner size=\"sm\" label=\"Syncing 128 photos\" labelPosition=\"right\" />\n <Spinner variant=\"dots\" size=\"md\" label=\"Maya is typing\" labelPosition=\"right\" />\n </View>\n );\n}\n"
|
|
108
|
+
"code": "import { Pressable, Text, View } from 'react-native';\nimport { Spinner, type SpinnerVariant } from '@/components/ui/spinner';\n\nexport default function InlineStatus() {\n return (\n <View style={{ gap: 16 }}>\n <Spinner size=\"sm\" label=\"Syncing 128 photos\" labelPosition=\"right\" />\n <Spinner variant=\"dots\" size=\"md\" label=\"Maya is typing\" labelPosition=\"right\" />\n </View>\n );\n}\n"
|
|
79
109
|
},
|
|
80
110
|
{
|
|
81
111
|
"name": "Colors",
|
|
82
112
|
"title": "Colors",
|
|
83
|
-
"code": "import { Pressable, Text, View } from 'react-native';\nimport { Spinner } from '@/components/ui/spinner';\nimport { readableOn, useKit } from '@/lib/kit';\n\nexport default function Colors() {\n const { colors } = useKit();\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-around', gap: 20 }}>\n <Spinner size=\"lg\" color={colors.success} track />\n <Spinner variant=\"dots\" size=\"lg\" color={colors.warning} />\n <Spinner size=\"lg\" color={colors.danger} />\n </View>\n );\n}\n"
|
|
113
|
+
"code": "import { Pressable, Text, View } from 'react-native';\nimport { Spinner, type SpinnerVariant } from '@/components/ui/spinner';\nimport { readableOn, useKit } from '@/lib/kit';\n\nexport default function Colors() {\n const { colors } = useKit();\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-around', gap: 20 }}>\n <Spinner size=\"lg\" color={colors.success} track />\n <Spinner variant=\"dots\" size=\"lg\" color={colors.warning} />\n <Spinner size=\"lg\" color={colors.danger} />\n </View>\n );\n}\n"
|
|
84
114
|
},
|
|
85
115
|
{
|
|
86
116
|
"name": "Sizes",
|
|
87
117
|
"title": "Sizes",
|
|
88
|
-
"code": "import { Pressable, Text, View } from 'react-native';\nimport { Spinner } from '@/components/ui/spinner';\n\nexport default function Sizes() {\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 24 }}>\n <Spinner size=\"sm\" />\n <Spinner size=\"md\" />\n <Spinner size=\"lg\" />\n <Spinner size=\"xl\" />\n </View>\n );\n}\n"
|
|
118
|
+
"code": "import { Pressable, Text, View } from 'react-native';\nimport { Spinner, type SpinnerVariant } from '@/components/ui/spinner';\n\nexport default function Sizes() {\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 24 }}>\n <Spinner size=\"sm\" />\n <Spinner size=\"md\" />\n <Spinner size=\"lg\" />\n <Spinner size=\"xl\" />\n </View>\n );\n}\n"
|
|
89
119
|
}
|
|
90
120
|
]
|
|
91
121
|
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "stateful-button",
|
|
3
|
+
"category": "Buttons",
|
|
4
|
+
"aliases": [
|
|
5
|
+
"loading-button",
|
|
6
|
+
"async-button",
|
|
7
|
+
"status-button"
|
|
8
|
+
],
|
|
9
|
+
"exportName": "StatefulButton",
|
|
10
|
+
"files": [
|
|
11
|
+
"src/components/ui/stateful-button.tsx"
|
|
12
|
+
],
|
|
13
|
+
"summary": "Button whose label rolls and width springs through loading, success and error.",
|
|
14
|
+
"props": [
|
|
15
|
+
[
|
|
16
|
+
"children",
|
|
17
|
+
"string",
|
|
18
|
+
"—",
|
|
19
|
+
"Label while idle."
|
|
20
|
+
],
|
|
21
|
+
[
|
|
22
|
+
"state",
|
|
23
|
+
"\"idle\" | \"loading\" | \"success\" | \"error\"",
|
|
24
|
+
"—",
|
|
25
|
+
"Controlled state. Omit it to let a Promise from onPress drive the button."
|
|
26
|
+
],
|
|
27
|
+
[
|
|
28
|
+
"onPress",
|
|
29
|
+
"() => void | Promise<unknown>",
|
|
30
|
+
"—",
|
|
31
|
+
"Resolve → success, reject → error, then back to idle (when state is not set)."
|
|
32
|
+
],
|
|
33
|
+
[
|
|
34
|
+
"loadingText",
|
|
35
|
+
"string",
|
|
36
|
+
"children",
|
|
37
|
+
"Label while loading."
|
|
38
|
+
],
|
|
39
|
+
[
|
|
40
|
+
"successText",
|
|
41
|
+
"string",
|
|
42
|
+
"\"Done\"",
|
|
43
|
+
"Label after success."
|
|
44
|
+
],
|
|
45
|
+
[
|
|
46
|
+
"errorText",
|
|
47
|
+
"string",
|
|
48
|
+
"\"Failed\"",
|
|
49
|
+
"Label after an error."
|
|
50
|
+
],
|
|
51
|
+
[
|
|
52
|
+
"variant",
|
|
53
|
+
"\"primary\" | \"secondary\" | \"outline\" | \"ghost\"",
|
|
54
|
+
"\"primary\"",
|
|
55
|
+
"Idle look; success and error tint it green / red."
|
|
56
|
+
],
|
|
57
|
+
[
|
|
58
|
+
"size",
|
|
59
|
+
"\"sm\" | \"md\" | \"lg\"",
|
|
60
|
+
"\"md\"",
|
|
61
|
+
"36 / 46 / 54pt tall."
|
|
62
|
+
],
|
|
63
|
+
[
|
|
64
|
+
"color",
|
|
65
|
+
"string",
|
|
66
|
+
"theme primary",
|
|
67
|
+
"Tint for the idle look."
|
|
68
|
+
],
|
|
69
|
+
[
|
|
70
|
+
"resetAfter",
|
|
71
|
+
"number",
|
|
72
|
+
"1600",
|
|
73
|
+
"ms the result shows before returning to idle (self-driving only)."
|
|
74
|
+
],
|
|
75
|
+
[
|
|
76
|
+
"minLoading",
|
|
77
|
+
"number",
|
|
78
|
+
"500",
|
|
79
|
+
"Minimum ms of spinner (self-driving only)."
|
|
80
|
+
],
|
|
81
|
+
[
|
|
82
|
+
"onStateChange",
|
|
83
|
+
"(state) => void",
|
|
84
|
+
"—",
|
|
85
|
+
"Called on every state change."
|
|
86
|
+
],
|
|
87
|
+
[
|
|
88
|
+
"pill",
|
|
89
|
+
"boolean",
|
|
90
|
+
"false",
|
|
91
|
+
"Fully rounded ends."
|
|
92
|
+
],
|
|
93
|
+
[
|
|
94
|
+
"block",
|
|
95
|
+
"boolean",
|
|
96
|
+
"false",
|
|
97
|
+
"Stretch to the parent's width (no width animation)."
|
|
98
|
+
],
|
|
99
|
+
[
|
|
100
|
+
"disabled",
|
|
101
|
+
"boolean",
|
|
102
|
+
"false",
|
|
103
|
+
"Blocks presses and dims the button."
|
|
104
|
+
],
|
|
105
|
+
[
|
|
106
|
+
"haptics",
|
|
107
|
+
"boolean",
|
|
108
|
+
"true",
|
|
109
|
+
"Taps on press, success and error."
|
|
110
|
+
],
|
|
111
|
+
[
|
|
112
|
+
"accessibilityLabel",
|
|
113
|
+
"string",
|
|
114
|
+
"current label",
|
|
115
|
+
"Override what screen readers hear."
|
|
116
|
+
],
|
|
117
|
+
[
|
|
118
|
+
"textStyle",
|
|
119
|
+
"StyleProp<TextStyle>",
|
|
120
|
+
"—",
|
|
121
|
+
"Style for the label letters."
|
|
122
|
+
],
|
|
123
|
+
[
|
|
124
|
+
"style",
|
|
125
|
+
"StyleProp<ViewStyle>",
|
|
126
|
+
"—",
|
|
127
|
+
"Style for the outer wrapper (margins, alignment)."
|
|
128
|
+
]
|
|
129
|
+
],
|
|
130
|
+
"behavior": [
|
|
131
|
+
[
|
|
132
|
+
"Touch",
|
|
133
|
+
"Springs to 96% on press; at least 44pt tall to touch (hitSlop on the small size). Presses are ignored while loading and, when self-driving, until it returns to idle."
|
|
134
|
+
],
|
|
135
|
+
[
|
|
136
|
+
"Motion",
|
|
137
|
+
"Label letters roll on the native driver; width, colour and the check / cross stroke spring or ease on the JS driver on separate views."
|
|
138
|
+
],
|
|
139
|
+
[
|
|
140
|
+
"Reduced motion",
|
|
141
|
+
"Labels crossfade instead of rolling and the width changes instantly."
|
|
142
|
+
],
|
|
143
|
+
[
|
|
144
|
+
"Haptics",
|
|
145
|
+
"Light tap on press, success / error notification on the result (expo-haptics; turn off with haptics={false})."
|
|
146
|
+
],
|
|
147
|
+
[
|
|
148
|
+
"Screen readers",
|
|
149
|
+
"Button role with busy and disabled state; every new label (Saving…, Saved, Failed) is announced."
|
|
150
|
+
],
|
|
151
|
+
[
|
|
152
|
+
"Platforms",
|
|
153
|
+
"iOS, Android and web."
|
|
154
|
+
]
|
|
155
|
+
],
|
|
156
|
+
"usage": "import { StatefulButton } from '@/components/ui/stateful-button';\n\n<StatefulButton loadingText=\"Saving…\" successText=\"Saved\" onPress={() => save()}>\n Save changes\n</StatefulButton>\n\n<StatefulButton state={state} errorText=\"Retry\">Upload</StatefulButton>",
|
|
157
|
+
"examples": [
|
|
158
|
+
{
|
|
159
|
+
"name": "SaveChanges",
|
|
160
|
+
"title": "Save Changes",
|
|
161
|
+
"code": "import { StatefulButton, type StatefulButtonState } from '@/components/ui/stateful-button';\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\nexport default function SaveChanges() {\n return (\n <StatefulButton loadingText=\"Saving…\" successText=\"Saved\" onPress={() => sleep(1200)}>\n Save changes\n </StatefulButton>\n );\n}\n"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"name": "PaymentRetry",
|
|
165
|
+
"title": "Payment Retry",
|
|
166
|
+
"code": "import React, { useRef, useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { StatefulButton, type StatefulButtonState } from '@/components/ui/stateful-button';\nimport { useKit } from '@/lib/kit';\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\nexport default function PaymentRetry() {\n const { colors } = useKit();\n const attempts = useRef(0);\n const [note, setNote] = useState('First attempt is declined, the retry goes through.');\n return (\n <View style={{ gap: 10 }}>\n <StatefulButton\n block\n size=\"lg\"\n loadingText=\"Processing…\"\n successText=\"Paid ₹1,299\"\n errorText=\"Card declined\"\n onPress={async () => {\n attempts.current += 1;\n await sleep(1300);\n if (attempts.current % 2 === 1) {\n setNote('Declined by the bank. Tap again to retry.');\n throw new Error('declined');\n }\n setNote('Payment received.');\n }}\n >\n Pay ₹1,299\n </StatefulButton>\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>{note}</Text>\n </View>\n );\n}\n"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"name": "Controlled",
|
|
170
|
+
"title": "Controlled",
|
|
171
|
+
"code": "import React, { useRef, useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { StatefulButton, type StatefulButtonState } from '@/components/ui/stateful-button';\nimport { useKit } from '@/lib/kit';\n\nconst ORDER: StatefulButtonState[] = ['idle', 'loading', 'success', 'error'];\n\nexport default function Controlled() {\n const { colors, radius } = useKit();\n const [state, setState] = useState<StatefulButtonState>('idle');\n return (\n <View style={{ gap: 14 }}>\n <StatefulButton state={state} loadingText=\"Uploading\" successText=\"Uploaded\" errorText=\"Retry upload\">\n Upload\n </StatefulButton>\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8 }}>\n {ORDER.map((s) => (\n <Text\n key={s}\n accessibilityRole=\"button\"\n accessibilityState={{ selected: s === state }}\n onPress={() => setState(s)}\n style={{\n paddingHorizontal: 12,\n paddingVertical: 8,\n borderRadius: radius.capsule(34),\n overflow: 'hidden',\n fontSize: 13,\n color: s === state ? colors.accentForeground : colors.foreground,\n backgroundColor: s === state ? colors.accent : colors.muted,\n }}\n >\n {s}\n </Text>\n ))}\n </View>\n </View>\n );\n}\n"
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"name": "VariantsAndSizes",
|
|
175
|
+
"title": "Variants And Sizes",
|
|
176
|
+
"code": "import { Text, View } from 'react-native';\nimport { StatefulButton, type StatefulButtonState } from '@/components/ui/stateful-button';\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\nexport default function VariantsAndSizes() {\n return (\n <View style={{ gap: 12 }}>\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 10 }}>\n <StatefulButton onPress={() => sleep(1000)}>Publish</StatefulButton>\n <StatefulButton variant=\"secondary\" onPress={() => sleep(1000)}>Sync</StatefulButton>\n <StatefulButton variant=\"outline\" onPress={() => sleep(1000)}>Export</StatefulButton>\n <StatefulButton variant=\"ghost\" onPress={() => sleep(1000)}>Archive</StatefulButton>\n </View>\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', alignItems: 'center', gap: 10 }}>\n <StatefulButton size=\"sm\" pill onPress={() => sleep(1000)}>Small</StatefulButton>\n <StatefulButton size=\"md\" pill onPress={() => sleep(1000)}>Medium</StatefulButton>\n <StatefulButton size=\"lg\" pill onPress={() => sleep(1000)}>Large</StatefulButton>\n </View>\n </View>\n );\n}\n"
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"name": "FollowToggle",
|
|
180
|
+
"title": "Follow Toggle",
|
|
181
|
+
"code": "import React, { useRef, useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { StatefulButton, type StatefulButtonState } from '@/components/ui/stateful-button';\nimport { useKit } from '@/lib/kit';\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\nexport default function FollowToggle() {\n const { colors } = useKit();\n const [following, setFollowing] = useState(false);\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>\n <View style={{ flexShrink: 1 }}>\n <Text style={{ fontSize: 16, fontWeight: '600', color: colors.foreground }}>Expo</Text>\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>@expo · 214K followers</Text>\n </View>\n <StatefulButton\n pill\n size=\"sm\"\n variant=\"secondary\"\n successText={following ? 'Following' : 'Unfollowed'}\n resetAfter={900}\n onPress={async () => {\n await sleep(700);\n setFollowing((f) => !f);\n }}\n >\n {following ? 'Following' : 'Follow'}\n </StatefulButton>\n </View>\n );\n}\n"
|
|
182
|
+
}
|
|
183
|
+
]
|
|
184
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"files": [
|
|
7
7
|
"src/components/ui/stepper.tsx"
|
|
8
8
|
],
|
|
9
|
+
"summary": "A quantity stepper whose digits roll up or down as the number changes.",
|
|
9
10
|
"props": [
|
|
10
11
|
[
|
|
11
12
|
"value / defaultValue",
|
|
@@ -128,41 +129,56 @@
|
|
|
128
129
|
"Applied to the number (fontSize, colour, weight)."
|
|
129
130
|
]
|
|
130
131
|
],
|
|
132
|
+
"behavior": [
|
|
133
|
+
[
|
|
134
|
+
"Touch",
|
|
135
|
+
"Hold a button to keep stepping, speeding up as you hold. The cart variant opens from a single + into − 1 +, with a trash can at 1."
|
|
136
|
+
],
|
|
137
|
+
[
|
|
138
|
+
"Motion",
|
|
139
|
+
"Each changed character slides out as the new one slides in; buttons spring on press and a press past min or max gives a small bump."
|
|
140
|
+
],
|
|
141
|
+
[
|
|
142
|
+
"Screen readers",
|
|
143
|
+
"One adjustable element with min, max and the formatted value (suffix included), plus 'Increase' and 'Decrease' actions."
|
|
144
|
+
]
|
|
145
|
+
],
|
|
146
|
+
"usage": "import { useState } from 'react';\nimport { Stepper } from '@/components/ui/stepper';\n\nconst [guests, setGuests] = useState(2);\n<Stepper value={guests} onValueChange={setGuests} min={1} max={16} label=\"Guests\" />",
|
|
131
147
|
"examples": [
|
|
132
148
|
{
|
|
133
149
|
"name": "Cart",
|
|
134
150
|
"title": "Cart",
|
|
135
|
-
"code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst eyebrow = { fontSize: 12, fontWeight: '400', letterSpacing: 0.6, textTransform: 'uppercase' } as const;\n\nconst money = (n: number) => {\n const [int, frac] = n.toFixed(2).split('.');\n return `₹${int.replace(/(\\d)(?=(\\d\\d)+\\d$)/g, '$1,')}${frac === '00' ? '' : `.${frac}`}`;\n};\n\nconst MENU = [\n { key: 'bowl', emoji: '🥗', name: 'Harvest bowl', note: 'Kale, squash, tahini', price: 349 },\n { key: 'ramen', emoji: '🍜', name: 'Miso ramen', note: 'Spicy · extra egg', price: 420 },\n { key: 'mochi', emoji: '🍡', name: 'Mochi trio', note: 'Matcha, yuzu, sesame', price: 180 },\n];\n\nexport default function Cart() {\n const { colors, radius } = useKit();\n const [qty, setQty] = useState<Record<string, number>>({ bowl: 1, ramen: 2, mochi: 0 });\n const total = MENU.reduce((sum, it) => sum + it.price * (qty[it.key] ?? 0), 0);\n return (\n <Card>\n <Text style={[eyebrow, { color: colors.mutedForeground }]}>Your order</Text>\n {MENU.map((it) => (\n <View key={it.key} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ width: 44, height: 44, borderRadius: radius.scale(12), backgroundColor: colors.muted, alignItems: 'center', justifyContent: 'center' }}>\n <Text style={{ fontSize: 22 }}>{it.emoji}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text style={{ fontSize: 15, fontWeight: '400', color: colors.foreground }}>{it.name}</Text>\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>{money(it.price)} · {it.note}</Text>\n </View>\n <Stepper\n variant=\"cart\"\n size=\"sm\"\n value={qty[it.key]}\n onValueChange={(n) => setQty((q) => ({ ...q, [it.key]: n }))}\n max={9}\n accessibilityLabel={`${it.name} quantity`}\n />\n </View>\n ))}\n <View style={{ height: StyleSheet.hairlineWidth, backgroundColor: colors.border }} />\n <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'baseline' }}>\n <Text style={{ fontSize: 15, fontWeight: '400', color: colors.foreground }}>Subtotal</Text>\n <Text style={{ fontSize: 18, fontWeight: '400', color: colors.foreground, fontVariant: ['tabular-nums'] }}>{money(total)}</Text>\n </View>\n </Card>\n );\n}\n"
|
|
151
|
+
"code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\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 card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, screen, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 16, gap: 12 }\n : { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst eyebrow = { fontSize: 12, fontWeight: '400', letterSpacing: 0.6, textTransform: 'uppercase' } as const;\n\nconst money = (n: number) => {\n const [int, frac] = n.toFixed(2).split('.');\n return `₹${int.replace(/(\\d)(?=(\\d\\d)+\\d$)/g, '$1,')}${frac === '00' ? '' : `.${frac}`}`;\n};\n\nconst MENU = [\n { key: 'bowl', emoji: '🥗', name: 'Harvest bowl', note: 'Kale, squash, tahini', price: 349 },\n { key: 'ramen', emoji: '🍜', name: 'Miso ramen', note: 'Spicy · extra egg', price: 420 },\n { key: 'mochi', emoji: '🍡', name: 'Mochi trio', note: 'Matcha, yuzu, sesame', price: 180 },\n];\n\nexport default function Cart({ screen }: DemoProps) {\n const { colors, radius } = useKit();\n const [qty, setQty] = useState<Record<string, number>>({ bowl: 1, ramen: 2, mochi: 0 });\n const total = MENU.reduce((sum, it) => sum + it.price * (qty[it.key] ?? 0), 0);\n return (\n <Card screen={screen}>\n <Text style={[eyebrow, { color: colors.mutedForeground }]}>Your order</Text>\n {MENU.map((it) => (\n <View key={it.key} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ width: 44, height: 44, borderRadius: radius.scale(12), backgroundColor: colors.muted, alignItems: 'center', justifyContent: 'center' }}>\n <Text style={{ fontSize: 22 }}>{it.emoji}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text style={{ fontSize: 15, fontWeight: '400', color: colors.foreground }}>{it.name}</Text>\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>{money(it.price)} · {it.note}</Text>\n </View>\n <Stepper\n variant=\"cart\"\n size=\"sm\"\n value={qty[it.key]}\n onValueChange={(n) => setQty((q) => ({ ...q, [it.key]: n }))}\n max={9}\n accessibilityLabel={`${it.name} quantity`}\n />\n </View>\n ))}\n <View style={{ height: StyleSheet.hairlineWidth, backgroundColor: colors.border }} />\n <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'baseline' }}>\n <Text style={{ fontSize: 15, fontWeight: '400', color: colors.foreground }}>Subtotal</Text>\n <Text style={{ fontSize: 18, fontWeight: '400', color: colors.foreground, fontVariant: ['tabular-nums'] }}>{money(total)}</Text>\n </View>\n </Card>\n );\n}\n"
|
|
136
152
|
},
|
|
137
153
|
{
|
|
138
154
|
"name": "Guests",
|
|
139
155
|
"title": "Guests",
|
|
140
|
-
"code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Guests() {\n const { colors } = useKit();\n const [adults, setAdults] = useState(2);\n const [children, setChildren] = useState(0);\n const [infants, setInfants] = useState(0);\n const cap = 6;\n const rule = <View style={{ height: StyleSheet.hairlineWidth, backgroundColor: colors.border }} />;\n return (\n <Card style={{ gap: 14 }}>\n <Stepper label=\"Adults\" description=\"Ages 13 or above\" value={adults} onValueChange={setAdults} min={1} max={cap - children} />\n {rule}\n <Stepper label=\"Children\" description=\"Ages 2–12\" value={children} onValueChange={setChildren} max={cap - adults} />\n {rule}\n <Stepper label=\"Infants\" description=\"Under 2\" value={infants} onValueChange={setInfants} max={5} />\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>\n This place has a maximum of {cap} guests, not including infants.\n </Text>\n </Card>\n );\n}\n"
|
|
156
|
+
"code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\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 card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, screen, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 16, gap: 12 }\n : { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Guests({ screen }: DemoProps) {\n const { colors } = useKit();\n const [adults, setAdults] = useState(2);\n const [children, setChildren] = useState(0);\n const [infants, setInfants] = useState(0);\n const cap = 6;\n const rule = <View style={{ height: StyleSheet.hairlineWidth, backgroundColor: colors.border }} />;\n return (\n <Card screen={screen} style={{ gap: 14 }}>\n <Stepper label=\"Adults\" description=\"Ages 13 or above\" value={adults} onValueChange={setAdults} min={1} max={cap - children} />\n {rule}\n <Stepper label=\"Children\" description=\"Ages 2–12\" value={children} onValueChange={setChildren} max={cap - adults} />\n {rule}\n <Stepper label=\"Infants\" description=\"Under 2\" value={infants} onValueChange={setInfants} max={5} />\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>\n This place has a maximum of {cap} guests, not including infants.\n </Text>\n </Card>\n );\n}\n"
|
|
141
157
|
},
|
|
142
158
|
{
|
|
143
159
|
"name": "AddToBag",
|
|
144
160
|
"title": "Add To Bag",
|
|
145
|
-
"code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst money = (n: number) => {\n const [int, frac] = n.toFixed(2).split('.');\n return `₹${int.replace(/(\\d)(?=(\\d\\d)+\\d$)/g, '$1,')}${frac === '00' ? '' : `.${frac}`}`;\n};\n\nexport default function AddToBag() {\n const { colors, radius } = useKit();\n const [qty, setQty] = useState(1);\n const price = 1899;\n return (\n <Card>\n <View style={{ gap: 2 }}>\n <Text style={{ fontSize: 17, fontWeight: '400', color: colors.foreground }}>Linen overshirt</Text>\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>Sand · Size M · Free returns</Text>\n </View>\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <Stepper variant=\"filled\" size=\"lg\" value={qty} onValueChange={setQty} min={1} max={10} accessibilityLabel=\"Quantity\" />\n <Pressable\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1,\n height: 44,\n borderRadius: radius.capsule(44),\n backgroundColor: colors.primary,\n alignItems: 'center',\n justifyContent: 'center',\n opacity: pressed ? 0.85 : 1,\n })}\n >\n <Text style={{ fontSize: 15, fontWeight: '400', color: colors.primaryForeground, fontVariant: ['tabular-nums'] }}>\n Add to bag · {money(price * qty)}\n </Text>\n </Pressable>\n </View>\n <Text style={{ fontSize: 12, color: colors.mutedForeground }}>Only 10 left — hold + to add faster.</Text>\n </Card>\n );\n}\n"
|
|
161
|
+
"code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\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 card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, screen, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 16, gap: 12 }\n : { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst money = (n: number) => {\n const [int, frac] = n.toFixed(2).split('.');\n return `₹${int.replace(/(\\d)(?=(\\d\\d)+\\d$)/g, '$1,')}${frac === '00' ? '' : `.${frac}`}`;\n};\n\nexport default function AddToBag({ screen }: DemoProps) {\n const { colors, radius } = useKit();\n const [qty, setQty] = useState(1);\n const price = 1899;\n return (\n <Card screen={screen}>\n <View style={{ gap: 2 }}>\n <Text style={{ fontSize: 17, fontWeight: '400', color: colors.foreground }}>Linen overshirt</Text>\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>Sand · Size M · Free returns</Text>\n </View>\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <Stepper variant=\"filled\" size=\"lg\" value={qty} onValueChange={setQty} min={1} max={10} accessibilityLabel=\"Quantity\" />\n <Pressable\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1,\n height: 44,\n borderRadius: radius.capsule(44),\n backgroundColor: colors.primary,\n alignItems: 'center',\n justifyContent: 'center',\n opacity: pressed ? 0.85 : 1,\n })}\n >\n <Text style={{ fontSize: 15, fontWeight: '400', color: colors.primaryForeground, fontVariant: ['tabular-nums'] }}>\n Add to bag · {money(price * qty)}\n </Text>\n </Pressable>\n </View>\n <Text style={{ fontSize: 12, color: colors.mutedForeground }}>Only 10 left — hold + to add faster.</Text>\n </Card>\n );\n}\n"
|
|
146
162
|
},
|
|
147
163
|
{
|
|
148
164
|
"name": "Menu",
|
|
149
165
|
"title": "Menu",
|
|
150
|
-
"code": "import { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst eyebrow = { fontSize: 12, fontWeight: '400', letterSpacing: 0.6, textTransform: 'uppercase' } as const;\n\nexport default function Menu() {\n const { colors } = useKit();\n return (\n <Card>\n <Text style={[eyebrow, { color: colors.mutedForeground }]}>Popular</Text>\n <Stepper variant=\"cart\" addLabel=\"Add\" label=\"Pistachio croissant\" description=\"₹220 · Baked this morning\" accessibilityLabel=\"Pistachio croissant\" />\n <Stepper variant=\"cart\" addLabel=\"Add\" label=\"Iced oat latte\" description=\"₹280 · 16 oz\" defaultValue={1} accessibilityLabel=\"Iced oat latte\" />\n </Card>\n );\n}\n"
|
|
166
|
+
"code": "import { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\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 card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, screen, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 16, gap: 12 }\n : { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst eyebrow = { fontSize: 12, fontWeight: '400', letterSpacing: 0.6, textTransform: 'uppercase' } as const;\n\nexport default function Menu({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen}>\n <Text style={[eyebrow, { color: colors.mutedForeground }]}>Popular</Text>\n <Stepper variant=\"cart\" addLabel=\"Add\" label=\"Pistachio croissant\" description=\"₹220 · Baked this morning\" accessibilityLabel=\"Pistachio croissant\" />\n <Stepper variant=\"cart\" addLabel=\"Add\" label=\"Iced oat latte\" description=\"₹280 · 16 oz\" defaultValue={1} accessibilityLabel=\"Iced oat latte\" />\n </Card>\n );\n}\n"
|
|
151
167
|
},
|
|
152
168
|
{
|
|
153
169
|
"name": "Formatted",
|
|
154
170
|
"title": "Formatted",
|
|
155
|
-
"code": "import { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst money = (n: number) => {\n const [int, frac] = n.toFixed(2).split('.');\n return `₹${int.replace(/(\\d)(?=(\\d\\d)+\\d$)/g, '$1,')}${frac === '00' ? '' : `.${frac}`}`;\n};\n\nexport default function Formatted() {\n return (\n <Card style={{ gap: 16 }}>\n <Stepper variant=\"filled\" size=\"sm\" label=\"Nights\" description=\"Check-in Fri, Oct 24\" defaultValue={3} min={1} max={28} suffix={(n) => (n === 1 ? 'night' : 'nights')} />\n <Stepper variant=\"filled\" size=\"sm\" label=\"Driver tip\" description=\"100% goes to your driver\" defaultValue={50} step={10} min={0} max={500} format={money} />\n <Stepper variant=\"filled\" size=\"sm\" label=\"Search radius\" defaultValue={2.5} step={0.5} min={0.5} max={10} suffix=\"km\" />\n <Stepper variant=\"compact\" label=\"Seats\" description=\"₹499 per seat / month\" defaultValue={5} min={1} max={50} />\n </Card>\n );\n}\n"
|
|
171
|
+
"code": "import { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\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 card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, screen, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 16, gap: 12 }\n : { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst money = (n: number) => {\n const [int, frac] = n.toFixed(2).split('.');\n return `₹${int.replace(/(\\d)(?=(\\d\\d)+\\d$)/g, '$1,')}${frac === '00' ? '' : `.${frac}`}`;\n};\n\nexport default function Formatted({ screen }: DemoProps) {\n return (\n <Card screen={screen} style={{ gap: 16 }}>\n <Stepper variant=\"filled\" size=\"sm\" label=\"Nights\" description=\"Check-in Fri, Oct 24\" defaultValue={3} min={1} max={28} suffix={(n) => (n === 1 ? 'night' : 'nights')} />\n <Stepper variant=\"filled\" size=\"sm\" label=\"Driver tip\" description=\"100% goes to your driver\" defaultValue={50} step={10} min={0} max={500} format={money} />\n <Stepper variant=\"filled\" size=\"sm\" label=\"Search radius\" defaultValue={2.5} step={0.5} min={0.5} max={10} suffix=\"km\" />\n <Stepper variant=\"compact\" label=\"Seats\" description=\"₹499 per seat / month\" defaultValue={5} min={1} max={50} />\n </Card>\n );\n}\n"
|
|
156
172
|
},
|
|
157
173
|
{
|
|
158
174
|
"name": "Sizes",
|
|
159
175
|
"title": "Sizes",
|
|
160
|
-
"code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Sizes() {\n const { colors } = useKit();\n const row = (name: string, el: React.ReactNode) => (\n <View key={name} style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', minHeight: 48 }}>\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{name}</Text>\n {el}\n </View>\n );\n return (\n <Card style={{ gap: 4 }}>\n {row('Small', <Stepper size=\"sm\" defaultValue={2} max={20} accessibilityLabel=\"Small stepper\" />)}\n {row('Medium', <Stepper size=\"md\" defaultValue={2} max={20} accessibilityLabel=\"Medium stepper\" />)}\n {row('Large', <Stepper size=\"lg\" defaultValue={2} max={20} accessibilityLabel=\"Large stepper\" />)}\n {row('Compact', <Stepper variant=\"compact\" defaultValue={2} max={20} accessibilityLabel=\"Compact stepper\" />)}\n </Card>\n );\n}\n"
|
|
176
|
+
"code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\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 card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, screen, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 16, gap: 12 }\n : { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Sizes({ screen }: DemoProps) {\n const { colors } = useKit();\n const row = (name: string, el: React.ReactNode) => (\n <View key={name} style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', minHeight: 48 }}>\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{name}</Text>\n {el}\n </View>\n );\n return (\n <Card screen={screen} style={{ gap: 4 }}>\n {row('Small', <Stepper size=\"sm\" defaultValue={2} max={20} accessibilityLabel=\"Small stepper\" />)}\n {row('Medium', <Stepper size=\"md\" defaultValue={2} max={20} accessibilityLabel=\"Medium stepper\" />)}\n {row('Large', <Stepper size=\"lg\" defaultValue={2} max={20} accessibilityLabel=\"Large stepper\" />)}\n {row('Compact', <Stepper variant=\"compact\" defaultValue={2} max={20} accessibilityLabel=\"Compact stepper\" />)}\n </Card>\n );\n}\n"
|
|
161
177
|
},
|
|
162
178
|
{
|
|
163
179
|
"name": "Colors",
|
|
164
180
|
"title": "Colors",
|
|
165
|
-
"code": "import { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\nimport { useKit } from '@/lib/kit';\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, ...rest }: ViewProps) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Colors() {\n const { colors } = useKit();\n return (\n <Card style={{ gap: 16 }}>\n <Stepper variant=\"filled\" accent={colors.success} label=\"Water\" description=\"Glasses today\" defaultValue={6} max={12} />\n <Stepper variant=\"compact\" accent={colors.warning} label=\"Espresso shots\" defaultValue={2} max={4} />\n <Stepper variant=\"cart\" accent={colors.danger} addLabel=\"Add\" label=\"Hot sauce\" description=\"₹30 each\" />\n </Card>\n );\n}\n"
|
|
181
|
+
"code": "import { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { Stepper } from '@/components/ui/stepper';\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 card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ style, screen, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 16, gap: 12 }\n : { backgroundColor: colors.card, borderColor: colors.border, borderWidth: StyleSheet.hairlineWidth, borderRadius: radius.xl, padding: 16, gap: 12 },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Colors({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen} style={{ gap: 16 }}>\n <Stepper variant=\"filled\" accent={colors.success} label=\"Water\" description=\"Glasses today\" defaultValue={6} max={12} />\n <Stepper variant=\"compact\" accent={colors.warning} label=\"Espresso shots\" defaultValue={2} max={4} />\n <Stepper variant=\"cart\" accent={colors.danger} addLabel=\"Add\" label=\"Hot sauce\" description=\"₹30 each\" />\n </Card>\n );\n}\n"
|
|
166
182
|
}
|
|
167
183
|
]
|
|
168
184
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"files": [
|
|
7
7
|
"src/components/ui/swipe-deck.tsx"
|
|
8
8
|
],
|
|
9
|
+
"summary": "A card stack you swipe left or right; the top card leans in 3D and stamps a label.",
|
|
9
10
|
"props": [
|
|
10
11
|
[
|
|
11
12
|
"data",
|
|
@@ -98,5 +99,20 @@
|
|
|
98
99
|
"Style for the root view."
|
|
99
100
|
]
|
|
100
101
|
],
|
|
102
|
+
"behavior": [
|
|
103
|
+
[
|
|
104
|
+
"Touch",
|
|
105
|
+
"Drag the top card past the threshold or flick it to throw it off; a short drag springs back. A ref exposes swipe, undo and reset for buttons."
|
|
106
|
+
],
|
|
107
|
+
[
|
|
108
|
+
"Motion",
|
|
109
|
+
"The card follows your finger, rotates and tilts in 3D, washes in the right or left colour and shows the stamp, while the next card rises."
|
|
110
|
+
],
|
|
111
|
+
[
|
|
112
|
+
"Screen readers",
|
|
113
|
+
"Only the top card is exposed. It offers the right and left labels as accessibility actions, which throw the card that way."
|
|
114
|
+
]
|
|
115
|
+
],
|
|
116
|
+
"usage": null,
|
|
101
117
|
"examples": []
|
|
102
118
|
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "swipeable-row",
|
|
3
3
|
"category": "Display",
|
|
4
|
-
"aliases": [
|
|
4
|
+
"aliases": [
|
|
5
|
+
"swipeable-list",
|
|
6
|
+
"swipe-actions"
|
|
7
|
+
],
|
|
5
8
|
"exportName": "SwipeableRow",
|
|
6
9
|
"files": [
|
|
7
10
|
"src/components/ui/swipeable-row.tsx"
|
|
8
11
|
],
|
|
12
|
+
"summary": "A mail-style row whose actions fan out and grow as you drag it sideways.",
|
|
9
13
|
"props": [
|
|
10
14
|
[
|
|
11
15
|
"leftActions",
|
|
@@ -68,5 +72,24 @@
|
|
|
68
72
|
"Style for the outer row that holds the actions behind the sliding content."
|
|
69
73
|
]
|
|
70
74
|
],
|
|
75
|
+
"behavior": [
|
|
76
|
+
[
|
|
77
|
+
"Touch",
|
|
78
|
+
"Drag sideways to reveal the actions; a full swipe past half the row fires the edge action. Tap the open row to close it."
|
|
79
|
+
],
|
|
80
|
+
[
|
|
81
|
+
"Motion",
|
|
82
|
+
"Actions fan out and grow with your finger, the edge action stretches across on a full swipe, and destructive actions slide away and collapse the row."
|
|
83
|
+
],
|
|
84
|
+
[
|
|
85
|
+
"Screen readers",
|
|
86
|
+
"Every action is offered as a named accessibility action on the row, and each revealed action is a labelled button."
|
|
87
|
+
],
|
|
88
|
+
[
|
|
89
|
+
"Web",
|
|
90
|
+
"Built on PanResponder, so a mouse drag works too; the row shows a grab cursor and text selection is turned off."
|
|
91
|
+
]
|
|
92
|
+
],
|
|
93
|
+
"usage": null,
|
|
71
94
|
"examples": []
|
|
72
95
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"files": [
|
|
7
7
|
"src/components/ui/switch.tsx"
|
|
8
8
|
],
|
|
9
|
+
"summary": "A toggle whose thumb squashes and stretches toward the other side while held.",
|
|
9
10
|
"props": [
|
|
10
11
|
[
|
|
11
12
|
"value / onValueChange",
|
|
@@ -74,6 +75,25 @@
|
|
|
74
75
|
"Style for the pressable — the switch itself, or the whole settings row when label is set."
|
|
75
76
|
]
|
|
76
77
|
],
|
|
78
|
+
"behavior": [
|
|
79
|
+
[
|
|
80
|
+
"Touch",
|
|
81
|
+
"Tap to flip, or drag the thumb across. Passing label (and description) turns it into a settings row."
|
|
82
|
+
],
|
|
83
|
+
[
|
|
84
|
+
"Motion",
|
|
85
|
+
"The thumb stretches toward its destination while pressed, springs over with a soft halo, and the track cross-fades."
|
|
86
|
+
],
|
|
87
|
+
[
|
|
88
|
+
"Haptics",
|
|
89
|
+
"A light impact on every flip; turn it off with haptics={false}."
|
|
90
|
+
],
|
|
91
|
+
[
|
|
92
|
+
"Screen readers",
|
|
93
|
+
"A switch role with checked and disabled state, labelled by accessibilityLabel or label."
|
|
94
|
+
]
|
|
95
|
+
],
|
|
96
|
+
"usage": "import { useState } from 'react';\nimport { Switch } from '@/components/ui/switch';\n\nconst [wifi, setWifi] = useState(true);\n<Switch label=\"Wi-Fi\" value={wifi} onValueChange={setWifi} />",
|
|
77
97
|
"examples": [
|
|
78
98
|
{
|
|
79
99
|
"name": "IOS",
|
package/registry/_docs/tabs.json
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"files": [
|
|
7
7
|
"src/components/ui/tabs.tsx"
|
|
8
8
|
],
|
|
9
|
+
"summary": "Underline tabs whose indicator moves like liquid, leading edge first.",
|
|
9
10
|
"props": [
|
|
10
11
|
[
|
|
11
12
|
"items",
|
|
@@ -80,6 +81,29 @@
|
|
|
80
81
|
"Style for the root view wrapping the tab bar and content."
|
|
81
82
|
]
|
|
82
83
|
],
|
|
84
|
+
"behavior": [
|
|
85
|
+
[
|
|
86
|
+
"Touch",
|
|
87
|
+
"Tap a tab or drag the content panes sideways; the underline follows your finger, with rubber-band resistance at the ends."
|
|
88
|
+
],
|
|
89
|
+
[
|
|
90
|
+
"Motion",
|
|
91
|
+
"The underline is measured to each label and springs with the leading edge first. Badges pop, and an overflowing bar scrolls to centre the active tab."
|
|
92
|
+
],
|
|
93
|
+
[
|
|
94
|
+
"Reduced motion",
|
|
95
|
+
"Springs become short timings, the bar scrolls without animating and badges change without the pop."
|
|
96
|
+
],
|
|
97
|
+
[
|
|
98
|
+
"Haptics",
|
|
99
|
+
"A selection tick on every change; turn it off with haptics={false}."
|
|
100
|
+
],
|
|
101
|
+
[
|
|
102
|
+
"Screen readers",
|
|
103
|
+
"The bar is a tablist of tabs with selected and disabled state; badges are read with the label, and inactive panes are hidden."
|
|
104
|
+
]
|
|
105
|
+
],
|
|
106
|
+
"usage": "import { Tabs } from '@/components/ui/tabs';\n\n<Tabs\n items={[\n { value: 'overview', label: 'Overview', content: <Overview /> },\n { value: 'reviews', label: 'Reviews', badge: 128, content: <Reviews /> },\n ]}\n/>",
|
|
83
107
|
"examples": [
|
|
84
108
|
{
|
|
85
109
|
"name": "Underline",
|