@expokit/cli 0.0.0-stage → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +46 -2
- package/bin/expo-kit.js +1266 -0
- package/bin/lib.mjs +343 -0
- package/bin/mcp.mjs +345 -0
- package/package.json +49 -4
- package/registry/_docs/accordion.json +115 -0
- package/registry/_docs/action-sheet.json +138 -0
- package/registry/_docs/alert.json +155 -0
- package/registry/_docs/avatar.json +227 -0
- package/registry/_docs/badge.json +148 -0
- package/registry/_docs/bottom-nav.json +143 -0
- package/registry/_docs/bottom-sheet.json +186 -0
- package/registry/_docs/bullet.json +27 -0
- package/registry/_docs/button-3d.json +138 -0
- package/registry/_docs/button.json +131 -0
- package/registry/_docs/calendar.json +198 -0
- package/registry/_docs/card-stack.json +124 -0
- package/registry/_docs/card.json +131 -0
- package/registry/_docs/carousel.json +122 -0
- package/registry/_docs/chat.json +184 -0
- package/registry/_docs/checkbox.json +109 -0
- package/registry/_docs/checkout.json +105 -0
- package/registry/_docs/chip-group.json +132 -0
- package/registry/_docs/color-swatch.json +82 -0
- package/registry/_docs/confetti.json +102 -0
- package/registry/_docs/coupon.json +192 -0
- package/registry/_docs/cube-pager.json +100 -0
- package/registry/_docs/dashboard.json +123 -0
- package/registry/_docs/date-picker.json +269 -0
- package/registry/_docs/dialog.json +186 -0
- package/registry/_docs/drawer.json +199 -0
- package/registry/_docs/fab.json +144 -0
- package/registry/_docs/flip-card.json +127 -0
- package/registry/_docs/floating-input.json +211 -0
- package/registry/_docs/gift-card.json +124 -0
- package/registry/_docs/glass-button.json +142 -0
- package/registry/_docs/glass-nav-bar.json +102 -0
- package/registry/_docs/glass-search-bar.json +114 -0
- package/registry/_docs/glass-segmented.json +94 -0
- package/registry/_docs/glass-sheet.json +136 -0
- package/registry/_docs/glass-tab-bar.json +100 -0
- package/registry/_docs/glass.json +94 -0
- package/registry/_docs/inbox.json +75 -0
- package/registry/_docs/isometric-bar.json +75 -0
- package/registry/_docs/kit.json +14 -0
- package/registry/_docs/kpi-card.json +14 -0
- package/registry/_docs/like-button.json +92 -0
- package/registry/_docs/liquidglass-tab.json +30 -0
- package/registry/_docs/list-item.json +216 -0
- package/registry/_docs/loyalty-card.json +124 -0
- package/registry/_docs/morph-button.json +128 -0
- package/registry/_docs/number-ticker.json +112 -0
- package/registry/_docs/offers.json +81 -0
- package/registry/_docs/onboarding.json +69 -0
- package/registry/_docs/otp-input.json +158 -0
- package/registry/_docs/pagination-dots.json +108 -0
- package/registry/_docs/parallax-layers.json +104 -0
- package/registry/_docs/parallax-scroll.json +210 -0
- package/registry/_docs/password-input.json +112 -0
- package/registry/_docs/payment-card.json +169 -0
- package/registry/_docs/pixel-bar.json +69 -0
- package/registry/_docs/polar-area.json +81 -0
- package/registry/_docs/profile.json +87 -0
- package/registry/_docs/progress-steps.json +116 -0
- package/registry/_docs/progress.json +152 -0
- package/registry/_docs/radio-group.json +112 -0
- package/registry/_docs/rating.json +114 -0
- package/registry/_docs/ring-carousel.json +122 -0
- package/registry/_docs/scratch-card.json +110 -0
- package/registry/_docs/search-bar.json +160 -0
- package/registry/_docs/search-results.json +123 -0
- package/registry/_docs/segmented-control.json +123 -0
- package/registry/_docs/select.json +277 -0
- package/registry/_docs/settings.json +75 -0
- package/registry/_docs/shimmer-button.json +98 -0
- package/registry/_docs/sign-in.json +201 -0
- package/registry/_docs/skeleton.json +162 -0
- package/registry/_docs/slide-to-confirm.json +108 -0
- package/registry/_docs/slider.json +177 -0
- package/registry/_docs/spin-wheel.json +138 -0
- package/registry/_docs/spinner.json +121 -0
- package/registry/_docs/stateful-button.json +184 -0
- package/registry/_docs/stepper.json +184 -0
- package/registry/_docs/swipe-deck.json +118 -0
- package/registry/_docs/swipeable-row.json +95 -0
- package/registry/_docs/switch.json +124 -0
- package/registry/_docs/tabs.json +129 -0
- package/registry/_docs/tag-input.json +148 -0
- package/registry/_docs/text-animation.json +141 -0
- package/registry/_docs/textarea.json +191 -0
- package/registry/_docs/ticket-card.json +194 -0
- package/registry/_docs/ticket-shape.json +14 -0
- package/registry/_docs/tilt-card.json +157 -0
- package/registry/_docs/time-picker.json +269 -0
- package/registry/_docs/timeline-chart.json +33 -0
- package/registry/_docs/timeline.json +172 -0
- package/registry/_docs/toast.json +185 -0
- package/registry/_docs/treemap.json +33 -0
- package/registry/_docs/waterfall.json +39 -0
- package/registry/_docs/wheel-list.json +122 -0
- package/registry/accordion/files/src/components/ui/accordion.tsx +233 -0
- package/registry/accordion/files/src/lib/kit.tsx +219 -0
- package/registry/accordion.json +17 -0
- package/registry/action-sheet/files/src/components/ui/action-sheet.tsx +580 -0
- package/registry/action-sheet/files/src/lib/kit.tsx +219 -0
- package/registry/action-sheet.json +18 -0
- package/registry/alert/files/src/components/ui/alert.tsx +654 -0
- package/registry/alert/files/src/components/ui/button.tsx +274 -0
- package/registry/alert/files/src/lib/kit.tsx +219 -0
- package/registry/alert.json +20 -0
- package/registry/avatar/files/src/components/ui/avatar.tsx +790 -0
- package/registry/avatar/files/src/lib/kit.tsx +219 -0
- package/registry/avatar.json +18 -0
- package/registry/badge/files/src/components/ui/badge.tsx +626 -0
- package/registry/badge/files/src/lib/kit.tsx +219 -0
- package/registry/badge.json +17 -0
- package/registry/bottom-nav/files/src/components/ui/bottom-nav.tsx +1050 -0
- package/registry/bottom-nav/files/src/lib/kit.tsx +219 -0
- package/registry/bottom-nav.json +18 -0
- package/registry/bottom-sheet/files/src/components/ui/bottom-sheet.tsx +585 -0
- package/registry/bottom-sheet/files/src/lib/kit.tsx +219 -0
- package/registry/bottom-sheet.json +17 -0
- package/registry/bullet.json +14 -0
- package/registry/button/files/src/components/ui/button.tsx +274 -0
- package/registry/button/files/src/lib/kit.tsx +219 -0
- package/registry/button-3d/files/src/components/ui/button-3d.tsx +212 -0
- package/registry/button-3d/files/src/lib/kit.tsx +219 -0
- package/registry/button-3d.json +18 -0
- package/registry/button.json +18 -0
- package/registry/calendar/files/src/components/ui/calendar.tsx +822 -0
- package/registry/calendar/files/src/lib/kit.tsx +219 -0
- package/registry/calendar.json +17 -0
- package/registry/card/files/src/components/ui/card.tsx +300 -0
- package/registry/card/files/src/lib/kit.tsx +219 -0
- package/registry/card-stack.json +15 -0
- package/registry/card.json +17 -0
- package/registry/carousel.json +15 -0
- package/registry/chat.json +18 -0
- package/registry/checkbox/files/src/components/ui/checkbox.tsx +150 -0
- package/registry/checkbox/files/src/lib/kit.tsx +219 -0
- package/registry/checkbox.json +17 -0
- package/registry/checkout.json +26 -0
- package/registry/chip-group/files/src/components/ui/chip-group.tsx +269 -0
- package/registry/chip-group/files/src/lib/kit.tsx +219 -0
- package/registry/chip-group.json +17 -0
- package/registry/color-swatch.json +17 -0
- package/registry/confetti/files/src/components/ui/confetti.tsx +222 -0
- package/registry/confetti/files/src/lib/kit.tsx +219 -0
- package/registry/confetti.json +15 -0
- package/registry/coupon/files/src/components/ui/coupon.tsx +698 -0
- package/registry/coupon/files/src/components/ui/flip-card.tsx +205 -0
- package/registry/coupon/files/src/components/ui/tilt-card.tsx +635 -0
- package/registry/coupon/files/src/lib/kit.tsx +219 -0
- package/registry/coupon/files/src/lib/ticket-shape.tsx +543 -0
- package/registry/coupon.json +23 -0
- package/registry/cube-pager.json +15 -0
- package/registry/dashboard.json +32 -0
- package/registry/date-picker/files/src/components/ui/calendar.tsx +822 -0
- package/registry/date-picker/files/src/components/ui/date-picker.tsx +796 -0
- package/registry/date-picker/files/src/lib/kit.tsx +219 -0
- package/registry/date-picker.json +19 -0
- package/registry/dialog/files/src/components/ui/dialog.tsx +759 -0
- package/registry/dialog/files/src/lib/kit.tsx +219 -0
- package/registry/dialog.json +18 -0
- package/registry/drawer/files/src/components/ui/drawer.tsx +1135 -0
- package/registry/drawer/files/src/lib/kit.tsx +219 -0
- package/registry/drawer.json +18 -0
- package/registry/fab.json +18 -0
- package/registry/flip-card/files/src/components/ui/flip-card.tsx +205 -0
- package/registry/flip-card/files/src/lib/kit.tsx +219 -0
- package/registry/flip-card.json +15 -0
- package/registry/floating-input/files/src/components/ui/floating-input.tsx +528 -0
- package/registry/floating-input/files/src/lib/kit.tsx +219 -0
- package/registry/floating-input.json +17 -0
- package/registry/gift-card.json +21 -0
- package/registry/glass-button.json +22 -0
- package/registry/glass-nav-bar.json +21 -0
- package/registry/glass-search-bar.json +21 -0
- package/registry/glass-segmented.json +22 -0
- package/registry/glass-sheet.json +22 -0
- package/registry/glass-tab-bar.json +22 -0
- package/registry/glass.json +19 -0
- package/registry/inbox.json +30 -0
- package/registry/isometric-bar.json +14 -0
- package/registry/kit/files/src/lib/kit.tsx +219 -0
- package/registry/kit.json +11 -0
- package/registry/kpi-card/files/src/components/charts/kpi-card.tsx +45 -0
- package/registry/kpi-card.json +14 -0
- package/registry/like-button.json +18 -0
- package/registry/liquidglass-tab/files/src/app/(tabs)/_layout.tsx +97 -0
- package/registry/liquidglass-tab/files/src/app/(tabs)/alerts.tsx +5 -0
- package/registry/liquidglass-tab/files/src/app/(tabs)/explore.tsx +5 -0
- package/registry/liquidglass-tab/files/src/app/(tabs)/index.tsx +5 -0
- package/registry/liquidglass-tab/files/src/app/(tabs)/profile.tsx +5 -0
- package/registry/liquidglass-tab/files/src/app/_layout.tsx +33 -0
- package/registry/liquidglass-tab/files/src/components/GlassTabBar.tsx +259 -0
- package/registry/liquidglass-tab/files/src/components/TabScreen.tsx +122 -0
- package/registry/liquidglass-tab/files/src/lib/tab-bar.ts +61 -0
- package/registry/liquidglass-tab/files/src/lib/tab-icons.ts +130 -0
- package/registry/liquidglass-tab/files/src/lib/tabs.ts +108 -0
- package/registry/liquidglass-tab.json +23 -0
- package/registry/list-item/files/src/components/ui/list-item.tsx +508 -0
- package/registry/list-item/files/src/lib/kit.tsx +219 -0
- package/registry/list-item.json +17 -0
- package/registry/loyalty-card.json +23 -0
- package/registry/morph-button.json +18 -0
- package/registry/number-ticker.json +15 -0
- package/registry/offers.json +24 -0
- package/registry/onboarding.json +17 -0
- package/registry/otp-input/files/src/components/ui/otp-input.tsx +380 -0
- package/registry/otp-input/files/src/lib/kit.tsx +219 -0
- package/registry/otp-input.json +17 -0
- package/registry/pagination-dots/files/src/components/ui/pagination-dots.tsx +262 -0
- package/registry/pagination-dots/files/src/lib/kit.tsx +219 -0
- package/registry/pagination-dots.json +15 -0
- package/registry/parallax-layers.json +15 -0
- package/registry/parallax-scroll.json +17 -0
- package/registry/password-input/files/src/components/ui/floating-input.tsx +528 -0
- package/registry/password-input/files/src/components/ui/password-input.tsx +271 -0
- package/registry/password-input/files/src/lib/kit.tsx +219 -0
- package/registry/password-input.json +19 -0
- package/registry/payment-card/files/src/components/ui/flip-card.tsx +205 -0
- package/registry/payment-card/files/src/components/ui/payment-card.tsx +385 -0
- package/registry/payment-card/files/src/components/ui/tilt-card.tsx +635 -0
- package/registry/payment-card/files/src/lib/kit.tsx +219 -0
- package/registry/payment-card.json +21 -0
- package/registry/pixel-bar.json +14 -0
- package/registry/polar-area.json +14 -0
- package/registry/profile.json +17 -0
- package/registry/progress/files/src/components/ui/progress.tsx +883 -0
- package/registry/progress/files/src/lib/kit.tsx +219 -0
- package/registry/progress-steps/files/src/components/ui/progress-steps.tsx +559 -0
- package/registry/progress-steps/files/src/lib/kit.tsx +219 -0
- package/registry/progress-steps.json +18 -0
- package/registry/progress.json +17 -0
- package/registry/radio-group/files/src/components/ui/radio-group.tsx +327 -0
- package/registry/radio-group/files/src/lib/kit.tsx +219 -0
- package/registry/radio-group.json +18 -0
- package/registry/rating.json +18 -0
- package/registry/ring-carousel.json +15 -0
- package/registry/scratch-card.json +18 -0
- package/registry/search-bar/files/src/components/ui/search-bar.tsx +530 -0
- package/registry/search-bar/files/src/lib/kit.tsx +219 -0
- package/registry/search-bar.json +17 -0
- package/registry/search-results.json +30 -0
- package/registry/segmented-control/files/src/components/ui/segmented-control.tsx +341 -0
- package/registry/segmented-control/files/src/lib/kit.tsx +219 -0
- package/registry/segmented-control.json +18 -0
- package/registry/select/files/src/components/ui/select.tsx +1398 -0
- package/registry/select/files/src/lib/kit.tsx +219 -0
- package/registry/select.json +17 -0
- package/registry/settings.json +26 -0
- package/registry/shimmer-button.json +17 -0
- package/registry/sign-in.json +26 -0
- package/registry/skeleton/files/src/components/ui/skeleton.tsx +799 -0
- package/registry/skeleton/files/src/lib/kit.tsx +219 -0
- package/registry/skeleton.json +17 -0
- package/registry/slide-to-confirm.json +18 -0
- package/registry/slider/files/src/components/ui/slider.tsx +716 -0
- package/registry/slider/files/src/lib/kit.tsx +219 -0
- package/registry/slider.json +17 -0
- package/registry/spin-wheel.json +18 -0
- package/registry/spinner/files/src/components/ui/spinner.tsx +748 -0
- package/registry/spinner/files/src/lib/kit.tsx +219 -0
- package/registry/spinner.json +17 -0
- package/registry/stateful-button/files/src/components/ui/stateful-button.tsx +532 -0
- package/registry/stateful-button/files/src/lib/kit.tsx +219 -0
- package/registry/stateful-button.json +18 -0
- package/registry/stepper/files/src/components/ui/stepper.tsx +646 -0
- package/registry/stepper/files/src/lib/kit.tsx +219 -0
- package/registry/stepper.json +17 -0
- package/registry/swipe-deck.json +15 -0
- package/registry/swipeable-row.json +15 -0
- package/registry/switch/files/src/components/ui/switch.tsx +344 -0
- package/registry/switch/files/src/lib/kit.tsx +219 -0
- package/registry/switch.json +18 -0
- package/registry/tabs/files/src/components/ui/tabs.tsx +539 -0
- package/registry/tabs/files/src/lib/kit.tsx +219 -0
- package/registry/tabs.json +17 -0
- package/registry/tag-input/files/src/components/ui/tag-input.tsx +720 -0
- package/registry/tag-input/files/src/lib/kit.tsx +219 -0
- package/registry/tag-input.json +17 -0
- package/registry/text-animation/files/src/components/ui/text-animation.tsx +650 -0
- package/registry/text-animation/files/src/lib/kit.tsx +219 -0
- package/registry/text-animation.json +15 -0
- package/registry/textarea/files/src/components/ui/textarea.tsx +553 -0
- package/registry/textarea/files/src/lib/kit.tsx +219 -0
- package/registry/textarea.json +17 -0
- package/registry/ticket-card.json +24 -0
- package/registry/ticket-shape/files/src/lib/kit.tsx +219 -0
- package/registry/ticket-shape/files/src/lib/ticket-shape.tsx +543 -0
- package/registry/ticket-shape.json +17 -0
- package/registry/tilt-card/files/src/components/ui/tilt-card.tsx +635 -0
- package/registry/tilt-card/files/src/lib/kit.tsx +219 -0
- package/registry/tilt-card.json +17 -0
- package/registry/time-picker/files/src/components/ui/time-picker.tsx +1159 -0
- package/registry/time-picker/files/src/lib/kit.tsx +219 -0
- package/registry/time-picker.json +17 -0
- package/registry/timeline/files/src/components/ui/avatar.tsx +790 -0
- package/registry/timeline/files/src/components/ui/timeline.tsx +1059 -0
- package/registry/timeline/files/src/lib/kit.tsx +219 -0
- package/registry/timeline-chart.json +14 -0
- package/registry/timeline.json +20 -0
- package/registry/toast/files/src/components/ui/button.tsx +274 -0
- package/registry/toast/files/src/components/ui/toast.tsx +1284 -0
- package/registry/toast/files/src/lib/kit.tsx +219 -0
- package/registry/toast.json +20 -0
- package/registry/treemap.json +14 -0
- package/registry/waterfall.json +14 -0
- package/registry/wheel-list.json +17 -0
- package/schema/components.schema.json +13 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "avatar",
|
|
3
|
+
"category": "Display",
|
|
4
|
+
"aliases": [],
|
|
5
|
+
"exportName": "Avatar",
|
|
6
|
+
"files": [
|
|
7
|
+
"src/components/ui/avatar.tsx"
|
|
8
|
+
],
|
|
9
|
+
"summary": "A photo over hashed-colour initials, with presence dots and badges cut into the edge.",
|
|
10
|
+
"props": [
|
|
11
|
+
[
|
|
12
|
+
"src",
|
|
13
|
+
"string",
|
|
14
|
+
"—",
|
|
15
|
+
"Image URL. Fades in over the initials; falls back to them on error."
|
|
16
|
+
],
|
|
17
|
+
[
|
|
18
|
+
"name",
|
|
19
|
+
"string",
|
|
20
|
+
"—",
|
|
21
|
+
"Initials, the deterministic fallback colour and the accessibility label."
|
|
22
|
+
],
|
|
23
|
+
[
|
|
24
|
+
"size",
|
|
25
|
+
"\"xs\" | \"sm\" | \"md\" | \"lg\" | \"xl\" | number",
|
|
26
|
+
"\"md\"",
|
|
27
|
+
"24 / 32 / 40 / 56 / 72pt, or any number."
|
|
28
|
+
],
|
|
29
|
+
[
|
|
30
|
+
"shape",
|
|
31
|
+
"\"circle\" | \"rounded\"",
|
|
32
|
+
"\"circle\"",
|
|
33
|
+
"Circle or rounded square (follows the kit radius)."
|
|
34
|
+
],
|
|
35
|
+
[
|
|
36
|
+
"fallback",
|
|
37
|
+
"\"soft\" | \"solid\" | \"gradient\"",
|
|
38
|
+
"\"soft\"",
|
|
39
|
+
"Soft tint with deep ink, flat fill, or an opt-in two-tone gradient."
|
|
40
|
+
],
|
|
41
|
+
[
|
|
42
|
+
"status",
|
|
43
|
+
"\"online\" | \"away\" | \"busy\" | \"offline\"",
|
|
44
|
+
"—",
|
|
45
|
+
"Presence dot in a ring cut-out: filled, crescent, bar, hollow."
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
"pulse",
|
|
49
|
+
"boolean",
|
|
50
|
+
"true when online",
|
|
51
|
+
"Soft pulse behind the dot (off with reduce motion)."
|
|
52
|
+
],
|
|
53
|
+
[
|
|
54
|
+
"ring",
|
|
55
|
+
"\"none\" | \"story\" | \"accent\" | \"seen\"",
|
|
56
|
+
"\"none\"",
|
|
57
|
+
"Solid accent ring with a clear gap. \"seen\" = story + seen."
|
|
58
|
+
],
|
|
59
|
+
[
|
|
60
|
+
"seen",
|
|
61
|
+
"boolean",
|
|
62
|
+
"false",
|
|
63
|
+
"Story watched: the ring cross-fades to a quiet hairline."
|
|
64
|
+
],
|
|
65
|
+
[
|
|
66
|
+
"ringColors",
|
|
67
|
+
"string[]",
|
|
68
|
+
"sunset",
|
|
69
|
+
"Ring colour override; the first entry is used as a solid colour."
|
|
70
|
+
],
|
|
71
|
+
[
|
|
72
|
+
"ringSpin",
|
|
73
|
+
"boolean",
|
|
74
|
+
"false",
|
|
75
|
+
"Broken ring that spins — a loading story (circle only)."
|
|
76
|
+
],
|
|
77
|
+
[
|
|
78
|
+
"verified",
|
|
79
|
+
"boolean",
|
|
80
|
+
"false",
|
|
81
|
+
"Rosette seal with a check, bottom-right (bottom-left if a status dot is shown)."
|
|
82
|
+
],
|
|
83
|
+
[
|
|
84
|
+
"badge",
|
|
85
|
+
"number | boolean",
|
|
86
|
+
"—",
|
|
87
|
+
"Notification count (99+), or true for a dot, top-right."
|
|
88
|
+
],
|
|
89
|
+
[
|
|
90
|
+
"surface",
|
|
91
|
+
"string",
|
|
92
|
+
"theme background",
|
|
93
|
+
"Colour behind the avatar; cut-outs and group borders use it."
|
|
94
|
+
],
|
|
95
|
+
[
|
|
96
|
+
"accent",
|
|
97
|
+
"string",
|
|
98
|
+
"theme accent",
|
|
99
|
+
"Ring and verified seal colour."
|
|
100
|
+
],
|
|
101
|
+
[
|
|
102
|
+
"elevated",
|
|
103
|
+
"boolean",
|
|
104
|
+
"true for 72pt+",
|
|
105
|
+
"Soft drop shadow under the face."
|
|
106
|
+
],
|
|
107
|
+
[
|
|
108
|
+
"onPress",
|
|
109
|
+
"() => void",
|
|
110
|
+
"—",
|
|
111
|
+
"Makes it a button with spring press feedback."
|
|
112
|
+
],
|
|
113
|
+
[
|
|
114
|
+
"haptics",
|
|
115
|
+
"boolean",
|
|
116
|
+
"true",
|
|
117
|
+
"Light haptic tick on press."
|
|
118
|
+
],
|
|
119
|
+
[
|
|
120
|
+
"AvatarGroup.max",
|
|
121
|
+
"number",
|
|
122
|
+
"4",
|
|
123
|
+
"Avatars shown before the +N chip."
|
|
124
|
+
],
|
|
125
|
+
[
|
|
126
|
+
"AvatarGroup.total",
|
|
127
|
+
"number",
|
|
128
|
+
"children count",
|
|
129
|
+
"Real total when larger than what you pass."
|
|
130
|
+
],
|
|
131
|
+
[
|
|
132
|
+
"AvatarGroup.overlap",
|
|
133
|
+
"number",
|
|
134
|
+
"0.28",
|
|
135
|
+
"Overlap as a fraction of the size."
|
|
136
|
+
],
|
|
137
|
+
[
|
|
138
|
+
"AvatarGroup.surface",
|
|
139
|
+
"string",
|
|
140
|
+
"theme background",
|
|
141
|
+
"Colour of the separating borders — match the parent."
|
|
142
|
+
],
|
|
143
|
+
[
|
|
144
|
+
"AvatarGroup.animated",
|
|
145
|
+
"boolean",
|
|
146
|
+
"true",
|
|
147
|
+
"Staggered spring-in on mount."
|
|
148
|
+
],
|
|
149
|
+
[
|
|
150
|
+
"AvatarGroup.onPressMore",
|
|
151
|
+
"() => void",
|
|
152
|
+
"—",
|
|
153
|
+
"Makes the +N chip a button."
|
|
154
|
+
],
|
|
155
|
+
[
|
|
156
|
+
"style",
|
|
157
|
+
"StyleProp<ViewStyle>",
|
|
158
|
+
"—",
|
|
159
|
+
"Style for the avatar root; AvatarGroup applies it to its row."
|
|
160
|
+
]
|
|
161
|
+
],
|
|
162
|
+
"behavior": [
|
|
163
|
+
[
|
|
164
|
+
"Motion",
|
|
165
|
+
"The photo fades in over the initials, a story ring cross-fades to a hairline once seen, and AvatarGroup springs its faces in with a stagger."
|
|
166
|
+
],
|
|
167
|
+
[
|
|
168
|
+
"Touch",
|
|
169
|
+
"With onPress it becomes a button that springs down while pressed."
|
|
170
|
+
],
|
|
171
|
+
[
|
|
172
|
+
"Reduced motion",
|
|
173
|
+
"The status pulse, ring spin, press squish and group stagger are skipped, and the seen cross-fade is instant."
|
|
174
|
+
],
|
|
175
|
+
[
|
|
176
|
+
"Haptics",
|
|
177
|
+
"A light impact on press when onPress is set. Turn off with haptics={false}."
|
|
178
|
+
],
|
|
179
|
+
[
|
|
180
|
+
"Screen readers",
|
|
181
|
+
"Read as an image (or a button when pressable) labelled with the name plus verified, status, notification and story details. A group reads its first names and \"and N more\"."
|
|
182
|
+
]
|
|
183
|
+
],
|
|
184
|
+
"usage": "import { Avatar, AvatarGroup } from '@/components/ui/avatar';\n\n<Avatar name=\"Ava Rossi\" src=\"https://i.pravatar.cc/240?img=47\" size=\"lg\" status=\"online\" />\n<AvatarGroup max={3} total={12}>\n <Avatar name=\"Noah Kim\" />\n <Avatar name=\"Priya Patel\" />\n <Avatar name=\"Diego Alvarez\" />\n</AvatarGroup>",
|
|
185
|
+
"examples": [
|
|
186
|
+
{
|
|
187
|
+
"name": "ProfileHeader",
|
|
188
|
+
"title": "Profile Header",
|
|
189
|
+
"code": "import { Pressable, ScrollView, Text, View } from 'react-native';\nimport { Avatar, AvatarGroup } from '@/components/ui/avatar';\nimport { alpha, useKit } from '@/lib/kit';\n\nexport default function ProfileHeader() {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={{\n alignItems: 'center',\n gap: 12,\n paddingVertical: 20,\n paddingHorizontal: 16,\n borderRadius: radius.xl,\n backgroundColor: colors.card,\n borderWidth: 1,\n borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06),\n }}\n >\n <Avatar\n size={88}\n src=\"https://i.pravatar.cc/240?img=47\"\n name=\"Ava Rossi\"\n ring=\"story\"\n verified\n surface={colors.card}\n elevated\n />\n <View style={{ alignItems: 'center', gap: 2 }}>\n <Text style={{ fontSize: 19, fontWeight: '400', letterSpacing: -0.3, color: colors.foreground }}>Ava Rossi</Text>\n <Text style={{ fontSize: 14, color: colors.mutedForeground }}>Product designer · Milan</Text>\n </View>\n <View style={{ flexDirection: 'row', gap: 24, marginTop: 4 }}>\n {[\n ['248', 'Posts'],\n ['12.4k', 'Followers'],\n ['311', 'Following'],\n ].map(([n, l]) => (\n <View key={l} style={{ alignItems: 'center' }}>\n <Text style={{ fontSize: 16, color: colors.foreground, fontVariant: ['tabular-nums'] }}>{n}</Text>\n <Text style={{ fontSize: 12, color: colors.mutedForeground }}>{l}</Text>\n </View>\n ))}\n </View>\n </View>\n );\n}\n"
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"name": "ChatList",
|
|
193
|
+
"title": "Chat List",
|
|
194
|
+
"code": "import { Pressable, ScrollView, Text, View } from 'react-native';\nimport { Avatar, AvatarGroup } from '@/components/ui/avatar';\nimport { alpha, useKit } from '@/lib/kit';\n\nexport default function ChatList() {\n const { colors } = useKit();\n const chats = [\n { name: 'Noah Kim', src: 'https://i.pravatar.cc/150?img=59', status: 'online', msg: 'Pushed the fix — can you re-test?', time: '9:41', unread: 2 },\n { name: 'Priya Patel', status: 'away', msg: 'Sounds good, see you at 3.', time: '9:12', unread: 0 },\n { name: 'Diego Alvarez', src: 'https://i.pravatar.cc/150?img=12', status: 'busy', msg: 'In a call until noon', time: 'Mon', unread: 0 },\n { name: 'Hannah Lee', status: 'offline', msg: 'Thanks for the notes!', time: 'Sun', unread: 0 },\n ] as const;\n return (\n <View>\n {chats.map((c, i) => (\n <View\n key={c.name}\n style={{\n flexDirection: 'row',\n alignItems: 'center',\n gap: 12,\n paddingVertical: 10,\n borderTopWidth: i ? 1 : 0,\n borderTopColor: alpha(colors.foreground, 0.06),\n }}\n >\n <Avatar size={48} name={c.name} src={'src' in c ? c.src : undefined} status={c.status} />\n <View style={{ flex: 1, minWidth: 0 }}>\n <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>\n <Text style={{ fontSize: 15, color: colors.foreground }}>{c.name}</Text>\n <Text style={{ fontSize: 12, color: c.unread ? colors.accent : colors.mutedForeground }}>{c.time}</Text>\n </View>\n <Text numberOfLines={1} style={{ fontSize: 14, color: colors.mutedForeground, marginTop: 2 }}>\n {c.msg}\n </Text>\n </View>\n </View>\n ))}\n </View>\n );\n}\n"
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"name": "Stories",
|
|
198
|
+
"title": "Stories",
|
|
199
|
+
"code": "import React, { useState } from 'react';\nimport { Pressable, ScrollView, Text, View } from 'react-native';\nimport { Avatar, AvatarGroup } from '@/components/ui/avatar';\nimport { alpha, useKit } from '@/lib/kit';\n\nexport default function Stories() {\n const { colors } = useKit();\n const [seen, setSeen] = useState<string[]>(['Theo']);\n const people = [\n { name: 'Kai Müller', short: 'Kai', img: 11 },\n { name: 'Mila Novak', short: 'Mila', img: 25 },\n { name: 'Theo Grant', short: 'Theo', img: 33 },\n { name: 'Nora Ali', short: 'Nora', img: 41 },\n { name: 'Sam Ortiz', short: 'Sam', img: 0 },\n { name: 'Lucía Romero', short: 'Lucía', img: 44 },\n ];\n return (\n <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{ gap: 14, paddingRight: 8 }}>\n {people.map((p, i) => (\n <View key={p.short} style={{ alignItems: 'center', gap: 6 }}>\n <Avatar\n size=\"lg\"\n src={p.img ? `https://i.pravatar.cc/150?img=${p.img}` : undefined}\n name={p.name}\n ring=\"story\"\n seen={seen.includes(p.short)}\n ringSpin={i === 1 && !seen.includes(p.short)}\n onPress={() => setSeen((s) => (s.includes(p.short) ? s.filter((n) => n !== p.short) : [...s, p.short]))}\n />\n <Text style={{ fontSize: 12, color: seen.includes(p.short) ? colors.mutedForeground : colors.foreground }}>{p.short}</Text>\n </View>\n ))}\n </ScrollView>\n );\n}\n"
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"name": "Group",
|
|
203
|
+
"title": "Group",
|
|
204
|
+
"code": "import { Pressable, ScrollView, Text, View } from 'react-native';\nimport { Avatar, AvatarGroup } from '@/components/ui/avatar';\nimport { alpha, useKit } from '@/lib/kit';\n\nexport default function Group() {\n const { colors, radius, dark } = useKit();\n return (\n <View style={{ gap: 14 }}>\n <View\n style={{\n padding: 14,\n gap: 12,\n borderRadius: radius.lg,\n backgroundColor: colors.card,\n borderWidth: 1,\n borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06),\n }}\n >\n <View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>\n <View>\n <Text style={{ fontSize: 15, color: colors.foreground }}>Mobile launch</Text>\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>Design · Engineering</Text>\n </View>\n <AvatarGroup max={4} total={9} surface={colors.card} size=\"sm\" onPressMore={() => {}}>\n {[8, 16, 26, 36, 48].map((n, i) => (\n <Avatar key={n} src={`https://i.pravatar.cc/150?img=${n}`} name={['Maya Chen', 'Leo Park', 'Ava Rossi', 'Noah Kim', 'Zoe Martin'][i]} />\n ))}\n </AvatarGroup>\n </View>\n </View>\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>\n <AvatarGroup size=\"sm\" max={3} total={1284}>\n <Avatar name=\"Ana Silva\" />\n <Avatar name=\"Ben Carter\" />\n <Avatar name=\"Chloe Dubois\" />\n </AvatarGroup>\n <Text style={{ fontSize: 13, color: colors.mutedForeground }}>1,284 members</Text>\n </View>\n <AvatarGroup size=\"lg\" shape=\"rounded\" max={5}>\n {['Priya Patel', 'Omar Haddad', 'Sven Larsen', 'Iris Okafor'].map((n) => (\n <Avatar key={n} name={n} />\n ))}\n </AvatarGroup>\n </View>\n );\n}\n"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"name": "Fallbacks",
|
|
208
|
+
"title": "Fallbacks",
|
|
209
|
+
"code": "import { Pressable, ScrollView, Text, View } from 'react-native';\nimport { Avatar, AvatarGroup } from '@/components/ui/avatar';\n\nexport default function Fallbacks() {\n const names = ['Priya Patel', 'Diego Alvarez', 'Hannah Lee', 'Omar Haddad', 'Sven Larsen'];\n return (\n <View style={{ gap: 14 }}>\n <View style={{ flexDirection: 'row', gap: 12 }}>\n {names.map((n) => (\n <Avatar key={n} name={n} size=\"lg\" />\n ))}\n </View>\n <View style={{ flexDirection: 'row', gap: 12 }}>\n {names.map((n) => (\n <Avatar key={n} name={n} size=\"lg\" fallback=\"solid\" />\n ))}\n </View>\n <View style={{ flexDirection: 'row', gap: 12 }}>\n {names.slice(0, 3).map((n) => (\n <Avatar key={n} name={n} size=\"lg\" shape=\"rounded\" fallback=\"gradient\" />\n ))}\n <Avatar size=\"lg\" shape=\"rounded\" src=\"https://example.invalid/broken.jpg\" name=\"Kenji Watanabe\" />\n <Avatar size=\"lg\" />\n </View>\n </View>\n );\n}\n"
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"name": "Sizes",
|
|
213
|
+
"title": "Sizes",
|
|
214
|
+
"code": "import { Pressable, ScrollView, Text, View } from 'react-native';\nimport { Avatar, AvatarGroup } from '@/components/ui/avatar';\n\nexport default function Sizes() {\n return (\n <View style={{ flexDirection: 'row', alignItems: 'flex-end', gap: 12 }}>\n <Avatar size=\"xs\" src=\"https://i.pravatar.cc/150?img=32\" name=\"Maya Chen\" />\n <Avatar size=\"sm\" src=\"https://i.pravatar.cc/150?img=12\" name=\"Leo Park\" status=\"online\" />\n <Avatar size=\"md\" name=\"Ava Rossi\" status=\"away\" />\n <Avatar size=\"lg\" src=\"https://i.pravatar.cc/150?img=59\" name=\"Noah Kim\" verified />\n <Avatar size=\"xl\" src=\"https://i.pravatar.cc/150?img=5\" name=\"Zoe Martin\" status=\"online\" />\n </View>\n );\n}\n"
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"name": "Status",
|
|
218
|
+
"title": "Status",
|
|
219
|
+
"code": "import { Pressable, ScrollView, Text, View } from 'react-native';\nimport { Avatar, AvatarGroup } from '@/components/ui/avatar';\nimport { alpha, useKit } from '@/lib/kit';\n\nexport default function Status() {\n const { colors } = useKit();\n const rows = [\n { name: 'Ethan Brooks', img: 15, status: 'online' },\n { name: 'Lina Novak', img: 44, status: 'away' },\n { name: 'Marcus Hill', img: 68, status: 'busy' },\n { name: 'Iris Okafor', img: 0, status: 'offline' },\n ] as const;\n return (\n <View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>\n {rows.map((r) => (\n <View key={r.name} style={{ alignItems: 'center', gap: 6 }}>\n <Avatar size=\"lg\" src={r.img ? `https://i.pravatar.cc/150?img=${r.img}` : undefined} name={r.name} status={r.status} />\n <Text style={{ fontSize: 12, color: colors.mutedForeground }}>{r.status[0].toUpperCase() + r.status.slice(1)}</Text>\n </View>\n ))}\n </View>\n );\n}\n"
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"name": "Badges",
|
|
223
|
+
"title": "Badges",
|
|
224
|
+
"code": "import React, { useState } from 'react';\nimport { Pressable, ScrollView, Text, View } from 'react-native';\nimport { Avatar, AvatarGroup } from '@/components/ui/avatar';\nimport { alpha, useKit } from '@/lib/kit';\n\nexport default function Badges() {\n const { colors } = useKit();\n const [count, setCount] = useState(3);\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 22 }}>\n <Pressable accessibilityRole=\"button\" accessibilityLabel=\"Add a notification\" onPress={() => setCount((c) => c + 1)} style={{ alignItems: 'center', gap: 6 }}>\n <Avatar size=\"lg\" src=\"https://i.pravatar.cc/150?img=22\" name=\"Maya Chen\" badge={count} />\n <Text style={{ fontSize: 12, color: colors.mutedForeground }}>Tap +1</Text>\n </Pressable>\n <View style={{ alignItems: 'center', gap: 6 }}>\n <Avatar size=\"lg\" src=\"https://i.pravatar.cc/150?img=9\" name=\"Leo Park\" badge={128} status=\"online\" />\n <Text style={{ fontSize: 12, color: colors.mutedForeground }}>Mentions</Text>\n </View>\n <View style={{ alignItems: 'center', gap: 6 }}>\n <Avatar size=\"lg\" name=\"Design Team\" shape=\"rounded\" badge verified />\n <Text style={{ fontSize: 12, color: colors.mutedForeground }}>Workspace</Text>\n </View>\n </View>\n );\n}\n"
|
|
225
|
+
}
|
|
226
|
+
]
|
|
227
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "badge",
|
|
3
|
+
"category": "Display",
|
|
4
|
+
"aliases": [],
|
|
5
|
+
"exportName": "BadgeIcon",
|
|
6
|
+
"files": [
|
|
7
|
+
"src/components/ui/badge.tsx"
|
|
8
|
+
],
|
|
9
|
+
"summary": "Status pills in four styles that roll, pop and re-tint when their status changes.",
|
|
10
|
+
"props": [
|
|
11
|
+
[
|
|
12
|
+
"variant",
|
|
13
|
+
"\"solid\" | \"soft\" | \"outline\" | \"dot\"",
|
|
14
|
+
"\"soft\"",
|
|
15
|
+
"Filled, tinted, bordered, or a neutral outline with a coloured leading dot."
|
|
16
|
+
],
|
|
17
|
+
[
|
|
18
|
+
"tone",
|
|
19
|
+
"\"neutral\" | \"accent\" | \"success\" | \"warning\" | \"danger\"",
|
|
20
|
+
"\"neutral\"",
|
|
21
|
+
"Semantic colour."
|
|
22
|
+
],
|
|
23
|
+
[
|
|
24
|
+
"size",
|
|
25
|
+
"\"sm\" | \"md\" | \"lg\"",
|
|
26
|
+
"\"md\"",
|
|
27
|
+
"20 / 24 / 28pt tall."
|
|
28
|
+
],
|
|
29
|
+
[
|
|
30
|
+
"color",
|
|
31
|
+
"string",
|
|
32
|
+
"—",
|
|
33
|
+
"Custom colour (overrides tone)."
|
|
34
|
+
],
|
|
35
|
+
[
|
|
36
|
+
"icon",
|
|
37
|
+
"ReactNode",
|
|
38
|
+
"—",
|
|
39
|
+
"Shown before the label."
|
|
40
|
+
],
|
|
41
|
+
[
|
|
42
|
+
"shape",
|
|
43
|
+
"\"pill\" | \"rounded\"",
|
|
44
|
+
"\"pill\"",
|
|
45
|
+
"Corner style."
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
"pulse",
|
|
49
|
+
"boolean",
|
|
50
|
+
"false",
|
|
51
|
+
"Pulsing dot for live states (dot variant)."
|
|
52
|
+
],
|
|
53
|
+
[
|
|
54
|
+
"animateChanges",
|
|
55
|
+
"boolean",
|
|
56
|
+
"true",
|
|
57
|
+
"Roll the label, pop the icon, cross-fade colours and spring the width when they change. Never on mount."
|
|
58
|
+
],
|
|
59
|
+
[
|
|
60
|
+
"NotificationBadge.count",
|
|
61
|
+
"number",
|
|
62
|
+
"0",
|
|
63
|
+
"Number shown; 0 hides it."
|
|
64
|
+
],
|
|
65
|
+
[
|
|
66
|
+
"NotificationBadge.showZero",
|
|
67
|
+
"boolean",
|
|
68
|
+
"false",
|
|
69
|
+
"Keep the badge visible (showing 0) when count is 0."
|
|
70
|
+
],
|
|
71
|
+
[
|
|
72
|
+
"NotificationBadge.max",
|
|
73
|
+
"number",
|
|
74
|
+
"99",
|
|
75
|
+
"Shows \"99+\" above this."
|
|
76
|
+
],
|
|
77
|
+
[
|
|
78
|
+
"NotificationBadge.dot",
|
|
79
|
+
"boolean",
|
|
80
|
+
"false",
|
|
81
|
+
"Dot only, no number."
|
|
82
|
+
],
|
|
83
|
+
[
|
|
84
|
+
"NotificationBadge.children",
|
|
85
|
+
"ReactNode",
|
|
86
|
+
"—",
|
|
87
|
+
"Element the badge is pinned to (top-right)."
|
|
88
|
+
],
|
|
89
|
+
[
|
|
90
|
+
"NotificationBadge.offset",
|
|
91
|
+
"[number, number]",
|
|
92
|
+
"[0, 0]",
|
|
93
|
+
"Nudge from the corner."
|
|
94
|
+
],
|
|
95
|
+
[
|
|
96
|
+
"style",
|
|
97
|
+
"StyleProp<ViewStyle>",
|
|
98
|
+
"—",
|
|
99
|
+
"Style for the badge pill; NotificationBadge applies it to the anchor wrapper or bare bubble wrapper."
|
|
100
|
+
]
|
|
101
|
+
],
|
|
102
|
+
"behavior": [
|
|
103
|
+
[
|
|
104
|
+
"Motion",
|
|
105
|
+
"On a change the old label rolls up and out as the new one rolls in (per letter for short Latin text), the icon swaps with a scale-and-rotate pop and the pill width springs to fit. Rolls and pops use the native driver; colour and width run on the JS driver on separate views. Nothing animates on mount; animateChanges={false} swaps instantly."
|
|
106
|
+
],
|
|
107
|
+
[
|
|
108
|
+
"Reduced motion",
|
|
109
|
+
"Changes swap instantly: no roll, pop, colour fade or width spring. The dot variant's pulse still loops."
|
|
110
|
+
],
|
|
111
|
+
[
|
|
112
|
+
"Screen readers",
|
|
113
|
+
"Text labels are one accessible element whose label is always the current text; on Android it is a polite live region so status changes are announced."
|
|
114
|
+
],
|
|
115
|
+
[
|
|
116
|
+
"Platforms",
|
|
117
|
+
"React Native Animated and react-native-svg only — iOS, Android, Expo Go and web."
|
|
118
|
+
]
|
|
119
|
+
],
|
|
120
|
+
"usage": "import { Badge } from '@/components/ui/badge';\n\nexport function BuildStatus({ done }: { done: boolean }) {\n return (\n <Badge tone={done ? 'success' : 'warning'}>{done ? 'Deployed' : 'Building'}</Badge>\n );\n}",
|
|
121
|
+
"examples": [
|
|
122
|
+
{
|
|
123
|
+
"name": "Variants",
|
|
124
|
+
"title": "Variants",
|
|
125
|
+
"code": "import { Pressable, Text, View } from 'react-native';\nimport { Badge, BadgeIcon, BadgeIconTile, NotificationBadge, type BadgeIconName, type BadgeTone } from '@/components/ui/badge';\n\nconst TONES = ['neutral', 'accent', 'success', 'warning', 'danger'] as const;\n\nexport default function Variants() {\n return (\n <View style={{ gap: 10 }}>\n {(['solid', 'soft', 'outline', 'dot'] as const).map((variant) => (\n <View key={variant} style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 6 }}>\n {TONES.map((tone) => (\n <Badge key={tone} variant={variant} tone={tone}>\n {tone[0].toUpperCase() + tone.slice(1)}\n </Badge>\n ))}\n </View>\n ))}\n </View>\n );\n}\n"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"name": "Statuses",
|
|
129
|
+
"title": "Statuses",
|
|
130
|
+
"code": "import { Pressable, Text, View } from 'react-native';\nimport { Badge, BadgeIcon, BadgeIconTile, NotificationBadge, type BadgeIconName, type BadgeTone } from '@/components/ui/badge';\nimport { alpha, useKit } from '@/lib/kit';\n\nexport default function Statuses() {\n const { colors } = useKit();\n return (\n <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 8 }}>\n <Badge variant=\"dot\" tone=\"success\" pulse>Live</Badge>\n <Badge variant=\"soft\" tone=\"success\" icon={<BadgeIcon name=\"package\" color={colors.success} size={14} />}>Paid</Badge>\n <Badge variant=\"soft\" tone=\"warning\">Pending review</Badge>\n <Badge variant=\"outline\" tone=\"danger\">Overdue</Badge>\n <Badge variant=\"solid\" tone=\"accent\" shape=\"rounded\">PRO</Badge>\n <Badge variant=\"soft\" tone=\"accent\" icon={<BadgeIcon name=\"heart\" color={colors.accent} size={14} />}>Saved</Badge>\n </View>\n );\n}\n"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"name": "LiveStatus",
|
|
134
|
+
"title": "Live Status",
|
|
135
|
+
"code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { Badge, BadgeIcon, BadgeIconTile, NotificationBadge, type BadgeIconName, type BadgeTone } from '@/components/ui/badge';\nimport { alpha, useKit } from '@/lib/kit';\n\nconst DEPLOY: { label: string; tone: BadgeTone; icon: BadgeIconName }[] = [\n { label: 'Queued', tone: 'neutral', icon: 'clock' },\n { label: 'Building', tone: 'warning', icon: 'refresh' },\n { label: 'Deployed', tone: 'success', icon: 'check' },\n { label: 'Failed', tone: 'danger', icon: 'alert' },\n];\n\nexport default function LiveStatus() {\n const { colors } = useKit();\n const [step, setStep] = useState(0);\n useEffect(() => {\n const id = setInterval(() => setStep((i) => (i + 1) % DEPLOY.length), 1800);\n return () => clearInterval(id);\n }, []);\n const { label, tone, icon } = DEPLOY[step];\n const tint = { neutral: colors.mutedForeground, accent: colors.accent, success: colors.success, warning: colors.warning, danger: colors.danger }[tone];\n return (\n <View style={{ gap: 12 }}>\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>\n <Text style={{ fontSize: 15, color: colors.foreground }}>web-checkout #482</Text>\n <Badge tone={tone} icon={<BadgeIcon name={icon} color={tint} size={14} />}>\n {label}\n </Badge>\n </View>\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 10 }}>\n <Text style={{ fontSize: 15, color: colors.foreground }}>api-gateway #117</Text>\n <Badge variant=\"solid\" tone={tone} size=\"lg\">\n {label}\n </Badge>\n <Badge variant=\"dot\" tone={tone} pulse={tone === 'warning'}>\n {label}\n </Badge>\n </View>\n </View>\n );\n}\n"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"name": "Sizes",
|
|
139
|
+
"title": "Sizes",
|
|
140
|
+
"code": "import { Pressable, Text, View } from 'react-native';\nimport { Badge, BadgeIcon, BadgeIconTile, NotificationBadge, type BadgeIconName, type BadgeTone } from '@/components/ui/badge';\n\nexport default function Sizes() {\n return (\n <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>\n <Badge size=\"sm\" tone=\"accent\">Small</Badge>\n <Badge size=\"md\" tone=\"accent\">Medium</Badge>\n <Badge size=\"lg\" tone=\"accent\">Large</Badge>\n </View>\n );\n}\n"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"name": "Notifications",
|
|
144
|
+
"title": "Notifications",
|
|
145
|
+
"code": "import React, { useEffect, useState } from 'react';\nimport { Pressable, Text, View } from 'react-native';\nimport { Badge, BadgeIcon, BadgeIconTile, NotificationBadge, type BadgeIconName, type BadgeTone } from '@/components/ui/badge';\nimport { alpha, useKit } from '@/lib/kit';\n\nexport default function Notifications() {\n const { colors, radius } = useKit();\n const [count, setCount] = useState(3);\n return (\n <View style={{ gap: 18 }}>\n <View style={{ flexDirection: 'row', gap: 26 }}>\n <NotificationBadge count={count}>\n <BadgeIconTile name=\"bell\" backgroundColor={alpha(colors.accent, 0.12)} color={colors.accent} />\n </NotificationBadge>\n <NotificationBadge count={count * 37} tone=\"accent\">\n <BadgeIconTile name=\"message\" backgroundColor={alpha(colors.accent, 0.1)} color={colors.accent} />\n </NotificationBadge>\n <NotificationBadge count={count} dot tone=\"success\">\n <BadgeIconTile name=\"package\" backgroundColor={alpha(colors.success, 0.12)} color={colors.success} />\n </NotificationBadge>\n </View>\n <View style={{ flexDirection: 'row', gap: 10 }}>\n {[\n ['+ New', () => setCount((c) => c + 1)],\n ['Mark all read', () => setCount(0)],\n ].map(([label, onPress]) => (\n <Pressable\n key={label as string}\n onPress={onPress as () => void}\n style={{ paddingHorizontal: 14, height: 36, borderRadius: radius.capsule(36), justifyContent: 'center', backgroundColor: colors.muted }}\n >\n <Text style={{ fontWeight: '400', color: colors.foreground }}>{label as string}</Text>\n </Pressable>\n ))}\n </View>\n </View>\n );\n}\n"
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bottom-nav",
|
|
3
|
+
"category": "Navigation",
|
|
4
|
+
"aliases": [
|
|
5
|
+
"bottom-tabs",
|
|
6
|
+
"tab-bar",
|
|
7
|
+
"bottom-bar"
|
|
8
|
+
],
|
|
9
|
+
"exportName": "BottomNav",
|
|
10
|
+
"files": [
|
|
11
|
+
"src/components/ui/bottom-nav.tsx"
|
|
12
|
+
],
|
|
13
|
+
"summary": "A tab bar whose indicator stretches between tabs, leading edge first.",
|
|
14
|
+
"props": [
|
|
15
|
+
[
|
|
16
|
+
"items",
|
|
17
|
+
"{ key, label, icon, badge?, action? }[]",
|
|
18
|
+
"—",
|
|
19
|
+
"Tabs. `icon` is a built-in name (home, explore, search, projects, saved, profile, inbox, activity, analytics, message, work, browse, library, music, create) or a render function `({ color, size, active }) => node` — called for both the outline (`active: false`) and filled (`active: true`) states so it can morph."
|
|
20
|
+
],
|
|
21
|
+
[
|
|
22
|
+
"value / defaultValue",
|
|
23
|
+
"string",
|
|
24
|
+
"first tab",
|
|
25
|
+
"Active tab key (controlled / uncontrolled)."
|
|
26
|
+
],
|
|
27
|
+
[
|
|
28
|
+
"onValueChange",
|
|
29
|
+
"(key: string) => void",
|
|
30
|
+
"—",
|
|
31
|
+
"Called when a tab is pressed."
|
|
32
|
+
],
|
|
33
|
+
[
|
|
34
|
+
"onAction",
|
|
35
|
+
"(key: string) => void",
|
|
36
|
+
"—",
|
|
37
|
+
"Called when an `action: true` item (e.g. Create) is pressed. Action items render as a solid accent button and never become active."
|
|
38
|
+
],
|
|
39
|
+
[
|
|
40
|
+
"variant",
|
|
41
|
+
"\"minimal\" | \"floating\" | \"pill\" | \"center-fab\" | \"indicator\"",
|
|
42
|
+
"\"minimal\"",
|
|
43
|
+
"Look. Deprecated values still render: `glass`, `neumorphic`, `dock`, `curved` → `floating`; `gradient`, `outline` → `pill`."
|
|
44
|
+
],
|
|
45
|
+
[
|
|
46
|
+
"accent",
|
|
47
|
+
"string",
|
|
48
|
+
"theme accent",
|
|
49
|
+
"Active colour and the action button fill."
|
|
50
|
+
],
|
|
51
|
+
[
|
|
52
|
+
"gradient",
|
|
53
|
+
"[string, string]",
|
|
54
|
+
"none (solid accent)",
|
|
55
|
+
"Opt-in two-colour gradient for the action button."
|
|
56
|
+
],
|
|
57
|
+
[
|
|
58
|
+
"showLabels",
|
|
59
|
+
"boolean",
|
|
60
|
+
"true",
|
|
61
|
+
"Show labels. `floating` and `pill` reveal only the active label; the others show all."
|
|
62
|
+
],
|
|
63
|
+
[
|
|
64
|
+
"bottomInset",
|
|
65
|
+
"number",
|
|
66
|
+
"0",
|
|
67
|
+
"Extra bottom padding — pass `useSafeAreaInsets().bottom` when the bar sits at the screen edge."
|
|
68
|
+
],
|
|
69
|
+
[
|
|
70
|
+
"homeIndicator",
|
|
71
|
+
"boolean",
|
|
72
|
+
"minimal",
|
|
73
|
+
"Draw the iOS home-indicator line."
|
|
74
|
+
],
|
|
75
|
+
[
|
|
76
|
+
"haptics",
|
|
77
|
+
"boolean",
|
|
78
|
+
"true",
|
|
79
|
+
"Selection tick on tab change, light impact on the action button."
|
|
80
|
+
],
|
|
81
|
+
[
|
|
82
|
+
"style",
|
|
83
|
+
"StyleProp<ViewStyle>",
|
|
84
|
+
"—",
|
|
85
|
+
"Style of the bar (the outer shadow view for `floating`)."
|
|
86
|
+
]
|
|
87
|
+
],
|
|
88
|
+
"behavior": [
|
|
89
|
+
[
|
|
90
|
+
"Touch",
|
|
91
|
+
"Tap a tab to select it. Items marked action call onAction instead and never become the active tab."
|
|
92
|
+
],
|
|
93
|
+
[
|
|
94
|
+
"Motion",
|
|
95
|
+
"The indicator springs its leading edge first and the trailing edge catches up. Icons morph from outline to filled and badges pop in."
|
|
96
|
+
],
|
|
97
|
+
[
|
|
98
|
+
"Reduced motion",
|
|
99
|
+
"The indicator glides with a short timing and no stretch, and icon changes use quick timings instead of springs."
|
|
100
|
+
],
|
|
101
|
+
[
|
|
102
|
+
"Haptics",
|
|
103
|
+
"A selection tick on tab change and a light impact on the action button. Turn off with haptics={false}."
|
|
104
|
+
],
|
|
105
|
+
[
|
|
106
|
+
"Screen readers",
|
|
107
|
+
"The bar is a tablist and each tab carries selected state, with the badge count in its label. Action items are buttons."
|
|
108
|
+
]
|
|
109
|
+
],
|
|
110
|
+
"usage": "import { useState } from 'react';\nimport { BottomNav } from '@/components/ui/bottom-nav';\n\nconst [tab, setTab] = useState('home');\n<BottomNav\n items={[{ key: 'home', label: 'Home', icon: 'home' }, { key: 'inbox', label: 'Inbox', icon: 'inbox', badge: 3 }]}\n value={tab}\n onValueChange={setTab}\n/>",
|
|
111
|
+
"examples": [
|
|
112
|
+
{
|
|
113
|
+
"name": "MinimalClean",
|
|
114
|
+
"title": "Minimal Clean",
|
|
115
|
+
"code": "import React, { useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { BottomNav, type BottomNavItem } from '@/components/ui/bottom-nav';\nimport { alpha, useKit } from '@/lib/kit';\n\ntype Row = [title: string, subtitle: string, meta: string];\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, children }: { title: string; rows: Row[]; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={{\n height: 300,\n alignSelf: 'stretch',\n borderRadius: radius.scale(28),\n overflow: 'hidden',\n borderWidth: 1,\n borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06),\n backgroundColor: colors.background,\n justifyContent: 'space-between',\n }}\n >\n <View style={{ paddingHorizontal: 18, paddingTop: 18, gap: 14 }}>\n <Text style={{ fontSize: 22, fontWeight: '400', letterSpacing: -0.5, color: colors.foreground }}>{title}</Text>\n {rows.map(([t, s, m]) => (\n <View key={t} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View\n style={{\n width: 36,\n height: 36,\n borderRadius: radius.capsule(36),\n backgroundColor: alpha(colors.accent, dark ? 0.22 : 0.12),\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{t.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text numberOfLines={1} style={{ fontSize: 14.5, fontWeight: '400', color: colors.foreground }}>{t}</Text>\n <Text numberOfLines={1} style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{s}</Text>\n </View>\n <Text style={{ fontSize: 12, fontWeight: '400', color: colors.mutedForeground }}>{m}</Text>\n </View>\n ))}\n </View>\n {children}\n </View>\n );\n}\n\nconst ROWS: Record<string, Row[]> = {\n home: [\n ['Maya Chen', 'Shipped the onboarding redesign', '2m'],\n ['Leo Park', 'Booked the team offsite for June', '1h'],\n ],\n explore: [\n ['Design Systems Weekly', 'Motion tokens that scale', 'New'],\n ['Product Notes', 'How Linear plans a cycle', '4m read'],\n ],\n search: [\n ['Recent: invoices', 'Q2 invoices · 14 results', 'Today'],\n ['Recent: Maya', 'People · Design team', 'Mon'],\n ],\n projects: [\n ['Mobile app 2.0', '18 of 24 tasks done', '75%'],\n ['Brand refresh', 'Review on Thursday', '40%'],\n ],\n saved: [\n ['Kyoto travel guide', 'Saved from Atlas', 'Apr 2'],\n ['Sourdough basics', 'Saved from Recipes', 'Mar 28'],\n ],\n inbox: [\n ['Priya Nair', 'Can you review the pricing page?', '9:41'],\n ['GitHub', 'PR #482 was approved', '9:12'],\n ],\n analytics: [\n ['Weekly active users', '12,480 · up 8.2%', '7d'],\n ['Conversion', '3.4% · up 0.6 pts', '7d'],\n ],\n activity: [\n ['Leo Park', 'Commented on Launch plan', '5m'],\n ['Ana Ruiz', 'Moved Pricing to Done', '22m'],\n ],\n profile: [\n ['Karan Kumar', 'Product designer · Berlin', 'Edit'],\n ['Notifications', 'Mentions and replies only', 'On'],\n ],\n home2: [\n ['Late Night Drive', 'Playlist · 42 songs', '2h 31m'],\n ['Midnight City', 'M83 · Hurry Up, We\\'re Dreaming', '4:03'],\n ],\n browse: [\n ['New this week', 'Fresh releases picked for you', '24'],\n ['Focus', 'Instrumental, low tempo', '60'],\n ],\n find: [\n ['Recent: Tycho', 'Artist · Ambient', 'Yesterday'],\n ['Recent: lo-fi beats', 'Playlist · 120 songs', 'Sun'],\n ],\n library: [\n ['Liked songs', '1,204 songs', 'Pinned'],\n ['Downloaded', '86 songs · 412 MB', 'Offline'],\n ],\n};\n\nconst titleOf = (items: BottomNavItem[], key: string) => items.find((i) => i.key === key)?.label ?? '';\n\nexport default function MinimalClean() {\n const items: BottomNavItem[] = [\n { key: 'home', label: 'Home', icon: 'home' },\n { key: 'explore', label: 'Explore', icon: 'explore' },\n { key: 'projects', label: 'Projects', icon: 'projects' },\n { key: 'saved', label: 'Saved', icon: 'saved' },\n { key: 'profile', label: 'Profile', icon: 'profile' },\n ];\n const [tab, setTab] = useState('home');\n return (\n <Screen title={titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav variant=\"minimal\" items={items} value={tab} onValueChange={setTab} />\n </Screen>\n );\n}\n"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"name": "Floating",
|
|
119
|
+
"title": "Floating",
|
|
120
|
+
"code": "import React, { useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { BottomNav, type BottomNavItem } from '@/components/ui/bottom-nav';\nimport { alpha, useKit } from '@/lib/kit';\n\ntype Row = [title: string, subtitle: string, meta: string];\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, children }: { title: string; rows: Row[]; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={{\n height: 300,\n alignSelf: 'stretch',\n borderRadius: radius.scale(28),\n overflow: 'hidden',\n borderWidth: 1,\n borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06),\n backgroundColor: colors.background,\n justifyContent: 'space-between',\n }}\n >\n <View style={{ paddingHorizontal: 18, paddingTop: 18, gap: 14 }}>\n <Text style={{ fontSize: 22, fontWeight: '400', letterSpacing: -0.5, color: colors.foreground }}>{title}</Text>\n {rows.map(([t, s, m]) => (\n <View key={t} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View\n style={{\n width: 36,\n height: 36,\n borderRadius: radius.capsule(36),\n backgroundColor: alpha(colors.accent, dark ? 0.22 : 0.12),\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{t.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text numberOfLines={1} style={{ fontSize: 14.5, fontWeight: '400', color: colors.foreground }}>{t}</Text>\n <Text numberOfLines={1} style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{s}</Text>\n </View>\n <Text style={{ fontSize: 12, fontWeight: '400', color: colors.mutedForeground }}>{m}</Text>\n </View>\n ))}\n </View>\n {children}\n </View>\n );\n}\n\nconst ROWS: Record<string, Row[]> = {\n home: [\n ['Maya Chen', 'Shipped the onboarding redesign', '2m'],\n ['Leo Park', 'Booked the team offsite for June', '1h'],\n ],\n explore: [\n ['Design Systems Weekly', 'Motion tokens that scale', 'New'],\n ['Product Notes', 'How Linear plans a cycle', '4m read'],\n ],\n search: [\n ['Recent: invoices', 'Q2 invoices · 14 results', 'Today'],\n ['Recent: Maya', 'People · Design team', 'Mon'],\n ],\n projects: [\n ['Mobile app 2.0', '18 of 24 tasks done', '75%'],\n ['Brand refresh', 'Review on Thursday', '40%'],\n ],\n saved: [\n ['Kyoto travel guide', 'Saved from Atlas', 'Apr 2'],\n ['Sourdough basics', 'Saved from Recipes', 'Mar 28'],\n ],\n inbox: [\n ['Priya Nair', 'Can you review the pricing page?', '9:41'],\n ['GitHub', 'PR #482 was approved', '9:12'],\n ],\n analytics: [\n ['Weekly active users', '12,480 · up 8.2%', '7d'],\n ['Conversion', '3.4% · up 0.6 pts', '7d'],\n ],\n activity: [\n ['Leo Park', 'Commented on Launch plan', '5m'],\n ['Ana Ruiz', 'Moved Pricing to Done', '22m'],\n ],\n profile: [\n ['Karan Kumar', 'Product designer · Berlin', 'Edit'],\n ['Notifications', 'Mentions and replies only', 'On'],\n ],\n home2: [\n ['Late Night Drive', 'Playlist · 42 songs', '2h 31m'],\n ['Midnight City', 'M83 · Hurry Up, We\\'re Dreaming', '4:03'],\n ],\n browse: [\n ['New this week', 'Fresh releases picked for you', '24'],\n ['Focus', 'Instrumental, low tempo', '60'],\n ],\n find: [\n ['Recent: Tycho', 'Artist · Ambient', 'Yesterday'],\n ['Recent: lo-fi beats', 'Playlist · 120 songs', 'Sun'],\n ],\n library: [\n ['Liked songs', '1,204 songs', 'Pinned'],\n ['Downloaded', '86 songs · 412 MB', 'Offline'],\n ],\n};\n\nconst titleOf = (items: BottomNavItem[], key: string) => items.find((i) => i.key === key)?.label ?? '';\n\nexport default function Floating() {\n const [tab, setTab] = useState('home');\n const [unread, setUnread] = useState(3);\n const [drafts, setDrafts] = useState(0);\n const items: BottomNavItem[] = [\n { key: 'home', label: 'Home', icon: 'home' },\n { key: 'search', label: 'Search', icon: 'search' },\n { key: 'create', label: 'New note', icon: 'create', action: true },\n { key: 'inbox', label: 'Inbox', icon: 'inbox', badge: unread || undefined },\n { key: 'profile', label: 'Profile', icon: 'profile' },\n ];\n const open = (k: string) => {\n setTab(k);\n if (k === 'inbox') setUnread(0);\n };\n return (\n <Screen title={drafts ? `${drafts} new ${drafts === 1 ? 'draft' : 'drafts'}` : titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav variant=\"floating\" items={items} value={tab} onValueChange={open} onAction={() => setDrafts((n) => n + 1)} />\n </Screen>\n );\n}\n"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"name": "PillHighlight",
|
|
124
|
+
"title": "Pill Highlight",
|
|
125
|
+
"code": "import React, { useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { BottomNav, type BottomNavItem } from '@/components/ui/bottom-nav';\nimport { alpha, useKit } from '@/lib/kit';\n\ntype Row = [title: string, subtitle: string, meta: string];\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, children }: { title: string; rows: Row[]; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={{\n height: 300,\n alignSelf: 'stretch',\n borderRadius: radius.scale(28),\n overflow: 'hidden',\n borderWidth: 1,\n borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06),\n backgroundColor: colors.background,\n justifyContent: 'space-between',\n }}\n >\n <View style={{ paddingHorizontal: 18, paddingTop: 18, gap: 14 }}>\n <Text style={{ fontSize: 22, fontWeight: '400', letterSpacing: -0.5, color: colors.foreground }}>{title}</Text>\n {rows.map(([t, s, m]) => (\n <View key={t} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View\n style={{\n width: 36,\n height: 36,\n borderRadius: radius.capsule(36),\n backgroundColor: alpha(colors.accent, dark ? 0.22 : 0.12),\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{t.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text numberOfLines={1} style={{ fontSize: 14.5, fontWeight: '400', color: colors.foreground }}>{t}</Text>\n <Text numberOfLines={1} style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{s}</Text>\n </View>\n <Text style={{ fontSize: 12, fontWeight: '400', color: colors.mutedForeground }}>{m}</Text>\n </View>\n ))}\n </View>\n {children}\n </View>\n );\n}\n\nconst ROWS: Record<string, Row[]> = {\n home: [\n ['Maya Chen', 'Shipped the onboarding redesign', '2m'],\n ['Leo Park', 'Booked the team offsite for June', '1h'],\n ],\n explore: [\n ['Design Systems Weekly', 'Motion tokens that scale', 'New'],\n ['Product Notes', 'How Linear plans a cycle', '4m read'],\n ],\n search: [\n ['Recent: invoices', 'Q2 invoices · 14 results', 'Today'],\n ['Recent: Maya', 'People · Design team', 'Mon'],\n ],\n projects: [\n ['Mobile app 2.0', '18 of 24 tasks done', '75%'],\n ['Brand refresh', 'Review on Thursday', '40%'],\n ],\n saved: [\n ['Kyoto travel guide', 'Saved from Atlas', 'Apr 2'],\n ['Sourdough basics', 'Saved from Recipes', 'Mar 28'],\n ],\n inbox: [\n ['Priya Nair', 'Can you review the pricing page?', '9:41'],\n ['GitHub', 'PR #482 was approved', '9:12'],\n ],\n analytics: [\n ['Weekly active users', '12,480 · up 8.2%', '7d'],\n ['Conversion', '3.4% · up 0.6 pts', '7d'],\n ],\n activity: [\n ['Leo Park', 'Commented on Launch plan', '5m'],\n ['Ana Ruiz', 'Moved Pricing to Done', '22m'],\n ],\n profile: [\n ['Karan Kumar', 'Product designer · Berlin', 'Edit'],\n ['Notifications', 'Mentions and replies only', 'On'],\n ],\n home2: [\n ['Late Night Drive', 'Playlist · 42 songs', '2h 31m'],\n ['Midnight City', 'M83 · Hurry Up, We\\'re Dreaming', '4:03'],\n ],\n browse: [\n ['New this week', 'Fresh releases picked for you', '24'],\n ['Focus', 'Instrumental, low tempo', '60'],\n ],\n find: [\n ['Recent: Tycho', 'Artist · Ambient', 'Yesterday'],\n ['Recent: lo-fi beats', 'Playlist · 120 songs', 'Sun'],\n ],\n library: [\n ['Liked songs', '1,204 songs', 'Pinned'],\n ['Downloaded', '86 songs · 412 MB', 'Offline'],\n ],\n};\n\nconst titleOf = (items: BottomNavItem[], key: string) => items.find((i) => i.key === key)?.label ?? '';\n\nexport default function PillHighlight() {\n const items: BottomNavItem[] = [\n { key: 'home', label: 'Home', icon: 'home' },\n { key: 'projects', label: 'Projects', icon: 'projects' },\n { key: 'inbox', label: 'Inbox', icon: 'inbox', badge: true },\n { key: 'profile', label: 'Profile', icon: 'profile' },\n ];\n const [tab, setTab] = useState('home');\n return (\n <Screen title={titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav variant=\"pill\" items={items} value={tab} onValueChange={setTab} />\n </Screen>\n );\n}\n"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"name": "CenterFab",
|
|
129
|
+
"title": "Center Fab",
|
|
130
|
+
"code": "import React, { useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { BottomNav, type BottomNavItem } from '@/components/ui/bottom-nav';\nimport { alpha, useKit } from '@/lib/kit';\n\ntype Row = [title: string, subtitle: string, meta: string];\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, children }: { title: string; rows: Row[]; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={{\n height: 300,\n alignSelf: 'stretch',\n borderRadius: radius.scale(28),\n overflow: 'hidden',\n borderWidth: 1,\n borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06),\n backgroundColor: colors.background,\n justifyContent: 'space-between',\n }}\n >\n <View style={{ paddingHorizontal: 18, paddingTop: 18, gap: 14 }}>\n <Text style={{ fontSize: 22, fontWeight: '400', letterSpacing: -0.5, color: colors.foreground }}>{title}</Text>\n {rows.map(([t, s, m]) => (\n <View key={t} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View\n style={{\n width: 36,\n height: 36,\n borderRadius: radius.capsule(36),\n backgroundColor: alpha(colors.accent, dark ? 0.22 : 0.12),\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{t.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text numberOfLines={1} style={{ fontSize: 14.5, fontWeight: '400', color: colors.foreground }}>{t}</Text>\n <Text numberOfLines={1} style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{s}</Text>\n </View>\n <Text style={{ fontSize: 12, fontWeight: '400', color: colors.mutedForeground }}>{m}</Text>\n </View>\n ))}\n </View>\n {children}\n </View>\n );\n}\n\nconst ROWS: Record<string, Row[]> = {\n home: [\n ['Maya Chen', 'Shipped the onboarding redesign', '2m'],\n ['Leo Park', 'Booked the team offsite for June', '1h'],\n ],\n explore: [\n ['Design Systems Weekly', 'Motion tokens that scale', 'New'],\n ['Product Notes', 'How Linear plans a cycle', '4m read'],\n ],\n search: [\n ['Recent: invoices', 'Q2 invoices · 14 results', 'Today'],\n ['Recent: Maya', 'People · Design team', 'Mon'],\n ],\n projects: [\n ['Mobile app 2.0', '18 of 24 tasks done', '75%'],\n ['Brand refresh', 'Review on Thursday', '40%'],\n ],\n saved: [\n ['Kyoto travel guide', 'Saved from Atlas', 'Apr 2'],\n ['Sourdough basics', 'Saved from Recipes', 'Mar 28'],\n ],\n inbox: [\n ['Priya Nair', 'Can you review the pricing page?', '9:41'],\n ['GitHub', 'PR #482 was approved', '9:12'],\n ],\n analytics: [\n ['Weekly active users', '12,480 · up 8.2%', '7d'],\n ['Conversion', '3.4% · up 0.6 pts', '7d'],\n ],\n activity: [\n ['Leo Park', 'Commented on Launch plan', '5m'],\n ['Ana Ruiz', 'Moved Pricing to Done', '22m'],\n ],\n profile: [\n ['Karan Kumar', 'Product designer · Berlin', 'Edit'],\n ['Notifications', 'Mentions and replies only', 'On'],\n ],\n home2: [\n ['Late Night Drive', 'Playlist · 42 songs', '2h 31m'],\n ['Midnight City', 'M83 · Hurry Up, We\\'re Dreaming', '4:03'],\n ],\n browse: [\n ['New this week', 'Fresh releases picked for you', '24'],\n ['Focus', 'Instrumental, low tempo', '60'],\n ],\n find: [\n ['Recent: Tycho', 'Artist · Ambient', 'Yesterday'],\n ['Recent: lo-fi beats', 'Playlist · 120 songs', 'Sun'],\n ],\n library: [\n ['Liked songs', '1,204 songs', 'Pinned'],\n ['Downloaded', '86 songs · 412 MB', 'Offline'],\n ],\n};\n\nconst titleOf = (items: BottomNavItem[], key: string) => items.find((i) => i.key === key)?.label ?? '';\n\nexport default function CenterFab() {\n const [tab, setTab] = useState('home');\n const [posts, setPosts] = useState(0);\n const items: BottomNavItem[] = [\n { key: 'home', label: 'Home', icon: 'home' },\n { key: 'explore', label: 'Explore', icon: 'explore' },\n { key: 'create', label: 'Post', icon: 'create', action: true },\n { key: 'inbox', label: 'Inbox', icon: 'inbox', badge: 12 + posts },\n { key: 'profile', label: 'Profile', icon: 'profile' },\n ];\n return (\n <Screen title={posts ? `Posted ${posts} ${posts === 1 ? 'time' : 'times'}` : titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav variant=\"center-fab\" items={items} value={tab} onValueChange={setTab} onAction={() => setPosts((n) => n + 1)} />\n </Screen>\n );\n}\n"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"name": "TabWithIndicator",
|
|
134
|
+
"title": "Tab With Indicator",
|
|
135
|
+
"code": "import React, { useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { BottomNav, type BottomNavItem } from '@/components/ui/bottom-nav';\nimport { alpha, useKit } from '@/lib/kit';\n\ntype Row = [title: string, subtitle: string, meta: string];\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, children }: { title: string; rows: Row[]; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={{\n height: 300,\n alignSelf: 'stretch',\n borderRadius: radius.scale(28),\n overflow: 'hidden',\n borderWidth: 1,\n borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06),\n backgroundColor: colors.background,\n justifyContent: 'space-between',\n }}\n >\n <View style={{ paddingHorizontal: 18, paddingTop: 18, gap: 14 }}>\n <Text style={{ fontSize: 22, fontWeight: '400', letterSpacing: -0.5, color: colors.foreground }}>{title}</Text>\n {rows.map(([t, s, m]) => (\n <View key={t} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View\n style={{\n width: 36,\n height: 36,\n borderRadius: radius.capsule(36),\n backgroundColor: alpha(colors.accent, dark ? 0.22 : 0.12),\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{t.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text numberOfLines={1} style={{ fontSize: 14.5, fontWeight: '400', color: colors.foreground }}>{t}</Text>\n <Text numberOfLines={1} style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{s}</Text>\n </View>\n <Text style={{ fontSize: 12, fontWeight: '400', color: colors.mutedForeground }}>{m}</Text>\n </View>\n ))}\n </View>\n {children}\n </View>\n );\n}\n\nconst ROWS: Record<string, Row[]> = {\n home: [\n ['Maya Chen', 'Shipped the onboarding redesign', '2m'],\n ['Leo Park', 'Booked the team offsite for June', '1h'],\n ],\n explore: [\n ['Design Systems Weekly', 'Motion tokens that scale', 'New'],\n ['Product Notes', 'How Linear plans a cycle', '4m read'],\n ],\n search: [\n ['Recent: invoices', 'Q2 invoices · 14 results', 'Today'],\n ['Recent: Maya', 'People · Design team', 'Mon'],\n ],\n projects: [\n ['Mobile app 2.0', '18 of 24 tasks done', '75%'],\n ['Brand refresh', 'Review on Thursday', '40%'],\n ],\n saved: [\n ['Kyoto travel guide', 'Saved from Atlas', 'Apr 2'],\n ['Sourdough basics', 'Saved from Recipes', 'Mar 28'],\n ],\n inbox: [\n ['Priya Nair', 'Can you review the pricing page?', '9:41'],\n ['GitHub', 'PR #482 was approved', '9:12'],\n ],\n analytics: [\n ['Weekly active users', '12,480 · up 8.2%', '7d'],\n ['Conversion', '3.4% · up 0.6 pts', '7d'],\n ],\n activity: [\n ['Leo Park', 'Commented on Launch plan', '5m'],\n ['Ana Ruiz', 'Moved Pricing to Done', '22m'],\n ],\n profile: [\n ['Karan Kumar', 'Product designer · Berlin', 'Edit'],\n ['Notifications', 'Mentions and replies only', 'On'],\n ],\n home2: [\n ['Late Night Drive', 'Playlist · 42 songs', '2h 31m'],\n ['Midnight City', 'M83 · Hurry Up, We\\'re Dreaming', '4:03'],\n ],\n browse: [\n ['New this week', 'Fresh releases picked for you', '24'],\n ['Focus', 'Instrumental, low tempo', '60'],\n ],\n find: [\n ['Recent: Tycho', 'Artist · Ambient', 'Yesterday'],\n ['Recent: lo-fi beats', 'Playlist · 120 songs', 'Sun'],\n ],\n library: [\n ['Liked songs', '1,204 songs', 'Pinned'],\n ['Downloaded', '86 songs · 412 MB', 'Offline'],\n ],\n};\n\nconst titleOf = (items: BottomNavItem[], key: string) => items.find((i) => i.key === key)?.label ?? '';\n\nexport default function TabWithIndicator() {\n const items: BottomNavItem[] = [\n { key: 'home', label: 'Home', icon: 'home' },\n { key: 'analytics', label: 'Insights', icon: 'analytics' },\n { key: 'activity', label: 'Activity', icon: 'activity' },\n { key: 'inbox', label: 'Inbox', icon: 'inbox', badge: 5 },\n { key: 'profile', label: 'Profile', icon: 'profile' },\n ];\n const [tab, setTab] = useState('analytics');\n return (\n <Screen title={titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav variant=\"indicator\" items={items} value={tab} onValueChange={setTab} />\n </Screen>\n );\n}\n"
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"name": "MusicIconsOnly",
|
|
139
|
+
"title": "Music Icons Only",
|
|
140
|
+
"code": "import React, { useState } from 'react';\nimport { Text, View } from 'react-native';\nimport { BottomNav, type BottomNavItem } from '@/components/ui/bottom-nav';\nimport { alpha, useKit } from '@/lib/kit';\n\ntype Row = [title: string, subtitle: string, meta: string];\n\n/** The bottom of a phone screen: a title, a few rows, the bar docked at the bottom. */\nfunction Screen({ title, rows, children }: { title: string; rows: Row[]; children: React.ReactNode }) {\n const { colors, radius, dark } = useKit();\n return (\n <View\n style={{\n height: 300,\n alignSelf: 'stretch',\n borderRadius: radius.scale(28),\n overflow: 'hidden',\n borderWidth: 1,\n borderColor: alpha(colors.foreground, dark ? 0.1 : 0.06),\n backgroundColor: colors.background,\n justifyContent: 'space-between',\n }}\n >\n <View style={{ paddingHorizontal: 18, paddingTop: 18, gap: 14 }}>\n <Text style={{ fontSize: 22, fontWeight: '400', letterSpacing: -0.5, color: colors.foreground }}>{title}</Text>\n {rows.map(([t, s, m]) => (\n <View key={t} style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>\n <View\n style={{\n width: 36,\n height: 36,\n borderRadius: radius.capsule(36),\n backgroundColor: alpha(colors.accent, dark ? 0.22 : 0.12),\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <Text style={{ fontSize: 13, fontWeight: '400', color: colors.accent }}>{t.slice(0, 1)}</Text>\n </View>\n <View style={{ flex: 1 }}>\n <Text numberOfLines={1} style={{ fontSize: 14.5, fontWeight: '400', color: colors.foreground }}>{t}</Text>\n <Text numberOfLines={1} style={{ fontSize: 13, fontWeight: '400', color: colors.mutedForeground }}>{s}</Text>\n </View>\n <Text style={{ fontSize: 12, fontWeight: '400', color: colors.mutedForeground }}>{m}</Text>\n </View>\n ))}\n </View>\n {children}\n </View>\n );\n}\n\nconst ROWS: Record<string, Row[]> = {\n home: [\n ['Maya Chen', 'Shipped the onboarding redesign', '2m'],\n ['Leo Park', 'Booked the team offsite for June', '1h'],\n ],\n explore: [\n ['Design Systems Weekly', 'Motion tokens that scale', 'New'],\n ['Product Notes', 'How Linear plans a cycle', '4m read'],\n ],\n search: [\n ['Recent: invoices', 'Q2 invoices · 14 results', 'Today'],\n ['Recent: Maya', 'People · Design team', 'Mon'],\n ],\n projects: [\n ['Mobile app 2.0', '18 of 24 tasks done', '75%'],\n ['Brand refresh', 'Review on Thursday', '40%'],\n ],\n saved: [\n ['Kyoto travel guide', 'Saved from Atlas', 'Apr 2'],\n ['Sourdough basics', 'Saved from Recipes', 'Mar 28'],\n ],\n inbox: [\n ['Priya Nair', 'Can you review the pricing page?', '9:41'],\n ['GitHub', 'PR #482 was approved', '9:12'],\n ],\n analytics: [\n ['Weekly active users', '12,480 · up 8.2%', '7d'],\n ['Conversion', '3.4% · up 0.6 pts', '7d'],\n ],\n activity: [\n ['Leo Park', 'Commented on Launch plan', '5m'],\n ['Ana Ruiz', 'Moved Pricing to Done', '22m'],\n ],\n profile: [\n ['Karan Kumar', 'Product designer · Berlin', 'Edit'],\n ['Notifications', 'Mentions and replies only', 'On'],\n ],\n home2: [\n ['Late Night Drive', 'Playlist · 42 songs', '2h 31m'],\n ['Midnight City', 'M83 · Hurry Up, We\\'re Dreaming', '4:03'],\n ],\n browse: [\n ['New this week', 'Fresh releases picked for you', '24'],\n ['Focus', 'Instrumental, low tempo', '60'],\n ],\n find: [\n ['Recent: Tycho', 'Artist · Ambient', 'Yesterday'],\n ['Recent: lo-fi beats', 'Playlist · 120 songs', 'Sun'],\n ],\n library: [\n ['Liked songs', '1,204 songs', 'Pinned'],\n ['Downloaded', '86 songs · 412 MB', 'Offline'],\n ],\n};\n\nconst titleOf = (items: BottomNavItem[], key: string) => items.find((i) => i.key === key)?.label ?? '';\n\nexport default function MusicIconsOnly() {\n const items: BottomNavItem[] = [\n { key: 'home2', label: 'Listen now', icon: 'music' },\n { key: 'browse', label: 'Browse', icon: 'browse' },\n { key: 'library', label: 'Library', icon: 'library' },\n { key: 'find', label: 'Search', icon: 'search' },\n ];\n const [tab, setTab] = useState('home2');\n return (\n <Screen title={titleOf(items, tab)} rows={ROWS[tab]}>\n <BottomNav variant=\"floating\" showLabels={false} items={items} value={tab} onValueChange={setTab} />\n </Screen>\n );\n}\n"
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
}
|