@expokit/cli 0.3.0 → 0.4.1
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 +8 -4
- package/bin/expo-kit.js +137 -24
- package/bin/lib.mjs +110 -1
- package/bin/mcp.mjs +40 -18
- package/package.json +2 -2
- package/registry/_docs/accordion.json +1 -1
- package/registry/_docs/assistant-app.json +47 -0
- package/registry/_docs/avatar.json +6 -6
- package/registry/_docs/badge.json +4 -4
- package/registry/_docs/bottom-nav.json +6 -6
- package/registry/_docs/bottom-sheet.json +3 -3
- package/registry/_docs/bullet.json +6 -0
- package/registry/_docs/button-3d.json +3 -3
- package/registry/_docs/button.json +2 -2
- package/registry/_docs/calendar.json +5 -5
- package/registry/_docs/card.json +1 -1
- package/registry/_docs/checkout.json +7 -1
- package/registry/_docs/chip-group.json +1 -1
- package/registry/_docs/confetti.json +3 -3
- package/registry/_docs/date-picker.json +4 -4
- package/registry/_docs/donut-shop.json +88 -0
- package/registry/_docs/drawer.json +4 -4
- package/registry/_docs/fintech-app.json +49 -0
- package/registry/_docs/flip-card.json +4 -4
- package/registry/_docs/floating-input.json +5 -5
- package/registry/_docs/food-app.json +47 -0
- package/registry/_docs/glass.json +1 -1
- package/registry/_docs/grain.json +80 -0
- package/registry/_docs/isometric-bar.json +6 -0
- package/registry/_docs/liquidglass-tab.json +1 -1
- package/registry/_docs/morphing-button-group.json +96 -0
- package/registry/_docs/orb-reveal.json +132 -0
- package/registry/_docs/otp-input.json +4 -4
- package/registry/_docs/password-input.json +3 -3
- package/registry/_docs/payment-card.json +3 -3
- package/registry/_docs/pixel-bar.json +6 -0
- package/registry/_docs/polar-area.json +6 -0
- package/registry/_docs/progress-steps.json +4 -4
- package/registry/_docs/progress.json +2 -2
- package/registry/_docs/qr-code-button.json +122 -0
- package/registry/_docs/radio-group.json +1 -1
- package/registry/_docs/search-bar.json +4 -4
- package/registry/_docs/segmented-control.json +1 -1
- package/registry/_docs/select.json +7 -7
- package/registry/_docs/settings.json +6 -0
- package/registry/_docs/skeleton.json +3 -3
- package/registry/_docs/slider.json +6 -6
- package/registry/_docs/spinner.json +3 -3
- package/registry/_docs/stateful-button.json +1 -1
- package/registry/_docs/stepper.json +7 -7
- package/registry/_docs/tabs.json +2 -2
- package/registry/_docs/text-animation.json +4 -4
- package/registry/_docs/textarea.json +3 -3
- package/registry/_docs/ticket-shape.json +1 -1
- package/registry/_docs/tilt-card.json +4 -4
- package/registry/_docs/time-picker.json +6 -6
- package/registry/_docs/timeline-chart.json +6 -0
- package/registry/_docs/timeline.json +6 -6
- package/registry/_docs/toast.json +8 -8
- package/registry/_docs/treemap.json +6 -0
- package/registry/_docs/waterfall.json +6 -0
- package/registry/accordion/files/src/components/ui/accordion.tsx +36 -7
- package/registry/accordion/files/src/lib/kit.tsx +8 -5
- package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +12 -8
- package/registry/action-sheet/files/src/lib/kit.tsx +8 -5
- package/registry/alert/files/src/components/ui/alert.tsx +11 -5
- package/registry/alert/files/src/components/ui/button.tsx +35 -6
- 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 +23 -9
- package/registry/badge/files/src/lib/kit.tsx +8 -5
- package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +21 -8
- 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 +35 -6
- package/registry/button/files/src/lib/kit.tsx +8 -5
- package/registry/button-3d/files/src/components/ui/button-3d.tsx +35 -7
- package/registry/button-3d/files/src/lib/kit.tsx +8 -5
- package/registry/calendar/files/src/components/ui/calendar.tsx +68 -19
- package/registry/calendar/files/src/lib/kit.tsx +8 -5
- package/registry/card/files/src/components/ui/card.tsx +32 -10
- package/registry/card/files/src/lib/kit.tsx +8 -5
- package/registry/checkbox/files/src/components/ui/checkbox.tsx +31 -8
- package/registry/checkbox/files/src/lib/kit.tsx +8 -5
- package/registry/chip-group/files/src/components/ui/chip-group.tsx +39 -7
- 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 +55 -28
- package/registry/coupon/files/src/components/ui/flip-card.tsx +23 -4
- package/registry/coupon/files/src/components/ui/tilt-card.tsx +59 -18
- package/registry/coupon/files/src/lib/kit.tsx +8 -5
- package/registry/date-picker/files/src/components/ui/calendar.tsx +68 -19
- package/registry/date-picker/files/src/components/ui/date-picker.tsx +98 -44
- package/registry/date-picker/files/src/lib/kit.tsx +8 -5
- package/registry/dialog/files/src/components/ui/dialog.tsx +19 -9
- 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 +31 -21
- 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 +23 -4
- package/registry/flip-card/files/src/lib/kit.tsx +8 -5
- package/registry/floating-input/files/src/components/ui/floating-input.tsx +91 -32
- 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 +2 -2
- package/registry/liquidglass-tab/files/src/lib/tab-bar.ts +1 -1
- package/registry/list-item/files/src/components/ui/list-item.tsx +28 -17
- 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 +57 -24
- package/registry/otp-input/files/src/lib/kit.tsx +8 -5
- package/registry/password-input/files/src/components/ui/floating-input.tsx +91 -32
- package/registry/password-input/files/src/components/ui/password-input.tsx +55 -21
- package/registry/password-input/files/src/lib/kit.tsx +8 -5
- package/registry/payment-card/files/src/components/ui/flip-card.tsx +23 -4
- package/registry/payment-card/files/src/components/ui/payment-card.tsx +35 -18
- package/registry/payment-card/files/src/components/ui/tilt-card.tsx +59 -18
- package/registry/payment-card/files/src/lib/kit.tsx +8 -5
- package/registry/progress/files/src/components/ui/progress.tsx +16 -6
- package/registry/progress/files/src/lib/kit.tsx +8 -5
- package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +22 -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 +48 -17
- package/registry/radio-group/files/src/lib/kit.tsx +8 -5
- package/registry/search-bar/files/src/components/ui/search-bar.tsx +81 -26
- package/registry/search-bar/files/src/lib/kit.tsx +8 -5
- package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +40 -10
- package/registry/segmented-control/files/src/lib/kit.tsx +8 -5
- package/registry/select/files/src/components/ui/select.tsx +161 -86
- 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 +56 -27
- package/registry/slider/files/src/lib/kit.tsx +8 -5
- package/registry/spinner/files/src/components/ui/spinner.tsx +17 -12
- package/registry/spinner/files/src/lib/kit.tsx +8 -5
- package/registry/stateful-button/files/src/components/ui/stateful-button.tsx +12 -4
- package/registry/stateful-button/files/src/lib/kit.tsx +8 -5
- package/registry/stepper/files/src/components/ui/stepper.tsx +83 -29
- package/registry/stepper/files/src/lib/kit.tsx +8 -5
- package/registry/switch/files/src/components/ui/switch.tsx +60 -22
- package/registry/switch/files/src/lib/kit.tsx +8 -5
- package/registry/tabs/files/src/components/ui/tabs.tsx +15 -10
- package/registry/tabs/files/src/lib/kit.tsx +8 -5
- package/registry/text-animation/files/src/components/ui/text-animation.tsx +11 -6
- package/registry/text-animation/files/src/lib/kit.tsx +8 -5
- package/registry/textarea/files/src/components/ui/textarea.tsx +115 -45
- 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 +59 -18
- package/registry/tilt-card/files/src/lib/kit.tsx +8 -5
- package/registry/time-picker/files/src/components/ui/time-picker.tsx +156 -75
- 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 +19 -12
- package/registry/timeline/files/src/lib/kit.tsx +8 -5
- package/registry/toast/files/src/components/ui/button.tsx +35 -6
- package/registry/toast/files/src/components/ui/toast.tsx +25 -14
- package/registry/toast/files/src/lib/kit.tsx +8 -5
- package/schema/components.schema.json +1 -1
- package/registry/_docs/color-swatch.json +0 -82
- package/registry/_docs/kpi-card.json +0 -14
- package/registry/_docs/like-button.json +0 -92
- package/registry/_docs/morph-button.json +0 -128
- package/registry/_docs/pagination-dots.json +0 -108
- package/registry/_docs/shimmer-button.json +0 -98
- package/registry/_docs/tag-input.json +0 -148
- 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/files/src/lib/kit.tsx +0 -219
- 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
|
@@ -90,22 +90,22 @@
|
|
|
90
90
|
{
|
|
91
91
|
"name": "Checkout",
|
|
92
92
|
"title": "Checkout",
|
|
93
|
-
"code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { ProgressSteps } from '@/components/ui/progress-steps';\nimport { alpha, useKit } from '@/lib/kit';\n\nfunction Buttons({ step, max, set, last = 'Place order' }: { step: number; max: number; set: (n: number) => void; last?: string }) {\n const { colors, radius } = useKit();\n const done = step >= max;\n return (\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Pressable\n onPress={() => set(Math.max(0, step - 1))}\n disabled={step === 0}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1,\n
|
|
93
|
+
"code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { ProgressSteps } from '@/components/ui/progress-steps';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\nfunction Buttons({ step, max, set, last = 'Place order' }: { step: number; max: number; set: (n: number) => void; last?: string }) {\n const { colors, radius } = useKit();\n const done = step >= max;\n return (\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Pressable\n onPress={() => set(Math.max(0, step - 1))}\n disabled={step === 0}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1,\n minHeight: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: alpha(colors.foreground, pressed ? 0.1 : 0.05),\n alignItems: 'center',\n justifyContent: 'center',\n opacity: step === 0 ? 0.4 : 1,\n })}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 15, color: colors.foreground }}>Back</Text>\n </Pressable>\n <Pressable\n onPress={() => set(done ? 0 : step + 1)}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1.5,\n minHeight: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: colors.accent,\n alignItems: 'center',\n justifyContent: 'center',\n transform: [{ scale: pressed ? 0.98 : 1 }],\n })}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 15, color: colors.accentForeground }}>\n {done ? 'Start over' : step === max - 1 ? last : 'Continue'}\n </Text>\n </Pressable>\n </View>\n );\n}\n\nexport default function Checkout() {\n const { colors } = useKit();\n const [step, setStep] = useState(1);\n const copy = [\n '2 items · ₹2,798',\n 'Delivering to 14 Rua das Flores, Lisbon',\n 'Visa ending 4242',\n 'Arrives Thursday, 16 October',\n ];\n return (\n <View style={{ gap: 22 }}>\n <ProgressSteps\n steps={['Cart', 'Shipping', 'Payment', 'Review']}\n current={step}\n pressableSteps=\"completed\"\n onStepPress={setStep}\n />\n <Text style={{ fontSize: 14, textAlign: 'center', color: colors.mutedForeground }}>\n {step >= 4 ? 'Order #4821 placed — receipt sent to amelia@hart.studio' : copy[step]}\n </Text>\n <Buttons step={step} max={4} set={setStep} />\n </View>\n );\n}\n"
|
|
94
94
|
},
|
|
95
95
|
{
|
|
96
96
|
"name": "Onboarding",
|
|
97
97
|
"title": "Onboarding",
|
|
98
|
-
"code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { ProgressSteps } from '@/components/ui/progress-steps';\nimport { alpha, useKit } from '@/lib/kit';\n\nfunction Buttons({ step, max, set, last = 'Place order' }: { step: number; max: number; set: (n: number) => void; last?: string }) {\n const { colors, radius } = useKit();\n const done = step >= max;\n return (\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Pressable\n onPress={() => set(Math.max(0, step - 1))}\n disabled={step === 0}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1,\n
|
|
98
|
+
"code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { ProgressSteps } from '@/components/ui/progress-steps';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\nfunction Buttons({ step, max, set, last = 'Place order' }: { step: number; max: number; set: (n: number) => void; last?: string }) {\n const { colors, radius } = useKit();\n const done = step >= max;\n return (\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Pressable\n onPress={() => set(Math.max(0, step - 1))}\n disabled={step === 0}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1,\n minHeight: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: alpha(colors.foreground, pressed ? 0.1 : 0.05),\n alignItems: 'center',\n justifyContent: 'center',\n opacity: step === 0 ? 0.4 : 1,\n })}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 15, color: colors.foreground }}>Back</Text>\n </Pressable>\n <Pressable\n onPress={() => set(done ? 0 : step + 1)}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1.5,\n minHeight: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: colors.accent,\n alignItems: 'center',\n justifyContent: 'center',\n transform: [{ scale: pressed ? 0.98 : 1 }],\n })}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 15, color: colors.accentForeground }}>\n {done ? 'Start over' : step === max - 1 ? last : 'Continue'}\n </Text>\n </Pressable>\n </View>\n );\n}\n\nexport default function Onboarding() {\n const [step, setStep] = useState(2);\n return (\n <View style={{ gap: 20 }}>\n <ProgressSteps\n variant=\"vertical\"\n current={step}\n pressableSteps=\"completed\"\n onStepPress={setStep}\n steps={[\n { label: 'Create your account', description: 'Signed up with amelia@hart.studio' },\n { label: 'Verify your email', description: 'Confirmed with a 6-digit code.' },\n { label: 'Connect your bank', description: 'Read-only access. You can disconnect any time.' },\n { label: 'Set your first budget', description: 'Pick categories and a monthly limit.' },\n ]}\n />\n <Buttons step={step} max={4} set={setStep} last=\"Finish setup\" />\n </View>\n );\n}\n"
|
|
99
99
|
},
|
|
100
100
|
{
|
|
101
101
|
"name": "Compact",
|
|
102
102
|
"title": "Compact",
|
|
103
|
-
"code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { ProgressSteps } from '@/components/ui/progress-steps';\nimport { alpha, useKit } from '@/lib/kit';\n\nfunction Buttons({ step, max, set, last = 'Place order' }: { step: number; max: number; set: (n: number) => void; last?: string }) {\n const { colors, radius } = useKit();\n const done = step >= max;\n return (\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Pressable\n onPress={() => set(Math.max(0, step - 1))}\n disabled={step === 0}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1,\n
|
|
103
|
+
"code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { ProgressSteps } from '@/components/ui/progress-steps';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\nfunction Buttons({ step, max, set, last = 'Place order' }: { step: number; max: number; set: (n: number) => void; last?: string }) {\n const { colors, radius } = useKit();\n const done = step >= max;\n return (\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Pressable\n onPress={() => set(Math.max(0, step - 1))}\n disabled={step === 0}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1,\n minHeight: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: alpha(colors.foreground, pressed ? 0.1 : 0.05),\n alignItems: 'center',\n justifyContent: 'center',\n opacity: step === 0 ? 0.4 : 1,\n })}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 15, color: colors.foreground }}>Back</Text>\n </Pressable>\n <Pressable\n onPress={() => set(done ? 0 : step + 1)}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1.5,\n minHeight: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: colors.accent,\n alignItems: 'center',\n justifyContent: 'center',\n transform: [{ scale: pressed ? 0.98 : 1 }],\n })}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 15, color: colors.accentForeground }}>\n {done ? 'Start over' : step === max - 1 ? last : 'Continue'}\n </Text>\n </Pressable>\n </View>\n );\n}\n\nexport default function Compact() {\n const [step, setStep] = useState(1);\n return (\n <View style={{ gap: 20 }}>\n <ProgressSteps variant=\"bar\" steps={['Shipping address', 'Delivery options', 'Payment', 'Review order']} current={step} />\n <Buttons step={step} max={4} set={setStep} />\n </View>\n );\n}\n"
|
|
104
104
|
},
|
|
105
105
|
{
|
|
106
106
|
"name": "WithDescriptions",
|
|
107
107
|
"title": "With Descriptions",
|
|
108
|
-
"code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { ProgressSteps } from '@/components/ui/progress-steps';\nimport { alpha, useKit } from '@/lib/kit';\n\nfunction Buttons({ step, max, set, last = 'Place order' }: { step: number; max: number; set: (n: number) => void; last?: string }) {\n const { colors, radius } = useKit();\n const done = step >= max;\n return (\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Pressable\n onPress={() => set(Math.max(0, step - 1))}\n disabled={step === 0}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1,\n
|
|
108
|
+
"code": "import React, { useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { ProgressSteps } from '@/components/ui/progress-steps';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\nfunction Buttons({ step, max, set, last = 'Place order' }: { step: number; max: number; set: (n: number) => void; last?: string }) {\n const { colors, radius } = useKit();\n const done = step >= max;\n return (\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Pressable\n onPress={() => set(Math.max(0, step - 1))}\n disabled={step === 0}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1,\n minHeight: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: alpha(colors.foreground, pressed ? 0.1 : 0.05),\n alignItems: 'center',\n justifyContent: 'center',\n opacity: step === 0 ? 0.4 : 1,\n })}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 15, color: colors.foreground }}>Back</Text>\n </Pressable>\n <Pressable\n onPress={() => set(done ? 0 : step + 1)}\n accessibilityRole=\"button\"\n style={({ pressed }) => ({\n flex: 1.5,\n minHeight: 48,\n borderRadius: radius.capsule(48),\n backgroundColor: colors.accent,\n alignItems: 'center',\n justifyContent: 'center',\n transform: [{ scale: pressed ? 0.98 : 1 }],\n })}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 15, color: colors.accentForeground }}>\n {done ? 'Start over' : step === max - 1 ? last : 'Continue'}\n </Text>\n </Pressable>\n </View>\n );\n}\n\nexport default function WithDescriptions() {\n const [step, setStep] = useState(1);\n return (\n <View style={{ gap: 22 }}>\n <ProgressSteps\n current={step}\n steps={[\n { label: 'Profile', description: 'Name, photo' },\n { label: 'Workspace', description: 'Invite team' },\n { label: 'Plan', description: 'Free or Pro' },\n ]}\n />\n <Buttons step={step} max={3} set={setStep} last=\"Create workspace\" />\n </View>\n );\n}\n"
|
|
109
109
|
},
|
|
110
110
|
{
|
|
111
111
|
"name": "WithError",
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
{
|
|
117
117
|
"name": "Linear",
|
|
118
118
|
"title": "Linear",
|
|
119
|
-
"code": "import React, { useEffect, useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { Progress } from '@/components/ui/progress';\n\nexport default function Linear() {\n const [value, setValue] = useState(32);\n return (\n <View style={{ gap: 18 }}>\n <Progress value={value} label=\"Uploading trip-to-lisbon.mov\" showValue />\n <Progress value={value} size=\"sm\" />\n <Progress value={value} size=\"lg\" />\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Button size=\"sm\" variant=\"outline\" onPress={() => setValue((v) => Math.max(0, v - 20))}>\n − 20\n </Button>\n <Button size=\"sm\" variant=\"outline\" onPress={() => setValue((v) => Math.min(100, v + 20))}>\n + 20\n </Button>\n </View>\n </View>\n );\n}\n"
|
|
119
|
+
"code": "import React, { useEffect, useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { Progress } from '@/components/ui/progress';\n\nexport default function Linear() {\n const [value, setValue] = useState(32);\n return (\n <View style={{ gap: 18 }}>\n <Progress value={value} label=\"Uploading trip-to-lisbon.mov\" showValue />\n <Progress value={value} size=\"sm\" />\n <Progress value={value} size=\"lg\" />\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 10 }}>\n <Button size=\"sm\" variant=\"outline\" onPress={() => setValue((v) => Math.max(0, v - 20))}>\n − 20\n </Button>\n <Button size=\"sm\" variant=\"outline\" onPress={() => setValue((v) => Math.min(100, v + 20))}>\n + 20\n </Button>\n </View>\n </View>\n );\n}\n"
|
|
120
120
|
},
|
|
121
121
|
{
|
|
122
122
|
"name": "Download",
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
{
|
|
147
147
|
"name": "Steps",
|
|
148
148
|
"title": "Steps",
|
|
149
|
-
"code": "import React, { useEffect, useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { Progress } from '@/components/ui/progress';\n\nexport default function Steps() {\n const [step, setStep] = useState(2);\n return (\n <View style={{ gap: 16 }}>\n <Progress\n variant=\"steps\"\n steps={5}\n value={step}\n label={step >= 5 ? 'Profile ready' : 'Set up your profile'}\n showValue\n formatValue={(v, max) => `Step ${Math.min(max, v + 1)} of ${max}`}\n />\n <View style={{ flexDirection: 'row', gap: 10 }}>\n <Button size=\"sm\" variant=\"outline\" onPress={() => setStep((s) => Math.max(0, s - 1))}>\n Back\n </Button>\n <Button size=\"sm\" variant=\"accent\" onPress={() => setStep((s) => Math.min(5, s + 1))}>\n Continue\n </Button>\n </View>\n </View>\n );\n}\n"
|
|
149
|
+
"code": "import React, { useEffect, useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { Progress } from '@/components/ui/progress';\n\nexport default function Steps() {\n const [step, setStep] = useState(2);\n return (\n <View style={{ gap: 16 }}>\n <Progress\n variant=\"steps\"\n steps={5}\n value={step}\n label={step >= 5 ? 'Profile ready' : 'Set up your profile'}\n showValue\n formatValue={(v, max) => `Step ${Math.min(max, v + 1)} of ${max}`}\n />\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 10 }}>\n <Button size=\"sm\" variant=\"outline\" onPress={() => setStep((s) => Math.max(0, s - 1))}>\n Back\n </Button>\n <Button size=\"sm\" variant=\"accent\" onPress={() => setStep((s) => Math.min(5, s + 1))}>\n Continue\n </Button>\n </View>\n </View>\n );\n}\n"
|
|
150
150
|
}
|
|
151
151
|
]
|
|
152
152
|
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "qr-code-button",
|
|
3
|
+
"category": "Buttons",
|
|
4
|
+
"aliases": [],
|
|
5
|
+
"exportName": "QrCodeButton",
|
|
6
|
+
"files": [
|
|
7
|
+
"src/components/ui/qr-code-button.tsx"
|
|
8
|
+
],
|
|
9
|
+
"summary": "A pill that morphs into a card with a real, scannable QR code.",
|
|
10
|
+
"props": [
|
|
11
|
+
[
|
|
12
|
+
"value",
|
|
13
|
+
"string",
|
|
14
|
+
"—",
|
|
15
|
+
"What the code encodes: a URL, a payment link, a ticket id."
|
|
16
|
+
],
|
|
17
|
+
[
|
|
18
|
+
"label",
|
|
19
|
+
"string",
|
|
20
|
+
"'Show QR Code'",
|
|
21
|
+
"Text on the pill."
|
|
22
|
+
],
|
|
23
|
+
[
|
|
24
|
+
"copyLabel",
|
|
25
|
+
"string",
|
|
26
|
+
"'Copy'",
|
|
27
|
+
"Copy button text."
|
|
28
|
+
],
|
|
29
|
+
[
|
|
30
|
+
"copiedLabel",
|
|
31
|
+
"string",
|
|
32
|
+
"'Copied'",
|
|
33
|
+
"Shown after Copy is pressed."
|
|
34
|
+
],
|
|
35
|
+
[
|
|
36
|
+
"onCopy",
|
|
37
|
+
"(value: string) => void",
|
|
38
|
+
"—",
|
|
39
|
+
"Wire your clipboard here (expo-clipboard)."
|
|
40
|
+
],
|
|
41
|
+
[
|
|
42
|
+
"open",
|
|
43
|
+
"boolean",
|
|
44
|
+
"—",
|
|
45
|
+
"Controlled state."
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
"defaultOpen",
|
|
49
|
+
"boolean",
|
|
50
|
+
"false",
|
|
51
|
+
"Uncontrolled initial state."
|
|
52
|
+
],
|
|
53
|
+
[
|
|
54
|
+
"onOpenChange",
|
|
55
|
+
"(open: boolean) => void",
|
|
56
|
+
"—",
|
|
57
|
+
"Opened or closed."
|
|
58
|
+
],
|
|
59
|
+
[
|
|
60
|
+
"size",
|
|
61
|
+
"\"sm\" | \"md\" | \"lg\"",
|
|
62
|
+
"\"md\"",
|
|
63
|
+
"Pill height 36 / 44 / 52."
|
|
64
|
+
],
|
|
65
|
+
[
|
|
66
|
+
"codeSize",
|
|
67
|
+
"number",
|
|
68
|
+
"168",
|
|
69
|
+
"Side of the code in points; the card wraps it."
|
|
70
|
+
],
|
|
71
|
+
[
|
|
72
|
+
"color",
|
|
73
|
+
"string",
|
|
74
|
+
"white (dark) / primary (light)",
|
|
75
|
+
"Pill colour. The card stays white so the code scans."
|
|
76
|
+
],
|
|
77
|
+
[
|
|
78
|
+
"accessibilityLabel",
|
|
79
|
+
"string",
|
|
80
|
+
"label",
|
|
81
|
+
"Screen reader label for the pill."
|
|
82
|
+
],
|
|
83
|
+
[
|
|
84
|
+
"style",
|
|
85
|
+
"StyleProp<ViewStyle>",
|
|
86
|
+
"—",
|
|
87
|
+
"Root style."
|
|
88
|
+
]
|
|
89
|
+
],
|
|
90
|
+
"behavior": [
|
|
91
|
+
[
|
|
92
|
+
"Motion",
|
|
93
|
+
"Press swells the pill; it glides into the card while its label fades out and the code fades and scales in. Close glides it back."
|
|
94
|
+
],
|
|
95
|
+
[
|
|
96
|
+
"Layout",
|
|
97
|
+
"Reserves the open card's size up front, so nothing around it moves and every control stays inside its own bounds (taps work on Android)."
|
|
98
|
+
],
|
|
99
|
+
[
|
|
100
|
+
"Code",
|
|
101
|
+
"Encoded in the component: UTF-8 bytes, error correction M, smallest version that fits, mask chosen by the spec's penalty rules. Always dark on white so it scans in dark mode too."
|
|
102
|
+
],
|
|
103
|
+
[
|
|
104
|
+
"Copy",
|
|
105
|
+
"Calls onCopy with the value and flips to a tick and \"Copied\" for 1.5 s."
|
|
106
|
+
],
|
|
107
|
+
[
|
|
108
|
+
"Haptics",
|
|
109
|
+
"A light impact on open, a success tap on Copy, a tick on close."
|
|
110
|
+
],
|
|
111
|
+
[
|
|
112
|
+
"Reduced motion",
|
|
113
|
+
"Opens and closes with a short fade."
|
|
114
|
+
],
|
|
115
|
+
[
|
|
116
|
+
"Screen readers",
|
|
117
|
+
"A button with an expanded state; the code reads as \"QR code for …\"."
|
|
118
|
+
]
|
|
119
|
+
],
|
|
120
|
+
"usage": null,
|
|
121
|
+
"examples": []
|
|
122
|
+
}
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
{
|
|
102
102
|
"name": "CardsHorizontal",
|
|
103
103
|
"title": "Cards Horizontal",
|
|
104
|
-
"code": "import { Text } from 'react-native';\nimport { RadioGroup } from '@/components/ui/radio-group';\n\nexport default function CardsHorizontal() {\n return (\n <RadioGroup\n variant=\"card\"\n direction=\"horizontal\"\n defaultValue=\"card\"\n options={[\n { value: 'card', label: 'Card', description: 'Visa ·· 4242', icon: <Text style={{ fontSize: 20 }}>💳</Text> },\n { value: 'wallet', label: 'Wallet', description: 'Apple Pay', icon: <Text style={{ fontSize: 20 }}>📱</Text> },\n ]}\n />\n );\n}\n"
|
|
104
|
+
"code": "import { Text } from 'react-native';\nimport { RadioGroup } from '@/components/ui/radio-group';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\nexport default function CardsHorizontal() {\n return (\n <RadioGroup\n variant=\"card\"\n direction=\"horizontal\"\n defaultValue=\"card\"\n options={[\n { value: 'card', label: 'Card', description: 'Visa ·· 4242', icon: <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 20 }}>💳</Text> },\n { value: 'wallet', label: 'Wallet', description: 'Apple Pay', icon: <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 20 }}>📱</Text> },\n ]}\n />\n );\n}\n"
|
|
105
105
|
},
|
|
106
106
|
{
|
|
107
107
|
"name": "GroupedList",
|
|
@@ -134,22 +134,22 @@
|
|
|
134
134
|
{
|
|
135
135
|
"name": "WithSuggestions",
|
|
136
136
|
"title": "With Suggestions",
|
|
137
|
-
"code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { SearchBar, type SearchSuggestion } from '@/components/ui/search-bar';\nimport { alpha, useKit } from '@/lib/kit';\n\nconst SUGGESTIONS: SearchSuggestion[] = [\n { key: 'r1', label: 'Noise cancelling headphones', icon: 'recent' },\n { key: 'r2', label: 'Running shoes', icon: 'recent' },\n { key: 't1', label: 'Mechanical keyboard', icon: 'trending', hint: 'Trending' },\n { key: 't2', label: 'Standing desk', icon: 'trending', hint: '2.1k searches' },\n { key: 's1', label: 'Wireless charger', hint: 'Electronics' },\n { key: 's2', label: 'Coffee grinder', hint: 'Kitchen' },\n];\n\nexport default function WithSuggestions() {\n const { colors } = useKit();\n const [query, setQuery] = useState('');\n const [picked, setPicked] = useState<string | null>(null);\n return (\n // Reserve room for the open list, so the bar stays put when suggestions drop down below it.\n <View style={{ gap: 10, minHeight: 400 }}>\n <SearchBar\n value={query}\n onChangeText={setQuery}\n placeholder=\"Search products\"\n suggestions={SUGGESTIONS}\n maxSuggestions={5}\n onSelectSuggestion={(item) => setPicked(item.label)}\n />\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>{picked ? `Showing results for “${picked}”` : 'Tap the field to see recent and trending searches'}</Text>\n </View>\n );\n}\n"
|
|
137
|
+
"code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { SearchBar, type SearchSuggestion } from '@/components/ui/search-bar';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\nconst SUGGESTIONS: SearchSuggestion[] = [\n { key: 'r1', label: 'Noise cancelling headphones', icon: 'recent' },\n { key: 'r2', label: 'Running shoes', icon: 'recent' },\n { key: 't1', label: 'Mechanical keyboard', icon: 'trending', hint: 'Trending' },\n { key: 't2', label: 'Standing desk', icon: 'trending', hint: '2.1k searches' },\n { key: 's1', label: 'Wireless charger', hint: 'Electronics' },\n { key: 's2', label: 'Coffee grinder', hint: 'Kitchen' },\n];\n\nexport default function WithSuggestions({ screen }: DemoProps) {\n const { colors } = useKit();\n const [query, setQuery] = useState('');\n const [picked, setPicked] = useState<string | null>(null);\n return (\n // Reserve room for the open list, so the bar stays put when suggestions drop down below it.\n <View style={[{ gap: 10 }, screen ? { flex: 1 } : { minHeight: 400 }]}>\n <SearchBar\n value={query}\n onChangeText={setQuery}\n placeholder=\"Search products\"\n suggestions={SUGGESTIONS}\n maxSuggestions={5}\n onSelectSuggestion={(item) => setPicked(item.label)}\n />\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>{picked ? `Showing results for “${picked}”` : 'Tap the field to see recent and trending searches'}</Text>\n </View>\n );\n}\n"
|
|
138
138
|
},
|
|
139
139
|
{
|
|
140
140
|
"name": "LiveResults",
|
|
141
141
|
"title": "Live Results",
|
|
142
|
-
"code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { SearchBar, type SearchSuggestion } from '@/components/ui/search-bar';\nimport { alpha, 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 { padding: 16, gap: 12, borderRadius: radius.xl, borderWidth: StyleSheet.hairlineWidth, borderColor: colors.border, backgroundColor: colors.card },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst CONTACTS = [\n { name: 'Aarav Mehta', role: 'Design lead' },\n { name: 'Priya Nair', role: 'Product manager' },\n { name: 'Rohan Kapoor', role: 'iOS engineer' },\n { name: 'Sara Ali', role: 'Marketing' },\n { name: 'Vikram Rao', role: 'Backend engineer' },\n];\n\nexport default function LiveResults() {\n const { colors } = useKit();\n const [query, setQuery] = useState('');\n const [loading, setLoading] = useState(false);\n useEffect(() => {\n if (!query) return setLoading(false);\n setLoading(true);\n const id = setTimeout(() => setLoading(false), 600);\n return () => clearTimeout(id);\n }, [query]);\n const q = query.trim().toLowerCase();\n const results = CONTACTS.filter((c) => c.name.toLowerCase().includes(q) || c.role.toLowerCase().includes(q));\n return (\n <Card>\n <SearchBar variant=\"filled\" value={query} onChangeText={setQuery} loading={loading} placeholder=\"Search people\" />\n {loading ? (\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>Searching your team…</Text>\n ) : results.length ? (\n results.slice(0, 4).map((c) => (\n <View key={c.name} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ width: 34, height: 34, borderRadius: 17, backgroundColor: alpha(colors.accent, 0.16), alignItems: 'center', justifyContent: 'center' }}>\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{c.name.split(' ').map((p) => p[0]).join('')}</Text>\n </View>\n <View>\n <Text style={{ fontSize: 15, fontWeight: '400', color: colors.foreground }}>{c.name}</Text>\n <Text style={{ fontSize: 12.5, color: colors.mutedForeground }}>{c.role}</Text>\n </View>\n </View>\n ))\n ) : (\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>No one matches “{query}”</Text>\n )}\n </Card>\n );\n}\n"
|
|
142
|
+
"code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { SearchBar, type SearchSuggestion } from '@/components/ui/search-bar';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** Control text stops at iOS's largest standard size. */\nconst FONT_CAP = 1.35;\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { padding: 16, gap: 12 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : { borderRadius: radius.xl, borderWidth: StyleSheet.hairlineWidth, borderColor: colors.border, backgroundColor: colors.card },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nconst CONTACTS = [\n { name: 'Aarav Mehta', role: 'Design lead' },\n { name: 'Priya Nair', role: 'Product manager' },\n { name: 'Rohan Kapoor', role: 'iOS engineer' },\n { name: 'Sara Ali', role: 'Marketing' },\n { name: 'Vikram Rao', role: 'Backend engineer' },\n];\n\nexport default function LiveResults({ screen }: DemoProps) {\n const { colors } = useKit();\n const [query, setQuery] = useState('');\n const [loading, setLoading] = useState(false);\n useEffect(() => {\n if (!query) return setLoading(false);\n setLoading(true);\n const id = setTimeout(() => setLoading(false), 600);\n return () => clearTimeout(id);\n }, [query]);\n const q = query.trim().toLowerCase();\n const results = CONTACTS.filter((c) => c.name.toLowerCase().includes(q) || c.role.toLowerCase().includes(q));\n return (\n <Card screen={screen}>\n <SearchBar variant=\"filled\" value={query} onChangeText={setQuery} loading={loading} placeholder=\"Search people\" />\n {loading ? (\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>Searching your team…</Text>\n ) : results.length ? (\n results.slice(0, 4).map((c) => (\n <View key={c.name} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View style={{ width: 34, height: 34, borderRadius: 17, backgroundColor: alpha(colors.accent, 0.16), alignItems: 'center', justifyContent: 'center' }}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{c.name.split(' ').map((p) => p[0]).join('')}</Text>\n </View>\n <View style={{ flexShrink: 1 }}>\n <Text style={{ fontSize: 15, fontWeight: '400', color: colors.foreground }}>{c.name}</Text>\n <Text style={{ fontSize: 12.5, color: colors.mutedForeground }}>{c.role}</Text>\n </View>\n </View>\n ))\n ) : (\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>No one matches “{query}”</Text>\n )}\n </Card>\n );\n}\n"
|
|
143
143
|
},
|
|
144
144
|
{
|
|
145
145
|
"name": "Minimal",
|
|
146
146
|
"title": "Minimal",
|
|
147
|
-
"code": "import { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { SearchBar, type SearchSuggestion } from '@/components/ui/search-bar';\nimport { alpha, 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 { padding: 16, gap: 12, borderRadius: radius.xl, borderWidth: StyleSheet.hairlineWidth, borderColor: colors.border, backgroundColor: colors.card },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Minimal() {\n const { colors } = useKit();\n return (\n <Card style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <Text style={{ fontSize: 22, fontWeight: '400', letterSpacing: -0.4, color: colors.foreground }}>Inbox</Text>\n <SearchBar variant=\"minimal\" align=\"right\" placeholder=\"Search mail\" style={{ flex: 1 }} />\n </Card>\n );\n}\n"
|
|
147
|
+
"code": "import { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { SearchBar, type SearchSuggestion } from '@/components/ui/search-bar';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** From the Expo Go viewer: `screen` = this IS the phone screen, so no mock frame; `bottomInset` = home indicator. */\ntype DemoProps = { screen?: boolean; bottomInset?: number };\n\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n { padding: 16, gap: 12 },\n screen\n ? { flex: 1, backgroundColor: colors.background }\n : { borderRadius: radius.xl, borderWidth: StyleSheet.hairlineWidth, borderColor: colors.border, backgroundColor: colors.card },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Minimal({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <Text style={{ flexShrink: 1, fontSize: 22, fontWeight: '400', letterSpacing: -0.4, color: colors.foreground }}>Inbox</Text>\n <SearchBar variant=\"minimal\" align=\"right\" placeholder=\"Search mail\" style={{ flex: 1 }} />\n </Card>\n );\n}\n"
|
|
148
148
|
},
|
|
149
149
|
{
|
|
150
150
|
"name": "WithFilter",
|
|
151
151
|
"title": "With Filter",
|
|
152
|
-
"code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { SearchBar, type SearchSuggestion } from '@/components/ui/search-bar';\nimport { alpha, useKit } from '@/lib/kit';\n\nfunction FilterButton({ count, onPress }: { count: number; onPress: () => void }) {\n const { colors, radius } = useKit();\n return (\n <Pressable\n onPress={onPress}\n hitSlop={8}\n accessibilityRole=\"button\"\n accessibilityLabel={`Filters, ${count} active`}\n style={{
|
|
152
|
+
"code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type ViewProps } from 'react-native';\nimport { SearchBar, type SearchSuggestion } from '@/components/ui/search-bar';\nimport { alpha, useKit } from '@/lib/kit';\n\n/** Control text stops at iOS's largest standard size. */\nconst FONT_CAP = 1.35;\n\nfunction FilterButton({ count, onPress }: { count: number; onPress: () => void }) {\n const { colors, radius } = useKit();\n return (\n <Pressable\n onPress={onPress}\n hitSlop={8}\n accessibilityRole=\"button\"\n accessibilityLabel={`Filters, ${count} active`}\n style={{ minHeight: 32, paddingHorizontal: 10, borderRadius: radius.capsule(32), backgroundColor: count ? colors.accent : colors.muted, flexDirection: 'row', alignItems: 'center', gap: 6 }}\n >\n <View style={{ gap: 3, alignItems: 'center' }}>\n {[14, 10, 6].map((w) => (\n <View key={w} style={{ width: w, height: 2, borderRadius: 1, backgroundColor: count ? colors.accentForeground : colors.mutedForeground }} />\n ))}\n </View>\n {count ? <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 12.5, fontWeight: '400', color: colors.accentForeground }}>{count}</Text> : null}\n </Pressable>\n );\n}\n\nexport default function WithFilter() {\n const { colors } = useKit();\n const [count, setCount] = useState(2);\n return (\n <View style={{ gap: 10 }}>\n <SearchBar\n placeholder=\"Stays in Goa\"\n showCancel={false}\n trailing={<FilterButton count={count} onPress={() => setCount((c) => (c + 1) % 4)} />}\n />\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>{count ? `${count} filters · Pool, Under ₹8,000` : 'No filters applied'}</Text>\n </View>\n );\n}\n"
|
|
153
153
|
},
|
|
154
154
|
{
|
|
155
155
|
"name": "Sizes",
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
{
|
|
108
108
|
"name": "WithIcons",
|
|
109
109
|
"title": "With Icons",
|
|
110
|
-
"code": "import { Text, View } from 'react-native';\nimport { SegmentedControl } from '@/components/ui/segmented-control';\n\nexport default function WithIcons() {\n const glyph = (g: string) => ({ color }: { color: string }) => <Text style={{ fontSize: 15, color }}>{g}</Text>;\n return (\n <SegmentedControl\n defaultValue=\"grid\"\n options={[\n { value: 'list', label: 'List', icon: glyph('☰') },\n { value: 'grid', label: 'Grid', icon: glyph('▦') },\n { value: 'map', label: 'Map', icon: glyph('◎') },\n ]}\n />\n );\n}\n"
|
|
110
|
+
"code": "import { Text, View } from 'react-native';\nimport { SegmentedControl } from '@/components/ui/segmented-control';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\nexport default function WithIcons() {\n const glyph = (g: string) => ({ color }: { color: string }) => <Text maxFontSizeMultiplier={FONT_CAP} style={{ fontSize: 15, color }}>{g}</Text>;\n return (\n <SegmentedControl\n defaultValue=\"grid\"\n options={[\n { value: 'list', label: 'List', icon: glyph('☰') },\n { value: 'grid', label: 'Grid', icon: glyph('▦') },\n { value: 'map', label: 'Map', icon: glyph('◎') },\n ]}\n />\n );\n}\n"
|
|
111
111
|
},
|
|
112
112
|
{
|
|
113
113
|
"name": "Accent",
|
|
@@ -241,37 +241,37 @@
|
|
|
241
241
|
{
|
|
242
242
|
"name": "Validation",
|
|
243
243
|
"title": "Validation",
|
|
244
|
-
"code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, 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 {\n backgroundColor: colors.card,\n
|
|
244
|
+
"code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\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({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text maxFontSizeMultiplier={FONT_CAP} style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { minHeight: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Validation({ screen }: DemoProps) {\n const { colors, radius } = useKit();\n const [size, setSize] = useState<string | null>(null);\n const [touched, setTouched] = useState(false);\n const missing = touched && !size;\n return (\n <Card screen={screen}>\n <Heading title=\"Merino crew sweater\" sub=\"₹3,499 · Oatmeal\" />\n <Select\n label=\"Size\"\n floatingLabel={false}\n placeholder=\"Choose a size\"\n size=\"sm\"\n value={size}\n surface={colors.card}\n onValueChange={(v) => {\n setSize(v);\n setTouched(true);\n }}\n options={[\n { label: 'XS', value: 'XS', disabled: true, description: 'Sold out' },\n { label: 'S', value: 'S', description: 'Chest 34–36\"' },\n { label: 'M', value: 'M', description: 'Chest 38–40\"' },\n { label: 'L', value: 'L', description: 'Chest 42–44\"' },\n { label: 'XL', value: 'XL', description: 'Chest 46–48\"' },\n ]}\n error={missing ? 'Pick a size to add this to your bag' : undefined}\n helper={missing ? undefined : 'Runs true to size · free exchanges'}\n />\n <Pressable\n onPress={() => setTouched(true)}\n accessibilityRole=\"button\"\n style={({ pressed }) => [s.button, { backgroundColor: colors.primary, borderRadius: radius.md, opacity: pressed ? 0.88 : 1 }]}\n >\n <Text maxFontSizeMultiplier={FONT_CAP} style={[s.buttonText, { color: colors.primaryForeground }]}>Add to bag</Text>\n </Pressable>\n </Card>\n );\n}\n"
|
|
245
245
|
},
|
|
246
246
|
{
|
|
247
247
|
"name": "Plan",
|
|
248
248
|
"title": "Plan",
|
|
249
|
-
"code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport Svg, { Circle, Path } from 'react-native-svg';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, 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 {\n backgroundColor: colors.card,\n
|
|
249
|
+
"code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport Svg, { Circle, Path } from 'react-native-svg';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\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({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\n/** Small icon tile for option rows. */\nfunction Glyph({ d, tone }: { d: string; tone?: 'accent' | 'success' | 'warning' }) {\n const { colors, radius } = useKit();\n const c = tone ? colors[tone] : colors.mutedForeground;\n return (\n <View style={[s.glyph, { borderRadius: radius.scale(8), backgroundColor: tone ? alpha(c, 0.14) : colors.muted }]}>\n <Svg width={15} height={15} viewBox=\"0 0 24 24\">\n <Path d={d} stroke={c} strokeWidth={2} strokeLinecap=\"round\" strokeLinejoin=\"round\" fill=\"none\" />\n </Svg>\n </View>\n );\n}\n\nconst ICON = {\n leaf: 'M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.5 19 2c1 2 2 4.2 2 8 0 5.5-4.8 10-10 10ZM2 21c0-3 1.9-5.4 5.2-6',\n bolt: 'M13 2 3 14h9l-1 8 10-12h-9l1-8Z',\n users: 'M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8ZM22 21v-2a4 4 0 0 0-3-3.9M16 3.1a4 4 0 0 1 0 7.8',\n building: 'M6 22V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v18ZM6 12H4a2 2 0 0 0-2 2v8h4M18 9h2a2 2 0 0 1 2 2v11h-4M10 6h4M10 10h4M10 14h4M10 18h4',\n};\n\nconst flag = (f: string) => <Text maxFontSizeMultiplier={FONT_CAP} style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { minHeight: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Plan({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen}>\n <Heading title=\"Workspace plan\" sub=\"Billed monthly · change anytime\" />\n <Select\n label=\"Plan\"\n floatingLabel={false}\n placeholder=\"Select a plan\"\n size=\"sm\"\n defaultValue=\"pro\"\n surface={colors.card}\n helper=\"You can switch plans later from billing.\"\n options={[\n { label: 'Hobby', value: 'hobby', description: 'Free · 1 project', icon: <Glyph d={ICON.leaf} tone=\"success\" /> },\n { label: 'Pro', value: 'pro', description: '₹499 / seat · unlimited projects', icon: <Glyph d={ICON.bolt} tone=\"accent\" /> },\n { label: 'Team', value: 'team', description: '₹1,499 / seat · SSO and audit log', icon: <Glyph d={ICON.users} tone=\"warning\" /> },\n { label: 'Enterprise', value: 'enterprise', description: 'Talk to sales', icon: <Glyph d={ICON.building} />, disabled: true },\n ]}\n />\n </Card>\n );\n}\n"
|
|
250
250
|
},
|
|
251
251
|
{
|
|
252
252
|
"name": "Country",
|
|
253
253
|
"title": "Country",
|
|
254
|
-
"code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, 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 {\n backgroundColor: colors.card,\n
|
|
254
|
+
"code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\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({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text maxFontSizeMultiplier={FONT_CAP} style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { minHeight: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Country({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen}>\n <Heading title=\"Shipping address\" sub=\"Where should we send this order?\" />\n <Select\n searchable\n label=\"Country\"\n floatingLabel={false}\n placeholder=\"Select country\"\n searchPlaceholder=\"Search countries\"\n size=\"sm\"\n defaultValue=\"DE\"\n surface={colors.card}\n options={[\n { label: 'United States', value: 'US', icon: flag('🇺🇸'), group: 'Americas' },\n { label: 'Canada', value: 'CA', icon: flag('🇨🇦'), group: 'Americas' },\n { label: 'Brazil', value: 'BR', icon: flag('🇧🇷'), group: 'Americas' },\n { label: 'United Kingdom', value: 'GB', icon: flag('🇬🇧'), group: 'Europe' },\n { label: 'Germany', value: 'DE', icon: flag('🇩🇪'), group: 'Europe' },\n { label: 'France', value: 'FR', icon: flag('🇫🇷'), group: 'Europe' },\n { label: 'India', value: 'IN', icon: flag('🇮🇳'), group: 'Asia Pacific' },\n { label: 'Japan', value: 'JP', icon: flag('🇯🇵'), group: 'Asia Pacific' },\n { label: 'Australia', value: 'AU', icon: flag('🇦🇺'), group: 'Asia Pacific' },\n ]}\n />\n </Card>\n );\n}\n"
|
|
255
255
|
},
|
|
256
256
|
{
|
|
257
257
|
"name": "Assignees",
|
|
258
258
|
"title": "Assignees",
|
|
259
|
-
"code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, 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 {\n backgroundColor: colors.card,\n
|
|
259
|
+
"code": "import React, { useState } from 'react';\nimport { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\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({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text maxFontSizeMultiplier={FONT_CAP} style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { minHeight: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Assignees({ screen }: DemoProps) {\n const { colors } = useKit();\n const [people, setPeople] = useState(['maya', 'leo']);\n return (\n <Card screen={screen}>\n <Heading title=\"Fix checkout crash on Android 14\" sub=\"ENG-2142 · In progress\" />\n <Select\n multiple\n searchable\n label=\"Assignees\"\n floatingLabel={false}\n placeholder=\"Add teammates\"\n searchPlaceholder=\"Search teammates\"\n size=\"sm\"\n values={people}\n onValuesChange={setPeople}\n surface={colors.card}\n options={[\n { label: 'Maya Chen', value: 'maya', description: 'Design', icon: avatar('MC', '#f472b6') },\n { label: 'Leo Martins', value: 'leo', description: 'iOS', icon: avatar('LM', '#60a5fa') },\n { label: 'Ana Ruiz', value: 'ana', description: 'Android', icon: avatar('AR', '#34d399') },\n { label: 'Sam Okafor', value: 'sam', description: 'Backend', icon: avatar('SO', '#fbbf24') },\n { label: 'Priya Nair', value: 'priya', description: 'QA', icon: avatar('PN', '#a78bfa') },\n ]}\n />\n </Card>\n );\n}\n"
|
|
260
260
|
},
|
|
261
261
|
{
|
|
262
262
|
"name": "Sheet",
|
|
263
263
|
"title": "Sheet",
|
|
264
|
-
"code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, 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 {\n backgroundColor: colors.card,\n
|
|
264
|
+
"code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\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({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text maxFontSizeMultiplier={FONT_CAP} style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { minHeight: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Sheet({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen}>\n <Heading title=\"Notifications\" sub=\"Opens as a sheet instead of a dropdown\" />\n <Select\n presentation=\"sheet\"\n label=\"Email digest\"\n floatingLabel={false}\n placeholder=\"How often?\"\n title=\"How often?\"\n size=\"sm\"\n defaultValue=\"weekly\"\n surface={colors.card}\n options={[\n { label: 'Daily', value: 'daily', description: 'Every morning at 8:00' },\n { label: 'Weekly', value: 'weekly', description: 'Mondays at 8:00' },\n { label: 'Monthly', value: 'monthly', description: 'First of the month' },\n { label: 'Never', value: 'never', description: 'Only security alerts' },\n ]}\n />\n </Card>\n );\n}\n"
|
|
265
265
|
},
|
|
266
266
|
{
|
|
267
267
|
"name": "Sizes",
|
|
268
268
|
"title": "Sizes",
|
|
269
|
-
"code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, 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 {\n backgroundColor: colors.card,\n
|
|
269
|
+
"code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { Select } from '@/components/ui/select';\nimport { alpha, 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\n/** Demo card that follows the kit theme, so the docs radius / accent picker reshapes it too. */\nfunction Card({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\nexport default function Sizes({ screen }: DemoProps) {\n const { colors } = useKit();\n const opts = ['Daily', 'Weekly', 'Monthly'];\n return (\n <Card screen={screen} style={{ gap: 16 }}>\n <Select size=\"sm\" label=\"Small\" floatingLabel={false} placeholder=\"Pick one\" defaultValue=\"Weekly\" options={opts} surface={colors.card} />\n <Select size=\"md\" label=\"Medium\" floatingLabel={false} placeholder=\"Pick one\" defaultValue=\"Weekly\" options={opts} surface={colors.card} />\n <Select size=\"lg\" label=\"Large\" floatingLabel={false} placeholder=\"Pick one\" defaultValue=\"Weekly\" options={opts} surface={colors.card} />\n <Select disabled label=\"Disabled\" floatingLabel={false} placeholder=\"Unavailable\" defaultValue=\"Daily\" options={opts} surface={colors.card} />\n </Card>\n );\n}\n"
|
|
270
270
|
},
|
|
271
271
|
{
|
|
272
272
|
"name": "Colors",
|
|
273
273
|
"title": "Colors",
|
|
274
|
-
"code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport Svg, { Circle, Path } from 'react-native-svg';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, 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 {\n backgroundColor: colors.card,\n
|
|
274
|
+
"code": "import { Pressable, StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport Svg, { Circle, Path } from 'react-native-svg';\nimport { Select } from '@/components/ui/select';\nimport { alpha, readableOn, useKit } from '@/lib/kit';\n\n/** Controls stop at iOS's largest standard text size; content text stays uncapped. */\nconst FONT_CAP = 1.35;\n\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({ screen, style, ...rest }: ViewProps & { screen?: boolean }) {\n const { colors, radius } = useKit();\n return (\n <View\n style={[\n screen\n ? { flex: 1, backgroundColor: colors.background, padding: 18, gap: 14 }\n : {\n backgroundColor: colors.card,\n borderColor: colors.border,\n borderWidth: StyleSheet.hairlineWidth,\n borderRadius: radius.xl,\n padding: 18,\n gap: 14,\n },\n style,\n ]}\n {...rest}\n />\n );\n}\n\n/** Text in the kit's ink: `tone=\"muted\"` for secondary copy. */\nfunction Ink({ tone, style, ...rest }: TextProps & { tone?: 'muted' }) {\n const { colors } = useKit();\n return <Text style={[{ color: tone === 'muted' ? colors.mutedForeground : colors.foreground }, style]} {...rest} />;\n}\n\nfunction Heading({ title, sub }: { title: string; sub?: string }) {\n return (\n <View style={s.heading}>\n <Ink style={s.title}>{title}</Ink>\n {sub ? (\n <Ink tone=\"muted\" style={s.sub}>\n {sub}\n </Ink>\n ) : null}\n </View>\n );\n}\n\nconst flag = (f: string) => <Text maxFontSizeMultiplier={FONT_CAP} style={s.flag}>{f}</Text>;\n\nconst avatar = (initials: string, color: string) => (\n <View style={[s.avatar, { backgroundColor: color }]}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={[s.initials, { color: readableOn(color) }]}>{initials}</Text>\n </View>\n);\n\nfunction Dot({ color }: { color: string }) {\n return (\n <Svg width={14} height={14} viewBox=\"0 0 14 14\">\n <Circle cx={7} cy={7} r={5} fill={color} />\n </Svg>\n );\n}\n\nconst s = StyleSheet.create({\n heading: { gap: 3, marginBottom: 2 },\n title: { fontSize: 16, fontWeight: '400', letterSpacing: -0.2 },\n sub: { fontSize: 13, lineHeight: 18 },\n glyph: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },\n flag: { fontSize: 18 },\n avatar: { width: 26, height: 26, borderRadius: 13, alignItems: 'center', justifyContent: 'center' },\n initials: { fontSize: 10.5, fontWeight: '400' },\n button: { minHeight: 44, alignItems: 'center', justifyContent: 'center', marginTop: 2 },\n buttonText: { fontSize: 15, fontWeight: '400' },\n});\n\nexport default function Colors({ screen }: DemoProps) {\n const { colors } = useKit();\n return (\n <Card screen={screen}>\n <Heading title=\"Project labels\" sub=\"Accent colour drives focus, checks and chips\" />\n <Select\n multiple\n label=\"Labels\"\n floatingLabel={false}\n placeholder=\"Add labels\"\n size=\"sm\"\n accent={colors.success}\n defaultValues={['frontend', 'perf']}\n surface={colors.card}\n options={[\n { label: 'Frontend', value: 'frontend' },\n { label: 'Performance', value: 'perf' },\n { label: 'Accessibility', value: 'a11y' },\n { label: 'Docs', value: 'docs' },\n ]}\n />\n <Select\n label=\"Priority\"\n floatingLabel={false}\n placeholder=\"Set priority\"\n size=\"sm\"\n accent={colors.warning}\n defaultValue=\"p1\"\n surface={colors.card}\n options={[\n { label: 'Urgent', value: 'p0', icon: <Dot color={colors.danger} /> },\n { label: 'High', value: 'p1', icon: <Dot color={colors.warning} /> },\n { label: 'Medium', value: 'p2', icon: <Dot color={colors.accent} /> },\n { label: 'Low', value: 'p3', icon: <Dot color={colors.mutedForeground} /> },\n ]}\n />\n </Card>\n );\n}\n"
|
|
275
275
|
}
|
|
276
276
|
]
|
|
277
277
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "skeleton",
|
|
3
3
|
"category": "Feedback",
|
|
4
4
|
"aliases": [],
|
|
5
|
-
"exportName": "
|
|
5
|
+
"exportName": "Skeleton",
|
|
6
6
|
"files": [
|
|
7
7
|
"src/components/ui/skeleton.tsx"
|
|
8
8
|
],
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
{
|
|
142
142
|
"name": "LoadingSwap",
|
|
143
143
|
"title": "Loading Swap",
|
|
144
|
-
"code": "import React, { useEffect, useRef, useState } from 'react';\nimport { Text, View, type DimensionValue } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { Skeleton } from '@/components/ui/skeleton';\nimport { useKit } from '@/lib/kit';\n\nexport default function LoadingSwap() {\n const { colors, radius, text } = useKit();\n const [loading, setLoading] = useState(true);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const reload = () => {\n setLoading(true);\n if (timer.current) clearTimeout(timer.current);\n timer.current = setTimeout(() => setLoading(false), 1600);\n };\n useEffect(() => {\n timer.current = setTimeout(() => setLoading(false), 1600);\n return () => {\n if (timer.current) clearTimeout(timer.current);\n };\n }, []);\n return (\n <View style={{ gap: 16 }}>\n <Skeleton.Reveal\n loading={loading}\n skeleton={\n <View style={{ gap: 12 }}>\n <Skeleton.List rows={1} trailing={false} />\n <Skeleton.Text lines={2} />\n </View>\n }\n >\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12, paddingVertical: 10 }}>\n <View style={{ width: 44, height: 44, borderRadius: radius.full, backgroundColor: colors.accent, alignItems: 'center', justifyContent: 'center' }}>\n <Text style={{ color: colors.accentForeground, fontSize: 16, fontWeight: '400' }}>MC</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text style={[text.md, { color: colors.foreground, fontWeight: '400' }]}>Maya Chen</Text>\n <Text style={[text.sm, { color: colors.mutedForeground, fontWeight: '400' }]}>Product designer · Lisbon</Text>\n </View>\n </View>\n <Text style={[text.sm, { color: colors.foreground, fontWeight: '400', marginTop: 4 }]}>\n Shipped the new onboarding flow today. Activation is up 18% in the first week — thanks to everyone who tested the beta.\n </Text>\n <Text style={[text.xs, { color: colors.mutedForeground, fontWeight: '400', marginTop: 10 }]}>\n 124 likes · 18 comments\n </Text>\n </Skeleton.Reveal>\n <Button size=\"sm\" variant=\"outline\" style={{ alignSelf: 'flex-start' }} disabled={loading} onPress={reload}>\n {loading ? 'Loading…' : 'Reload'}\n </Button>\n </View>\n );\n}\n"
|
|
144
|
+
"code": "import React, { useEffect, useRef, useState } from 'react';\nimport { Text, View, type DimensionValue } from 'react-native';\nimport { Button } from '@/components/ui/button';\nimport { Skeleton } from '@/components/ui/skeleton';\nimport { useKit } from '@/lib/kit';\n\n/** Control text stops at iOS's largest standard size. */\nconst FONT_CAP = 1.35;\n\nexport default function LoadingSwap() {\n const { colors, radius, text } = useKit();\n const [loading, setLoading] = useState(true);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const reload = () => {\n setLoading(true);\n if (timer.current) clearTimeout(timer.current);\n timer.current = setTimeout(() => setLoading(false), 1600);\n };\n useEffect(() => {\n timer.current = setTimeout(() => setLoading(false), 1600);\n return () => {\n if (timer.current) clearTimeout(timer.current);\n };\n }, []);\n return (\n <View style={{ gap: 16 }}>\n <Skeleton.Reveal\n loading={loading}\n skeleton={\n <View style={{ gap: 12 }}>\n <Skeleton.List rows={1} trailing={false} />\n <Skeleton.Text lines={2} />\n </View>\n }\n >\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 12, paddingVertical: 10 }}>\n <View style={{ width: 44, height: 44, borderRadius: radius.full, backgroundColor: colors.accent, alignItems: 'center', justifyContent: 'center' }}>\n <Text maxFontSizeMultiplier={FONT_CAP} style={{ color: colors.accentForeground, fontSize: 16, fontWeight: '400' }}>MC</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text style={[text.md, { color: colors.foreground, fontWeight: '400' }]}>Maya Chen</Text>\n <Text style={[text.sm, { color: colors.mutedForeground, fontWeight: '400' }]}>Product designer · Lisbon</Text>\n </View>\n </View>\n <Text style={[text.sm, { color: colors.foreground, fontWeight: '400', marginTop: 4 }]}>\n Shipped the new onboarding flow today. Activation is up 18% in the first week — thanks to everyone who tested the beta.\n </Text>\n <Text style={[text.xs, { color: colors.mutedForeground, fontWeight: '400', marginTop: 10 }]}>\n 124 likes · 18 comments\n </Text>\n </Skeleton.Reveal>\n <Button size=\"sm\" variant=\"outline\" style={{ alignSelf: 'flex-start' }} disabled={loading} onPress={reload}>\n {loading ? 'Loading…' : 'Reload'}\n </Button>\n </View>\n );\n}\n"
|
|
145
145
|
},
|
|
146
146
|
{
|
|
147
147
|
"name": "Wave",
|
|
@@ -156,7 +156,7 @@
|
|
|
156
156
|
{
|
|
157
157
|
"name": "Nav",
|
|
158
158
|
"title": "Nav",
|
|
159
|
-
"code": "import { Skeleton } from '@/components/ui/skeleton';\n\nexport default function Nav() {\n return <Skeleton.Nav rows={6} tabCount={5} style={{ minHeight: 600 }} />;\n}\n"
|
|
159
|
+
"code": "import { Skeleton } from '@/components/ui/skeleton';\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 Nav({ screen }: DemoProps) {\n return <Skeleton.Nav rows={6} tabCount={5} style={screen ? undefined : { minHeight: 600 }} />;\n}\n"
|
|
160
160
|
}
|
|
161
161
|
]
|
|
162
162
|
}
|