@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
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "morph-button",
|
|
3
|
-
"category": "Buttons",
|
|
4
|
-
"aliases": [],
|
|
5
|
-
"exportName": "MorphButton",
|
|
6
|
-
"files": [
|
|
7
|
-
"src/components/ui/morph-button.tsx"
|
|
8
|
-
],
|
|
9
|
-
"summary": "An async button that shrinks to a spinner, then draws a tick or shakes a cross.",
|
|
10
|
-
"props": [
|
|
11
|
-
[
|
|
12
|
-
"onPress",
|
|
13
|
-
"() => Promise<unknown>",
|
|
14
|
-
"—",
|
|
15
|
-
"Async work. Resolve → tick, reject → cross + shake."
|
|
16
|
-
],
|
|
17
|
-
[
|
|
18
|
-
"variant",
|
|
19
|
-
"\"primary\" | \"accent\" | \"soft\" | \"outline\"",
|
|
20
|
-
"\"primary\"",
|
|
21
|
-
"Look while idle."
|
|
22
|
-
],
|
|
23
|
-
[
|
|
24
|
-
"size",
|
|
25
|
-
"\"sm\" | \"md\" | \"lg\"",
|
|
26
|
-
"\"md\"",
|
|
27
|
-
"40 / 48 / 56pt tall."
|
|
28
|
-
],
|
|
29
|
-
[
|
|
30
|
-
"color",
|
|
31
|
-
"string",
|
|
32
|
-
"theme accent",
|
|
33
|
-
"Brand colour for accent, soft and outline."
|
|
34
|
-
],
|
|
35
|
-
[
|
|
36
|
-
"successColor / errorColor",
|
|
37
|
-
"string",
|
|
38
|
-
"theme success / danger",
|
|
39
|
-
"Colours of the result circle."
|
|
40
|
-
],
|
|
41
|
-
[
|
|
42
|
-
"resetAfter",
|
|
43
|
-
"number",
|
|
44
|
-
"1300",
|
|
45
|
-
"ms the result stays before morphing back."
|
|
46
|
-
],
|
|
47
|
-
[
|
|
48
|
-
"minLoading",
|
|
49
|
-
"number",
|
|
50
|
-
"600",
|
|
51
|
-
"Minimum ms the spinner is shown."
|
|
52
|
-
],
|
|
53
|
-
[
|
|
54
|
-
"onSuccess / onError",
|
|
55
|
-
"(value) => void",
|
|
56
|
-
"—",
|
|
57
|
-
"Called with the resolved value or the thrown error."
|
|
58
|
-
],
|
|
59
|
-
[
|
|
60
|
-
"onStatusChange",
|
|
61
|
-
"(status) => void",
|
|
62
|
-
"—",
|
|
63
|
-
"\"idle\" | \"loading\" | \"success\" | \"error\"."
|
|
64
|
-
],
|
|
65
|
-
[
|
|
66
|
-
"pill",
|
|
67
|
-
"boolean",
|
|
68
|
-
"true",
|
|
69
|
-
"Fully rounded ends while idle."
|
|
70
|
-
],
|
|
71
|
-
[
|
|
72
|
-
"block",
|
|
73
|
-
"boolean",
|
|
74
|
-
"false",
|
|
75
|
-
"Full width; shrinks to the centre while busy."
|
|
76
|
-
],
|
|
77
|
-
[
|
|
78
|
-
"left",
|
|
79
|
-
"ReactNode",
|
|
80
|
-
"—",
|
|
81
|
-
"Icon before the label."
|
|
82
|
-
],
|
|
83
|
-
[
|
|
84
|
-
"haptics",
|
|
85
|
-
"boolean",
|
|
86
|
-
"true",
|
|
87
|
-
"Taps on start, success and error."
|
|
88
|
-
],
|
|
89
|
-
[
|
|
90
|
-
"disabled",
|
|
91
|
-
"boolean",
|
|
92
|
-
"false",
|
|
93
|
-
"Blocks presses."
|
|
94
|
-
],
|
|
95
|
-
[
|
|
96
|
-
"textStyle",
|
|
97
|
-
"StyleProp<TextStyle>",
|
|
98
|
-
"—",
|
|
99
|
-
"Style for the idle label text."
|
|
100
|
-
],
|
|
101
|
-
[
|
|
102
|
-
"style",
|
|
103
|
-
"StyleProp<ViewStyle>",
|
|
104
|
-
"—",
|
|
105
|
-
"Style for the outer animated wrapper (margins, alignment)."
|
|
106
|
-
]
|
|
107
|
-
],
|
|
108
|
-
"behavior": [
|
|
109
|
-
[
|
|
110
|
-
"Motion",
|
|
111
|
-
"While the promise runs the button springs into a circle with a spinner. A resolve draws a success tick, a reject shakes with an error cross, then it springs back to its label."
|
|
112
|
-
],
|
|
113
|
-
[
|
|
114
|
-
"Touch",
|
|
115
|
-
"Presses are ignored while it is busy, and the spinner shows for at least minLoading so fast requests still register."
|
|
116
|
-
],
|
|
117
|
-
[
|
|
118
|
-
"Haptics",
|
|
119
|
-
"A light impact on start, then a success or error notification when the promise settles."
|
|
120
|
-
],
|
|
121
|
-
[
|
|
122
|
-
"Screen readers",
|
|
123
|
-
"A button with busy and disabled states; while not idle its value reads \"Working\", \"Done\" or \"Failed\"."
|
|
124
|
-
]
|
|
125
|
-
],
|
|
126
|
-
"usage": null,
|
|
127
|
-
"examples": []
|
|
128
|
-
}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "pagination-dots",
|
|
3
|
-
"category": "Display",
|
|
4
|
-
"aliases": [],
|
|
5
|
-
"exportName": "PaginationDots",
|
|
6
|
-
"files": [
|
|
7
|
-
"src/components/ui/pagination-dots.tsx"
|
|
8
|
-
],
|
|
9
|
-
"summary": "The active dot stretches into a pill that follows your swipe live.",
|
|
10
|
-
"props": [
|
|
11
|
-
[
|
|
12
|
-
"count",
|
|
13
|
-
"number",
|
|
14
|
-
"—",
|
|
15
|
-
"Number of pages."
|
|
16
|
-
],
|
|
17
|
-
[
|
|
18
|
-
"index",
|
|
19
|
-
"number",
|
|
20
|
-
"0",
|
|
21
|
-
"Active page; changes spring the indicator across."
|
|
22
|
-
],
|
|
23
|
-
[
|
|
24
|
-
"progress",
|
|
25
|
-
"Animated.Value | AnimatedInterpolation | number",
|
|
26
|
-
"—",
|
|
27
|
-
"Live page position (e.g. Animated.divide(scrollX, width)) for smooth tracking while swiping. Use useNativeDriver: false for the 'dots' variant."
|
|
28
|
-
],
|
|
29
|
-
[
|
|
30
|
-
"variant",
|
|
31
|
-
"\"dots\" | \"scale\" | \"line\" | \"numbers\"",
|
|
32
|
-
"\"dots\"",
|
|
33
|
-
"Stretching pill · growing dot · filling segments · rolling \"2 / 5\"."
|
|
34
|
-
],
|
|
35
|
-
[
|
|
36
|
-
"size",
|
|
37
|
-
"\"sm\" | \"md\" | \"lg\"",
|
|
38
|
-
"\"md\"",
|
|
39
|
-
"6 / 8 / 10pt dots."
|
|
40
|
-
],
|
|
41
|
-
[
|
|
42
|
-
"onPress",
|
|
43
|
-
"(index: number) => void",
|
|
44
|
-
"—",
|
|
45
|
-
"Makes dots tappable (44pt hit area) and enables accessibility increment / decrement."
|
|
46
|
-
],
|
|
47
|
-
[
|
|
48
|
-
"accent",
|
|
49
|
-
"string",
|
|
50
|
-
"theme accent",
|
|
51
|
-
"Active colour."
|
|
52
|
-
],
|
|
53
|
-
[
|
|
54
|
-
"inactiveColor",
|
|
55
|
-
"string",
|
|
56
|
-
"18% foreground",
|
|
57
|
-
"Inactive dot colour."
|
|
58
|
-
],
|
|
59
|
-
[
|
|
60
|
-
"accessibilityLabel",
|
|
61
|
-
"string",
|
|
62
|
-
"\"Page\"",
|
|
63
|
-
"Announced with \"2 of 5\"."
|
|
64
|
-
],
|
|
65
|
-
[
|
|
66
|
-
"style",
|
|
67
|
-
"StyleProp<ViewStyle>",
|
|
68
|
-
"—",
|
|
69
|
-
"Style for the row of dots (the numbers pill or line track in those variants)."
|
|
70
|
-
]
|
|
71
|
-
],
|
|
72
|
-
"behavior": [
|
|
73
|
-
[
|
|
74
|
-
"Motion",
|
|
75
|
-
"Pass a live progress value (scroll offset ÷ page width) and the indicator tracks the finger; with index alone it springs across. Variants stretch a pill, scale a dot, fill line segments or roll the page number."
|
|
76
|
-
],
|
|
77
|
-
[
|
|
78
|
-
"Touch",
|
|
79
|
-
"With onPress, each dot becomes a button with an enlarged hit area that reports its page."
|
|
80
|
-
],
|
|
81
|
-
[
|
|
82
|
-
"Screen readers",
|
|
83
|
-
"Read as one element with 'N of M'. With onPress it is adjustable, so swiping up or down changes the page."
|
|
84
|
-
],
|
|
85
|
-
[
|
|
86
|
-
"Platforms",
|
|
87
|
-
"The 'dots' variant animates widths, so drive its progress with useNativeDriver: false. The other variants work with either driver."
|
|
88
|
-
]
|
|
89
|
-
],
|
|
90
|
-
"usage": "import { useState } from 'react';\nimport { PaginationDots } from '@/components/ui/pagination-dots';\n\nconst [page, setPage] = useState(0);\n<PaginationDots count={4} index={page} onPress={setPage} />",
|
|
91
|
-
"examples": [
|
|
92
|
-
{
|
|
93
|
-
"name": "Onboarding",
|
|
94
|
-
"title": "Onboarding",
|
|
95
|
-
"code": "import React, { useMemo, useRef, useState } from 'react';\nimport { Animated, Pressable, ScrollView, Text, View } from 'react-native';\nimport { PaginationDots } from '@/components/ui/pagination-dots';\nimport { useKit } from '@/lib/kit';\n\nconst PAGES: { color?: string; kicker: string; title: string; body: string }[] = [\n { kicker: 'PLAN', title: 'Plan your week in minutes', body: 'Drag tasks onto your calendar and we block the time for you.' },\n { color: '#0ea5e9', kicker: 'FOCUS', title: 'Stay in the zone', body: 'Focus sessions silence notifications until your block ends.' },\n { color: '#10b981', kicker: 'REVIEW', title: 'See what moved', body: 'A Friday recap shows what you shipped and what slipped.' },\n { color: '#f59e0b', kicker: 'SHARE', title: 'Bring your team along', body: 'Shared boards keep everyone on the same page.' },\n];\n\nexport default function Onboarding() {\n const { colors, radius } = useKit();\n const [width, setWidth] = useState(0);\n const [page, setPage] = useState(0);\n const scrollX = useRef(new Animated.Value(0)).current;\n const ref = useRef<ScrollView>(null);\n const progress = useMemo(() => (width ? Animated.divide(scrollX, width) : 0), [scrollX, width]);\n const go = (i: number) => ref.current?.scrollTo({ x: i * width, animated: true });\n\n return (\n <View style={{ gap: 18 }} onLayout={(e) => setWidth(e.nativeEvent.layout.width)}>\n <Animated.ScrollView\n ref={ref}\n horizontal\n pagingEnabled\n showsHorizontalScrollIndicator={false}\n scrollEventThrottle={16}\n onScroll={Animated.event([{ nativeEvent: { contentOffset: { x: scrollX } } }], { useNativeDriver: false })}\n onMomentumScrollEnd={(e) => width && setPage(Math.round(e.nativeEvent.contentOffset.x / width))}\n >\n {PAGES.map((p) => (\n <View key={p.title} style={{ width: width || 1, paddingHorizontal: 2 }}>\n <View style={{ height: 210, borderRadius: radius.scale(24), backgroundColor: p.color ?? colors.accent, padding: 22, justifyContent: 'flex-end' }}>\n <Text style={{ color: 'rgba(255,255,255,0.75)', fontSize: 12, fontWeight: '400', letterSpacing: 1.2 }}>{p.kicker}</Text>\n <Text style={{ color: '#fff', fontSize: 24, fontWeight: '400', letterSpacing: -0.5, marginTop: 6 }}>{p.title}</Text>\n <Text style={{ color: 'rgba(255,255,255,0.85)', fontSize: 14, lineHeight: 20, marginTop: 6 }}>{p.body}</Text>\n </View>\n </View>\n ))}\n </Animated.ScrollView>\n <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>\n <Pressable onPress={() => go(PAGES.length - 1)} hitSlop={12} accessibilityRole=\"button\">\n <Text style={{ fontSize: 15, fontWeight: '400', color: colors.mutedForeground }}>Skip</Text>\n </Pressable>\n <PaginationDots count={PAGES.length} progress={progress} onPress={go} accessibilityLabel=\"Onboarding page\" />\n <Pressable\n onPress={() => go(Math.min(PAGES.length - 1, page + 1))}\n accessibilityRole=\"button\"\n style={{ height: 44, paddingHorizontal: 18, borderRadius: radius.capsule(44), backgroundColor: colors.accent, justifyContent: 'center' }}\n >\n <Text style={{ fontSize: 15, fontWeight: '400', color: colors.accentForeground }}>{page === PAGES.length - 1 ? 'Done' : 'Next'}</Text>\n </Pressable>\n </View>\n </View>\n );\n}\n"
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
"name": "Variants",
|
|
99
|
-
"title": "Variants",
|
|
100
|
-
"code": "import React, { useMemo, useRef, useState } from 'react';\nimport { Animated, Pressable, ScrollView, Text, View } from 'react-native';\nimport { PaginationDots } from '@/components/ui/pagination-dots';\nimport { useKit } from '@/lib/kit';\n\nfunction Label({ children }: { children: string }) {\n const { colors } = useKit();\n return <Text style={{ fontSize: 12, fontWeight: '400', letterSpacing: 0.4, color: colors.mutedForeground }}>{children}</Text>;\n}\n\nexport default function Variants() {\n const { colors, radius } = useKit();\n const [i, setI] = useState(1);\n return (\n <View style={{ gap: 22 }}>\n {(['dots', 'scale', 'line', 'numbers'] as const).map((variant) => (\n <View key={variant} style={{ gap: 10 }}>\n <Label>{variant.toUpperCase()}</Label>\n <PaginationDots variant={variant} count={5} index={i} onPress={setI} />\n </View>\n ))}\n <View style={{ flexDirection: 'row', gap: 10, justifyContent: 'center' }}>\n {['Previous', 'Next'].map((t, k) => (\n <Pressable\n key={t}\n onPress={() => setI((v) => Math.max(0, Math.min(4, v + (k ? 1 : -1))))}\n accessibilityRole=\"button\"\n style={{ height: 44, paddingHorizontal: 18, borderRadius: radius.md, borderWidth: 1, borderColor: colors.border, justifyContent: 'center' }}\n >\n <Text style={{ fontSize: 14, fontWeight: '400', color: colors.foreground }}>{t}</Text>\n </Pressable>\n ))}\n </View>\n </View>\n );\n}\n"
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
"name": "Sizes",
|
|
104
|
-
"title": "Sizes",
|
|
105
|
-
"code": "import { Animated, Pressable, ScrollView, Text, View } from 'react-native';\nimport { PaginationDots } from '@/components/ui/pagination-dots';\n\nexport default function Sizes() {\n return (\n <View style={{ gap: 18, alignItems: 'center' }}>\n <PaginationDots size=\"sm\" count={6} index={2} />\n <PaginationDots size=\"md\" count={6} index={2} />\n <PaginationDots size=\"lg\" count={6} index={2} />\n </View>\n );\n}\n"
|
|
106
|
-
}
|
|
107
|
-
]
|
|
108
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "shimmer-button",
|
|
3
|
-
"category": "Buttons",
|
|
4
|
-
"aliases": [],
|
|
5
|
-
"exportName": "ShimmerButton",
|
|
6
|
-
"files": [
|
|
7
|
-
"src/components/ui/shimmer-button.tsx"
|
|
8
|
-
],
|
|
9
|
-
"summary": "A call-to-action with a looping light sweep, breathing glow or orbiting beam.",
|
|
10
|
-
"props": [
|
|
11
|
-
[
|
|
12
|
-
"variant",
|
|
13
|
-
"\"shimmer\" | \"glow\" | \"border-beam\"",
|
|
14
|
-
"\"shimmer\"",
|
|
15
|
-
"Light sweep, breathing halo or orbiting border beam."
|
|
16
|
-
],
|
|
17
|
-
[
|
|
18
|
-
"size",
|
|
19
|
-
"\"sm\" | \"md\" | \"lg\"",
|
|
20
|
-
"\"md\"",
|
|
21
|
-
"40 / 50 / 58pt tall."
|
|
22
|
-
],
|
|
23
|
-
[
|
|
24
|
-
"color",
|
|
25
|
-
"string",
|
|
26
|
-
"primary / accent",
|
|
27
|
-
"Button colour (beam colour for border-beam)."
|
|
28
|
-
],
|
|
29
|
-
[
|
|
30
|
-
"textColor",
|
|
31
|
-
"string",
|
|
32
|
-
"readable on color",
|
|
33
|
-
"Label colour."
|
|
34
|
-
],
|
|
35
|
-
[
|
|
36
|
-
"speed",
|
|
37
|
-
"number",
|
|
38
|
-
"2600 / 2400 / 3200",
|
|
39
|
-
"Loop length in ms."
|
|
40
|
-
],
|
|
41
|
-
[
|
|
42
|
-
"pill",
|
|
43
|
-
"boolean",
|
|
44
|
-
"true",
|
|
45
|
-
"Fully rounded ends."
|
|
46
|
-
],
|
|
47
|
-
[
|
|
48
|
-
"block",
|
|
49
|
-
"boolean",
|
|
50
|
-
"false",
|
|
51
|
-
"Stretches to the parent's width (alignSelf: stretch)."
|
|
52
|
-
],
|
|
53
|
-
[
|
|
54
|
-
"left / right",
|
|
55
|
-
"ReactNode",
|
|
56
|
-
"—",
|
|
57
|
-
"Icons beside the label."
|
|
58
|
-
],
|
|
59
|
-
[
|
|
60
|
-
"textStyle",
|
|
61
|
-
"StyleProp<TextStyle>",
|
|
62
|
-
"—",
|
|
63
|
-
"Style for the label text."
|
|
64
|
-
],
|
|
65
|
-
[
|
|
66
|
-
"style",
|
|
67
|
-
"StyleProp<ViewStyle>",
|
|
68
|
-
"—",
|
|
69
|
-
"Style for the outer animated wrapper (margins, alignment)."
|
|
70
|
-
],
|
|
71
|
-
[
|
|
72
|
-
"…PressableProps",
|
|
73
|
-
"",
|
|
74
|
-
"",
|
|
75
|
-
"onPress, disabled, …"
|
|
76
|
-
]
|
|
77
|
-
],
|
|
78
|
-
"behavior": [
|
|
79
|
-
[
|
|
80
|
-
"Motion",
|
|
81
|
-
"'shimmer' sweeps a light band across on a loop, 'glow' breathes a soft halo and 'border-beam' sends a bright comet around the border. speed sets the loop length."
|
|
82
|
-
],
|
|
83
|
-
[
|
|
84
|
-
"Touch",
|
|
85
|
-
"Pressing scales the button down on a bouncy spring."
|
|
86
|
-
],
|
|
87
|
-
[
|
|
88
|
-
"Reduced motion",
|
|
89
|
-
"With Reduce Motion on, or while disabled, the loop stops and the button rests in a still state."
|
|
90
|
-
],
|
|
91
|
-
[
|
|
92
|
-
"Screen readers",
|
|
93
|
-
"A button with its disabled state. Other Pressable props, such as accessibilityLabel, pass through."
|
|
94
|
-
]
|
|
95
|
-
],
|
|
96
|
-
"usage": null,
|
|
97
|
-
"examples": []
|
|
98
|
-
}
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "tag-input",
|
|
3
|
-
"category": "Inputs",
|
|
4
|
-
"aliases": [],
|
|
5
|
-
"exportName": "TagInput",
|
|
6
|
-
"files": [
|
|
7
|
-
"src/components/ui/tag-input.tsx"
|
|
8
|
-
],
|
|
9
|
-
"summary": "Chips that spring in on Enter or comma and shrink away as neighbours slide over.",
|
|
10
|
-
"props": [
|
|
11
|
-
[
|
|
12
|
-
"value / defaultValue",
|
|
13
|
-
"string[]",
|
|
14
|
-
"[]",
|
|
15
|
-
"Controlled or uncontrolled tags."
|
|
16
|
-
],
|
|
17
|
-
[
|
|
18
|
-
"onChange",
|
|
19
|
-
"(tags) => void",
|
|
20
|
-
"—",
|
|
21
|
-
"Called when a tag is added or removed (the removed chip then shrinks out)."
|
|
22
|
-
],
|
|
23
|
-
[
|
|
24
|
-
"variant",
|
|
25
|
-
"\"outlined\" | \"filled\"",
|
|
26
|
-
"\"outlined\"",
|
|
27
|
-
"Field style."
|
|
28
|
-
],
|
|
29
|
-
[
|
|
30
|
-
"chipVariant",
|
|
31
|
-
"\"soft\" | \"solid\" | \"outline\"",
|
|
32
|
-
"\"soft\"",
|
|
33
|
-
"Tinted accent chips, filled accent chips, or bordered chips with an accent dot."
|
|
34
|
-
],
|
|
35
|
-
[
|
|
36
|
-
"colorize",
|
|
37
|
-
"boolean",
|
|
38
|
-
"false",
|
|
39
|
-
"Tint each chip from a hash of its label."
|
|
40
|
-
],
|
|
41
|
-
[
|
|
42
|
-
"prefix",
|
|
43
|
-
"string",
|
|
44
|
-
"—",
|
|
45
|
-
"Shown before every chip label (e.g. \"#\"); typed copies are stripped."
|
|
46
|
-
],
|
|
47
|
-
[
|
|
48
|
-
"transform",
|
|
49
|
-
"(tag) => string",
|
|
50
|
-
"—",
|
|
51
|
-
"Normalise a tag before validation (lowercase, strip spaces…)."
|
|
52
|
-
],
|
|
53
|
-
[
|
|
54
|
-
"maxTags",
|
|
55
|
-
"number",
|
|
56
|
-
"—",
|
|
57
|
-
"Limit; shows a filling ring and \"3/5\" counter, and locks the input when reached."
|
|
58
|
-
],
|
|
59
|
-
[
|
|
60
|
-
"suggestions",
|
|
61
|
-
"string[]",
|
|
62
|
-
"—",
|
|
63
|
-
"Pills under the empty field; while typing, a list with the match highlighted and an \"Add …\" row."
|
|
64
|
-
],
|
|
65
|
-
[
|
|
66
|
-
"separators",
|
|
67
|
-
"string[]",
|
|
68
|
-
"[\",\"]",
|
|
69
|
-
"Characters that commit a tag (Enter always does)."
|
|
70
|
-
],
|
|
71
|
-
[
|
|
72
|
-
"validate",
|
|
73
|
-
"(tag, tags) => boolean | string",
|
|
74
|
-
"—",
|
|
75
|
-
"Reject a tag (the field shakes); return a string to show it as the error."
|
|
76
|
-
],
|
|
77
|
-
[
|
|
78
|
-
"error / helper",
|
|
79
|
-
"string",
|
|
80
|
-
"—",
|
|
81
|
-
"Message under the field; errors shake once."
|
|
82
|
-
],
|
|
83
|
-
[
|
|
84
|
-
"accent",
|
|
85
|
-
"string",
|
|
86
|
-
"theme accent",
|
|
87
|
-
"Focus and chip colour."
|
|
88
|
-
],
|
|
89
|
-
[
|
|
90
|
-
"style",
|
|
91
|
-
"StyleProp<ViewStyle>",
|
|
92
|
-
"—",
|
|
93
|
-
"Style for the outer wrapper around the field, suggestions and message (the one that shakes)."
|
|
94
|
-
],
|
|
95
|
-
[
|
|
96
|
-
"…TextInputProps",
|
|
97
|
-
"",
|
|
98
|
-
"",
|
|
99
|
-
"placeholder, keyboardType, …"
|
|
100
|
-
]
|
|
101
|
-
],
|
|
102
|
-
"behavior": [
|
|
103
|
-
[
|
|
104
|
-
"Keyboard",
|
|
105
|
-
"Enter or a separator commits the text as a chip. Backspace in an empty field highlights the last chip, and a second press removes it."
|
|
106
|
-
],
|
|
107
|
-
[
|
|
108
|
-
"Motion",
|
|
109
|
-
"Chips pop in on a spring and shrink out on removal; duplicates bump and a rejected tag shakes the field."
|
|
110
|
-
],
|
|
111
|
-
[
|
|
112
|
-
"Screen readers",
|
|
113
|
-
"Each chip has a 'Remove' button and each suggestion an 'Add' button; error messages land in a polite live region and the counter reads '3 of 5 tags'."
|
|
114
|
-
],
|
|
115
|
-
[
|
|
116
|
-
"Web",
|
|
117
|
-
"The browser focus outline on the input is removed; the field draws its own focus state."
|
|
118
|
-
]
|
|
119
|
-
],
|
|
120
|
-
"usage": "import { useState } from 'react';\nimport { TagInput } from '@/components/ui/tag-input';\n\nconst [skills, setSkills] = useState(['TypeScript']);\n<TagInput value={skills} onChange={setSkills} placeholder=\"Add a skill…\" suggestions={['React', 'Swift', 'Kotlin']} />",
|
|
121
|
-
"examples": [
|
|
122
|
-
{
|
|
123
|
-
"name": "Skills",
|
|
124
|
-
"title": "Skills",
|
|
125
|
-
"code": "import React, { useState } from 'react';\nimport { StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { TagInput } from '@/components/ui/tag-input';\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 s.card,\n { borderRadius: radius.xl, borderColor: colors.border, backgroundColor: colors.card },\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\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n title: { fontSize: 15, fontWeight: '400' },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -6 },\n});\n\nexport default function Skills() {\n return (\n <Card>\n <Ink style={s.title}>Your skills</Ink>\n <TagInput\n placeholder=\"Add a skill…\"\n defaultValue={['TypeScript', 'React Native']}\n suggestions={['React', 'Swift', 'SwiftUI', 'Kotlin', 'GraphQL', 'Figma', 'Node.js', 'Rust', 'PostgreSQL']}\n helper=\"Type to search, or tap a suggestion.\"\n />\n </Card>\n );\n}\n"
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
"name": "InviteByEmail",
|
|
129
|
-
"title": "Invite By Email",
|
|
130
|
-
"code": "import React, { useState } from 'react';\nimport { StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { TagInput } from '@/components/ui/tag-input';\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 s.card,\n { borderRadius: radius.xl, borderColor: colors.border, backgroundColor: colors.card },\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\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n title: { fontSize: 15, fontWeight: '400' },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -6 },\n});\n\nexport default function InviteByEmail() {\n const [emails, setEmails] = useState(['maya@studio.co', 'leo@studio.co']);\n return (\n <Card>\n <Ink style={s.title}>Invite teammates</Ink>\n <TagInput\n value={emails}\n onChange={setEmails}\n validate={(tag) => /^[^\\s@]+@[^\\s@]+\\.[^\\s@]{2,}$/.test(tag) || `“${tag}” isn't a valid email address`}\n transform={(tag) => tag.toLowerCase()}\n chipVariant=\"outline\"\n keyboardType=\"email-address\"\n placeholder=\"name@company.com\"\n helper=\"Separate addresses with a comma or space.\"\n separators={[',', ' ']}\n />\n </Card>\n );\n}\n"
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
"name": "Topics",
|
|
134
|
-
"title": "Topics",
|
|
135
|
-
"code": "import { StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { TagInput } from '@/components/ui/tag-input';\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 s.card,\n { borderRadius: radius.xl, borderColor: colors.border, backgroundColor: colors.card },\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\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n title: { fontSize: 15, fontWeight: '400' },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -6 },\n});\n\nexport default function Topics() {\n return (\n <Card>\n <Ink style={s.title}>Follow topics</Ink>\n <TagInput\n colorize\n maxTags={5}\n defaultValue={['Design', 'Climate', 'Startups']}\n suggestions={['Space', 'AI', 'Music', 'Cooking', 'Photography', 'Travel']}\n placeholder=\"Add a topic…\"\n helper=\"Pick up to five for your feed.\"\n />\n </Card>\n );\n}\n"
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
"name": "Hashtags",
|
|
139
|
-
"title": "Hashtags",
|
|
140
|
-
"code": "import { StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { TagInput } from '@/components/ui/tag-input';\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 s.card,\n { borderRadius: radius.xl, borderColor: colors.border, backgroundColor: colors.card },\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\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n title: { fontSize: 15, fontWeight: '400' },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -6 },\n});\n\nexport default function Hashtags() {\n return (\n <Card>\n <Ink style={s.title}>New post</Ink>\n <TagInput\n variant=\"filled\"\n chipVariant=\"solid\"\n prefix=\"#\"\n transform={(tag) => tag.replace(/\\s+/g, '').toLowerCase()}\n separators={[',', ' ']}\n defaultValue={['sunset', 'lisbon']}\n suggestions={['travel', 'goldenhour', 'streetphotography', 'portugal']}\n placeholder=\"#hashtag\"\n helper=\"Backspace once to highlight the last tag, twice to remove it.\"\n />\n </Card>\n );\n}\n"
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
"name": "ChipStyles",
|
|
144
|
-
"title": "Chip Styles",
|
|
145
|
-
"code": "import { StyleSheet, Text, View, type TextProps, type ViewProps } from 'react-native';\nimport { TagInput } from '@/components/ui/tag-input';\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 s.card,\n { borderRadius: radius.xl, borderColor: colors.border, backgroundColor: colors.card },\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\nconst s = StyleSheet.create({\n card: { padding: 16, borderWidth: StyleSheet.hairlineWidth, gap: 12 },\n title: { fontSize: 15, fontWeight: '400' },\n label: { fontSize: 12, fontWeight: '400', letterSpacing: 0.4, textTransform: 'uppercase', marginBottom: -6 },\n});\n\nexport default function ChipStyles() {\n return (\n <Card>\n <Ink tone=\"muted\" style={s.label}>Soft</Ink>\n <TagInput defaultValue={['Vegan', 'Gluten-free']} placeholder=\"Dietary needs\" />\n <Ink tone=\"muted\" style={s.label}>Solid</Ink>\n <TagInput chipVariant=\"solid\" defaultValue={['Urgent', 'Bug']} placeholder=\"Labels\" />\n <Ink tone=\"muted\" style={s.label}>Outline</Ink>\n <TagInput chipVariant=\"outline\" defaultValue={['Remote', 'Full-time']} placeholder=\"Job filters\" />\n <Ink tone=\"muted\" style={s.label}>Disabled</Ink>\n <TagInput editable={false} defaultValue={['Read-only']} />\n </Card>\n );\n}\n"
|
|
146
|
-
}
|
|
147
|
-
]
|
|
148
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "color-swatch",
|
|
3
|
-
"type": "component",
|
|
4
|
-
"title": "Color Swatch",
|
|
5
|
-
"description": "A colour picker row or grid. The chosen swatch settles inward while a ring springs out around it and a check pops in, in whichever colour reads best on top.",
|
|
6
|
-
"tier": "pro",
|
|
7
|
-
"dependencies": [
|
|
8
|
-
"react-native-svg"
|
|
9
|
-
],
|
|
10
|
-
"registryDependencies": [
|
|
11
|
-
"kit"
|
|
12
|
-
],
|
|
13
|
-
"sharedFiles": [
|
|
14
|
-
"lib/kit.tsx"
|
|
15
|
-
],
|
|
16
|
-
"filesDir": null
|
|
17
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { StyleSheet, Text, View, type StyleProp, type ViewStyle } from "react-native";
|
|
2
|
-
import { Sparkline, useChartTheme } from "expo-charts";
|
|
3
|
-
|
|
4
|
-
export interface KpiCardProps {
|
|
5
|
-
label: string;
|
|
6
|
-
value: string;
|
|
7
|
-
/** Percentage change, e.g. 12.4 or -3.1. */
|
|
8
|
-
delta?: number;
|
|
9
|
-
/** Recent values for the sparkline (oldest first). */
|
|
10
|
-
trend?: number[];
|
|
11
|
-
style?: StyleProp<ViewStyle>;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/** Big number + trend badge + sparkline. Colours follow <ChartProvider>. */
|
|
15
|
-
export function KpiCard({ label, value, delta, trend, style }: KpiCardProps) {
|
|
16
|
-
const theme = useChartTheme();
|
|
17
|
-
const up = (delta ?? 0) >= 0;
|
|
18
|
-
const tone = up ? theme.positive : theme.negative;
|
|
19
|
-
return (
|
|
20
|
-
<View style={[styles.card, { backgroundColor: theme.card, borderColor: theme.border }, style]}>
|
|
21
|
-
<View style={styles.row}>
|
|
22
|
-
<Text style={[styles.label, { color: theme.mutedForeground }]}>{label}</Text>
|
|
23
|
-
{delta !== undefined ? (
|
|
24
|
-
<View style={[styles.badge, { borderColor: tone }]}>
|
|
25
|
-
<Text style={[styles.badgeText, { color: tone }]}>
|
|
26
|
-
{up ? "+" : ""}
|
|
27
|
-
{delta.toFixed(1)}%
|
|
28
|
-
</Text>
|
|
29
|
-
</View>
|
|
30
|
-
) : null}
|
|
31
|
-
</View>
|
|
32
|
-
<Text style={[styles.value, { color: theme.foreground }]}>{value}</Text>
|
|
33
|
-
{trend && trend.length > 1 ? <Sparkline data={trend} height={40} color={tone} /> : null}
|
|
34
|
-
</View>
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const styles = StyleSheet.create({
|
|
39
|
-
card: { borderRadius: 16, borderWidth: StyleSheet.hairlineWidth, padding: 16, gap: 6 },
|
|
40
|
-
row: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" },
|
|
41
|
-
label: { fontSize: 13, fontWeight: "400" },
|
|
42
|
-
badge: { borderWidth: StyleSheet.hairlineWidth, borderRadius: 999, paddingHorizontal: 8, paddingVertical: 2 },
|
|
43
|
-
badgeText: { fontSize: 11, fontWeight: "400", fontVariant: ["tabular-nums"] },
|
|
44
|
-
value: { fontSize: 28, fontWeight: "400", letterSpacing: -0.5, fontVariant: ["tabular-nums"] },
|
|
45
|
-
});
|
package/registry/kpi-card.json
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "kpi-card",
|
|
3
|
-
"type": "chart",
|
|
4
|
-
"title": "KPI card",
|
|
5
|
-
"description": "Big number, trend badge and an animated sparkline. Built on expo-charts.",
|
|
6
|
-
"tier": "free",
|
|
7
|
-
"dependencies": [
|
|
8
|
-
"expo-charts",
|
|
9
|
-
"react-native-svg"
|
|
10
|
-
],
|
|
11
|
-
"registryDependencies": [],
|
|
12
|
-
"sharedFiles": [],
|
|
13
|
-
"filesDir": "kpi-card/files"
|
|
14
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "like-button",
|
|
3
|
-
"type": "component",
|
|
4
|
-
"title": "Like Button",
|
|
5
|
-
"description": "Heart, star or bookmark that pops, throws a ring and a burst of coloured particles when liked, with an optional counter that rolls to the new number.",
|
|
6
|
-
"tier": "pro",
|
|
7
|
-
"dependencies": [
|
|
8
|
-
"react-native-svg",
|
|
9
|
-
"expo-haptics"
|
|
10
|
-
],
|
|
11
|
-
"registryDependencies": [
|
|
12
|
-
"kit"
|
|
13
|
-
],
|
|
14
|
-
"sharedFiles": [
|
|
15
|
-
"lib/kit.tsx"
|
|
16
|
-
],
|
|
17
|
-
"filesDir": null
|
|
18
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "morph-button",
|
|
3
|
-
"type": "component",
|
|
4
|
-
"title": "Morph Button",
|
|
5
|
-
"description": "Async button that shrinks into a circle with a spinner while your promise runs, then draws a success tick or shakes with an error cross before springing back to its label.",
|
|
6
|
-
"tier": "pro",
|
|
7
|
-
"dependencies": [
|
|
8
|
-
"react-native-svg",
|
|
9
|
-
"expo-haptics"
|
|
10
|
-
],
|
|
11
|
-
"registryDependencies": [
|
|
12
|
-
"kit"
|
|
13
|
-
],
|
|
14
|
-
"sharedFiles": [
|
|
15
|
-
"lib/kit.tsx"
|
|
16
|
-
],
|
|
17
|
-
"filesDir": null
|
|
18
|
-
}
|