@snowcone-app/ui 0.1.43 → 0.2.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/CHANGELOG.md +26 -0
- package/README.md +18 -4
- package/package.json +9 -5
- package/src/components/CanvasIsolationBoundary.tsx +202 -0
- package/src/components/LoadingOverlayPrism.tsx +251 -0
- package/src/composed/AddToCart.tsx +229 -0
- package/src/composed/ArtAlignment.tsx +703 -0
- package/src/composed/ArtSelector.tsx +290 -0
- package/src/composed/ArtworkCustomizer.tsx +212 -0
- package/src/composed/CanvasEditor.tsx +79 -0
- package/src/composed/ColorPicker.tsx +111 -0
- package/src/composed/CurrentSelectionDisplay.tsx +86 -0
- package/src/composed/HeroProductImage.tsx +1071 -0
- package/src/composed/Lightbox.index.ts +2 -0
- package/src/composed/Lightbox.tsx +230 -0
- package/src/composed/PlacementClipShapeSelector.tsx +88 -0
- package/src/composed/PlacementTabs.tsx +179 -0
- package/src/composed/ProductCard.tsx +298 -0
- package/src/composed/ProductGallery.tsx +54 -0
- package/src/composed/ProductImage.tsx +129 -0
- package/src/composed/ProductList.tsx +147 -0
- package/src/composed/ProductOptions.tsx +305 -0
- package/src/composed/RealtimeMockup.tsx +121 -0
- package/src/composed/TileCount.tsx +348 -0
- package/src/composed/carousels/HeroCarousel.tsx +240 -0
- package/src/composed/carousels/MobileProductCarousel.tsx +1002 -0
- package/src/composed/carousels/index.ts +11 -0
- package/src/composed/carousels/types.ts +58 -0
- package/src/composed/grids/MasonryGrid.tsx +238 -0
- package/src/composed/grids/index.ts +9 -0
- package/src/composed/search/CurrentRefinements.tsx +80 -0
- package/src/composed/search/Filters.tsx +49 -0
- package/src/composed/search/FiltersButton.tsx +57 -0
- package/src/composed/search/FiltersDrawer.tsx +375 -0
- package/src/composed/search/ProductGrid.tsx +118 -0
- package/src/composed/search/ProductHit.tsx +56 -0
- package/src/composed/search/SearchBox.tsx +109 -0
- package/src/composed/search/SearchProvider.tsx +136 -0
- package/src/composed/search/facetConfig.ts +16 -0
- package/src/composed/search/index.ts +22 -0
- package/src/composed/search/meilisearchAdapter.ts +20 -0
- package/src/composed/search/types.ts +22 -0
- package/src/composed/zoom/EnhancedImageViewer.tsx +505 -0
- package/src/composed/zoom/ResponsiveZoom.tsx +134 -0
- package/src/composed/zoom/ZoomOverlay.tsx +194 -0
- package/src/composed/zoom/index.ts +12 -0
- package/src/composed/zoom/types.ts +12 -0
- package/src/design-system/ColorPalette.tsx +126 -0
- package/src/design-system/ColorSwatch.tsx +49 -0
- package/src/design-system/DesignSystemPage.tsx +130 -0
- package/src/design-system/ThemeSwitcher.tsx +181 -0
- package/src/design-system/TypographyScale.tsx +106 -0
- package/src/design-system/index.ts +5 -0
- package/src/ecommerce/stories/HeroProductImage.stories.tsx +66 -0
- package/src/ecommerce/stories/PDPHeroGallery.stories.tsx +105 -0
- package/src/ecommerce/stories/PDPInfoPanel.stories.tsx +472 -0
- package/src/ecommerce/stories/PDPLayout.stories.tsx +365 -0
- package/src/hooks/useBrand.ts +41 -0
- package/src/hooks/useCanvasContext.ts +127 -0
- package/src/hooks/useDeviceDetection.ts +64 -0
- package/src/hooks/useFocusTrap.ts +70 -0
- package/src/hooks/useImagePreloader.ts +268 -0
- package/src/hooks/useImageTransition.ts +608 -0
- package/src/hooks/usePlacementsProcessor.ts +74 -0
- package/src/hooks/useProductGallery.ts +193 -0
- package/src/hooks/useProductPage.ts +467 -0
- package/src/hooks/useRenderGuard.ts +96 -0
- package/src/hooks/useScrollDirection.ts +196 -0
- package/src/hooks/viewport/index.ts +25 -0
- package/src/hooks/viewport/useContainerWidth.ts +59 -0
- package/src/hooks/viewport/useMediaQuery.ts +52 -0
- package/src/hooks/viewport/useResponsiveImageCap.ts +149 -0
- package/src/hooks/viewport/useViewportDimensions.ts +135 -0
- package/src/hooks/viewport/useWideMonitorMode.ts +150 -0
- package/src/hooks/visibility/index.ts +15 -0
- package/src/hooks/visibility/observerPool.ts +150 -0
- package/src/index.ts +240 -0
- package/src/layouts/hero-zoom/HeroShrinkLayout.tsx +209 -0
- package/src/layouts/hero-zoom/HeroZoomLayout.tsx +351 -0
- package/src/layouts/hero-zoom/index.ts +30 -0
- package/src/layouts/hero-zoom/stories/HeroZoomLayout.stories.tsx +350 -0
- package/src/layouts/hero-zoom/types.ts +113 -0
- package/src/layouts/hero-zoom/useHeroZoomScales.ts +156 -0
- package/src/layouts/index.ts +9 -0
- package/src/layouts/pdp/EdgeBlurBox.tsx +210 -0
- package/src/layouts/pdp/ImageBlurExtension.tsx +215 -0
- package/src/layouts/pdp/ImageEdgeBlur.tsx +215 -0
- package/src/layouts/pdp/PDPLayout.tsx +246 -0
- package/src/layouts/pdp/SimpleImageBlur.tsx +140 -0
- package/src/layouts/pdp/index.ts +40 -0
- package/src/lib/env.ts +15 -0
- package/src/lib/locale.ts +167 -0
- package/src/lib/router.tsx +46 -0
- package/src/lib/utils.ts +6 -0
- package/src/lightbox/README.md +77 -0
- package/src/next/index.tsx +26 -0
- package/src/patterns/MockupPriorityProvider.tsx +1014 -0
- package/src/patterns/Product.tsx +850 -0
- package/src/patterns/ProductPageProvider.tsx +224 -0
- package/src/patterns/RealtimeProvider.tsx +1162 -0
- package/src/patterns/ShopProvider.tsx +603 -0
- package/src/personalization/PersonalizationBridge.tsx +235 -0
- package/src/personalization/PersonalizationContext.ts +29 -0
- package/src/personalization/PersonalizationInputs.tsx +110 -0
- package/src/personalization/PersonalizationProvider.tsx +407 -0
- package/src/personalization/canvas-stub.d.ts +22 -0
- package/src/personalization/index.ts +43 -0
- package/src/personalization/types.ts +48 -0
- package/src/personalization/usePersonalization.ts +32 -0
- package/src/personalization/usePersonalizationShimmer.ts +159 -0
- package/src/personalization/utils.ts +59 -0
- package/src/primitives/BrandLogo.tsx +65 -0
- package/src/primitives/BrandName.tsx +51 -0
- package/src/primitives/Button.tsx +123 -0
- package/src/primitives/ColorSwatch.tsx +221 -0
- package/src/primitives/DragHintAnimation.tsx +190 -0
- package/src/primitives/EdgeSwipeGuards.tsx +60 -0
- package/src/primitives/FloatingActionGroup.tsx +176 -0
- package/src/primitives/ProductPrice.tsx +171 -0
- package/src/primitives/ProgressiveBlur.tsx +295 -0
- package/src/primitives/ThemeToggle.tsx +125 -0
- package/src/primitives/__tests__/story-coverage.test.ts +98 -0
- package/src/primitives/accordion.tsx +280 -0
- package/src/primitives/badge.tsx +137 -0
- package/src/primitives/card.tsx +61 -0
- package/src/primitives/checkbox.tsx +56 -0
- package/src/primitives/collapsible.tsx +51 -0
- package/src/primitives/drawer.tsx +828 -0
- package/src/primitives/dropdown-menu.tsx +197 -0
- package/src/primitives/fieldset.tsx +73 -0
- package/src/primitives/index.ts +138 -0
- package/src/primitives/input.tsx +91 -0
- package/src/primitives/kbd.tsx +130 -0
- package/src/primitives/label.tsx +20 -0
- package/src/primitives/link.tsx +182 -0
- package/src/primitives/popover.tsx +80 -0
- package/src/primitives/radio-group.tsx +79 -0
- package/src/primitives/scroll-fade.tsx +159 -0
- package/src/primitives/select.tsx +170 -0
- package/src/primitives/separator.tsx +25 -0
- package/src/primitives/slider.tsx +221 -0
- package/src/primitives/spinner.tsx +72 -0
- package/src/primitives/stories/Accordion.stories.tsx +121 -0
- package/src/primitives/stories/Badge.stories.tsx +221 -0
- package/src/primitives/stories/Button.stories.tsx +185 -0
- package/src/primitives/stories/Card.stories.tsx +171 -0
- package/src/primitives/stories/Checkbox.stories.tsx +214 -0
- package/src/primitives/stories/Collapsible.stories.tsx +230 -0
- package/src/primitives/stories/Drawer.stories.tsx +378 -0
- package/src/primitives/stories/DropdownMenu.stories.tsx +182 -0
- package/src/primitives/stories/Fieldset.stories.tsx +212 -0
- package/src/primitives/stories/Input.stories.tsx +172 -0
- package/src/primitives/stories/Kbd.stories.tsx +183 -0
- package/src/primitives/stories/Label.stories.tsx +98 -0
- package/src/primitives/stories/Link.stories.tsx +260 -0
- package/src/primitives/stories/Popover.stories.tsx +178 -0
- package/src/primitives/stories/RadioGroup.stories.tsx +205 -0
- package/src/primitives/stories/Select.stories.tsx +222 -0
- package/src/primitives/stories/Separator.stories.tsx +134 -0
- package/src/primitives/stories/Slider.stories.tsx +203 -0
- package/src/primitives/stories/Spinner.stories.tsx +142 -0
- package/src/primitives/stories/Surface.stories.tsx +257 -0
- package/src/primitives/stories/Switch.stories.tsx +131 -0
- package/src/primitives/stories/Tabs.stories.tsx +275 -0
- package/src/primitives/stories/TextField.stories.tsx +139 -0
- package/src/primitives/stories/Textarea.stories.tsx +148 -0
- package/src/primitives/stories/Tooltip.stories.tsx +119 -0
- package/src/primitives/surface.tsx +86 -0
- package/src/primitives/switch.tsx +35 -0
- package/src/primitives/tabs.tsx +206 -0
- package/src/primitives/text-field.tsx +84 -0
- package/src/primitives/textarea.tsx +50 -0
- package/src/primitives/tooltip.tsx +58 -0
- package/src/services/CanvasExportService.ts +518 -0
- package/src/styles/base.css +380 -0
- package/src/styles/defaults.css +280 -0
- package/src/styles/globals.css +1242 -0
- package/src/styles/index.css +17 -0
- package/src/styles/ne-themes.css +4740 -0
- package/src/styles/tailwind.css +11 -0
- package/src/styles/tokens.css +117 -0
- package/src/styles/utilities.css +188 -0
- package/src/themes/apply-theme.ts +449 -0
- package/src/themes/getThemeStyles.ts +454 -0
- package/src/themes/index.ts +48 -0
- package/src/themes/oklch-theme.ts +283 -0
- package/src/themes/presets.ts +989 -0
- package/src/themes/types.ts +386 -0
- package/src/themes/useTheme.tsx +450 -0
- package/src/utils/dev-warnings.ts +161 -0
- package/src/utils/devWarnings.ts +153 -0
- package/dist/styles.css +0 -1
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Surface } from '../surface';
|
|
3
|
+
import { Input } from '../input';
|
|
4
|
+
import { Label } from '../label';
|
|
5
|
+
import { Textarea } from '../textarea';
|
|
6
|
+
import { Checkbox } from '../checkbox';
|
|
7
|
+
import { RadioGroup, RadioGroupItem } from '../radio-group';
|
|
8
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../select';
|
|
9
|
+
import { Button } from '../button';
|
|
10
|
+
import { Icon } from '@iconify/react';
|
|
11
|
+
|
|
12
|
+
const meta: Meta<typeof Surface> = {
|
|
13
|
+
title: 'UI/Surface',
|
|
14
|
+
component: Surface,
|
|
15
|
+
parameters: {
|
|
16
|
+
layout: 'centered',
|
|
17
|
+
},
|
|
18
|
+
tags: ['autodocs'],
|
|
19
|
+
argTypes: {
|
|
20
|
+
variant: {
|
|
21
|
+
control: 'select',
|
|
22
|
+
options: ['transparent', 'default', 'secondary'],
|
|
23
|
+
},
|
|
24
|
+
asChild: {
|
|
25
|
+
control: 'boolean',
|
|
26
|
+
},
|
|
27
|
+
children: { control: false },
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default meta;
|
|
32
|
+
type Story = StoryObj<typeof meta>;
|
|
33
|
+
|
|
34
|
+
export const Default: Story = {
|
|
35
|
+
args: {
|
|
36
|
+
variant: 'default',
|
|
37
|
+
className: 'p-6 rounded-lg',
|
|
38
|
+
},
|
|
39
|
+
render: (args) => (
|
|
40
|
+
<Surface {...args}>
|
|
41
|
+
<div className="space-y-2">
|
|
42
|
+
<Label htmlFor="email">Email</Label>
|
|
43
|
+
<Input id="email" placeholder="Enter your email" />
|
|
44
|
+
</div>
|
|
45
|
+
</Surface>
|
|
46
|
+
),
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const AllVariants: Story = {
|
|
50
|
+
render: () => (
|
|
51
|
+
<div className="flex gap-6">
|
|
52
|
+
<div className="space-y-2">
|
|
53
|
+
<p className="text-sm text-foreground font-label">Transparent</p>
|
|
54
|
+
<Surface variant="transparent" className="p-6 rounded-lg w-64">
|
|
55
|
+
<div className="space-y-4">
|
|
56
|
+
<div className="space-y-2">
|
|
57
|
+
<Label htmlFor="name-transparent">Name</Label>
|
|
58
|
+
<Input id="name-transparent" placeholder="Your name" />
|
|
59
|
+
</div>
|
|
60
|
+
<div className="space-y-2">
|
|
61
|
+
<Label htmlFor="country-transparent">Country</Label>
|
|
62
|
+
<Select>
|
|
63
|
+
<SelectTrigger id="country-transparent">
|
|
64
|
+
<SelectValue placeholder="Select country" />
|
|
65
|
+
</SelectTrigger>
|
|
66
|
+
<SelectContent>
|
|
67
|
+
<SelectItem value="us">United States</SelectItem>
|
|
68
|
+
<SelectItem value="uk">United Kingdom</SelectItem>
|
|
69
|
+
</SelectContent>
|
|
70
|
+
</Select>
|
|
71
|
+
</div>
|
|
72
|
+
<RadioGroup defaultValue="email" className="space-y-2">
|
|
73
|
+
<div className="flex items-center space-x-2">
|
|
74
|
+
<RadioGroupItem value="email" id="contact-email-t" />
|
|
75
|
+
<Label htmlFor="contact-email-t">Email</Label>
|
|
76
|
+
</div>
|
|
77
|
+
<div className="flex items-center space-x-2">
|
|
78
|
+
<RadioGroupItem value="phone" id="contact-phone-t" />
|
|
79
|
+
<Label htmlFor="contact-phone-t">Phone</Label>
|
|
80
|
+
</div>
|
|
81
|
+
</RadioGroup>
|
|
82
|
+
<div className="flex items-center space-x-2">
|
|
83
|
+
<Checkbox id="terms-transparent" />
|
|
84
|
+
<Label htmlFor="terms-transparent">Accept terms</Label>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
</Surface>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div className="space-y-2">
|
|
91
|
+
<p className="text-sm text-foreground font-label">Default</p>
|
|
92
|
+
<Surface variant="default" className="p-6 rounded-lg w-64">
|
|
93
|
+
<div className="space-y-4">
|
|
94
|
+
<div className="space-y-2">
|
|
95
|
+
<Label htmlFor="name-default">Name</Label>
|
|
96
|
+
<Input id="name-default" placeholder="Your name" />
|
|
97
|
+
</div>
|
|
98
|
+
<div className="space-y-2">
|
|
99
|
+
<Label htmlFor="country-default">Country</Label>
|
|
100
|
+
<Select>
|
|
101
|
+
<SelectTrigger id="country-default">
|
|
102
|
+
<SelectValue placeholder="Select country" />
|
|
103
|
+
</SelectTrigger>
|
|
104
|
+
<SelectContent>
|
|
105
|
+
<SelectItem value="us">United States</SelectItem>
|
|
106
|
+
<SelectItem value="uk">United Kingdom</SelectItem>
|
|
107
|
+
</SelectContent>
|
|
108
|
+
</Select>
|
|
109
|
+
</div>
|
|
110
|
+
<RadioGroup defaultValue="email" className="space-y-2">
|
|
111
|
+
<div className="flex items-center space-x-2">
|
|
112
|
+
<RadioGroupItem value="email" id="contact-email-d" />
|
|
113
|
+
<Label htmlFor="contact-email-d">Email</Label>
|
|
114
|
+
</div>
|
|
115
|
+
<div className="flex items-center space-x-2">
|
|
116
|
+
<RadioGroupItem value="phone" id="contact-phone-d" />
|
|
117
|
+
<Label htmlFor="contact-phone-d">Phone</Label>
|
|
118
|
+
</div>
|
|
119
|
+
</RadioGroup>
|
|
120
|
+
<div className="flex items-center space-x-2">
|
|
121
|
+
<Checkbox id="terms-default" />
|
|
122
|
+
<Label htmlFor="terms-default">Accept terms</Label>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
</Surface>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<div className="space-y-2">
|
|
129
|
+
<p className="text-sm text-foreground font-label">Secondary</p>
|
|
130
|
+
<Surface variant="secondary" className="p-6 rounded-lg w-64">
|
|
131
|
+
<p className="text-sm text-foreground/70">
|
|
132
|
+
Do not use form fields on this surface.
|
|
133
|
+
<br />
|
|
134
|
+
Use for content sections only.
|
|
135
|
+
</p>
|
|
136
|
+
</Surface>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
),
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export const WithForm: Story = {
|
|
143
|
+
render: () => (
|
|
144
|
+
<Surface variant="default" className="p-6 rounded-lg w-80">
|
|
145
|
+
<div className="space-y-4">
|
|
146
|
+
<h3 className="text-lg text-foreground font-heading">Contact Form</h3>
|
|
147
|
+
|
|
148
|
+
<div className="space-y-2">
|
|
149
|
+
<Label htmlFor="fullname">Full Name</Label>
|
|
150
|
+
<Input id="fullname" placeholder="John Doe" />
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<div className="space-y-2">
|
|
154
|
+
<Label htmlFor="contact-email">Email Address</Label>
|
|
155
|
+
<Input id="contact-email" type="email" placeholder="john@example.com" />
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<div className="space-y-2">
|
|
159
|
+
<Label htmlFor="message">Message</Label>
|
|
160
|
+
<Textarea id="message" placeholder="Your message here..." className="min-h-[100px]" />
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
</Surface>
|
|
164
|
+
),
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const ButtonShowcase = ({ title }: { title: string }) => (
|
|
168
|
+
<div className="space-y-4">
|
|
169
|
+
<p className="text-sm text-foreground font-label">{title}</p>
|
|
170
|
+
|
|
171
|
+
{/* All variants */}
|
|
172
|
+
<div className="space-y-3">
|
|
173
|
+
<div className="flex flex-wrap gap-2">
|
|
174
|
+
<Button variant="default">Default</Button>
|
|
175
|
+
<Button variant="secondary">Secondary</Button>
|
|
176
|
+
<Button variant="field">Field</Button>
|
|
177
|
+
<Button variant="outline">Outline</Button>
|
|
178
|
+
<Button variant="ghost">Ghost</Button>
|
|
179
|
+
<Button variant="link">Link</Button>
|
|
180
|
+
<Button variant="destructive">Destructive</Button>
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
{/* With icons */}
|
|
184
|
+
<div className="flex flex-wrap gap-2">
|
|
185
|
+
<Button variant="default">
|
|
186
|
+
<Icon icon="gravity-ui:plus" className="size-4" />
|
|
187
|
+
Add Item
|
|
188
|
+
</Button>
|
|
189
|
+
<Button variant="secondary">
|
|
190
|
+
<Icon icon="gravity-ui:gear" className="size-4" />
|
|
191
|
+
Settings
|
|
192
|
+
</Button>
|
|
193
|
+
<Button variant="field">
|
|
194
|
+
<Icon icon="gravity-ui:filter" className="size-4" />
|
|
195
|
+
Filters
|
|
196
|
+
</Button>
|
|
197
|
+
<Button variant="outline">
|
|
198
|
+
<Icon icon="gravity-ui:pencil" className="size-4" />
|
|
199
|
+
Edit
|
|
200
|
+
</Button>
|
|
201
|
+
<Button variant="ghost">
|
|
202
|
+
<Icon icon="gravity-ui:trash-bin" className="size-4" />
|
|
203
|
+
Delete
|
|
204
|
+
</Button>
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
{/* Icon only */}
|
|
208
|
+
<div className="flex flex-wrap gap-2">
|
|
209
|
+
<Button variant="default" size="icon">
|
|
210
|
+
<Icon icon="gravity-ui:plus" className="size-4" />
|
|
211
|
+
</Button>
|
|
212
|
+
<Button variant="secondary" size="icon">
|
|
213
|
+
<Icon icon="gravity-ui:gear" className="size-4" />
|
|
214
|
+
</Button>
|
|
215
|
+
<Button variant="field" size="icon">
|
|
216
|
+
<Icon icon="gravity-ui:filter" className="size-4" />
|
|
217
|
+
</Button>
|
|
218
|
+
<Button variant="outline" size="icon">
|
|
219
|
+
<Icon icon="gravity-ui:pencil" className="size-4" />
|
|
220
|
+
</Button>
|
|
221
|
+
<Button variant="ghost" size="icon">
|
|
222
|
+
<Icon icon="gravity-ui:trash-bin" className="size-4" />
|
|
223
|
+
</Button>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
{/* Sizes */}
|
|
227
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
228
|
+
<Button variant="default" size="sm">Small</Button>
|
|
229
|
+
<Button variant="default" size="default">Default</Button>
|
|
230
|
+
<Button variant="default" size="lg">Large</Button>
|
|
231
|
+
</div>
|
|
232
|
+
|
|
233
|
+
{/* States */}
|
|
234
|
+
<div className="flex flex-wrap gap-2">
|
|
235
|
+
<Button variant="default" disabled>Disabled</Button>
|
|
236
|
+
<Button variant="secondary" disabled>Disabled</Button>
|
|
237
|
+
<Button variant="field" disabled>Disabled</Button>
|
|
238
|
+
<Button variant="outline" disabled>Disabled</Button>
|
|
239
|
+
<Button variant="ghost" disabled>Disabled</Button>
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
</div>
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
export const WithButtons: Story = {
|
|
246
|
+
render: () => (
|
|
247
|
+
<div className="flex gap-6">
|
|
248
|
+
<Surface variant="transparent" className="p-6 rounded-lg">
|
|
249
|
+
<ButtonShowcase title="Transparent (Page Background)" />
|
|
250
|
+
</Surface>
|
|
251
|
+
|
|
252
|
+
<Surface variant="default" className="p-6 rounded-lg">
|
|
253
|
+
<ButtonShowcase title="Default Surface" />
|
|
254
|
+
</Surface>
|
|
255
|
+
</div>
|
|
256
|
+
),
|
|
257
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Switch } from '../switch';
|
|
4
|
+
import { Label } from '../label';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Switch> = {
|
|
7
|
+
title: 'UI/Switch',
|
|
8
|
+
component: Switch,
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: 'centered',
|
|
11
|
+
},
|
|
12
|
+
tags: ['autodocs'],
|
|
13
|
+
argTypes: {
|
|
14
|
+
checked: {
|
|
15
|
+
control: 'boolean',
|
|
16
|
+
description: 'Checked state',
|
|
17
|
+
},
|
|
18
|
+
disabled: {
|
|
19
|
+
control: 'boolean',
|
|
20
|
+
description: 'Disable the switch',
|
|
21
|
+
},
|
|
22
|
+
onCheckedChange: {
|
|
23
|
+
control: false,
|
|
24
|
+
description: 'Callback when checked state changes',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default meta;
|
|
30
|
+
type Story = StoryObj<typeof meta>;
|
|
31
|
+
|
|
32
|
+
export const Default: Story = {
|
|
33
|
+
args: {},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const WithLabel: Story = {
|
|
37
|
+
render: () => (
|
|
38
|
+
<div className="flex items-center space-x-2">
|
|
39
|
+
<Switch id="airplane-mode" />
|
|
40
|
+
<Label htmlFor="airplane-mode">Airplane Mode</Label>
|
|
41
|
+
</div>
|
|
42
|
+
),
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const Controlled: Story = {
|
|
46
|
+
render: () => {
|
|
47
|
+
const [checked, setChecked] = useState(false);
|
|
48
|
+
return (
|
|
49
|
+
<div className="space-y-4">
|
|
50
|
+
<div className="flex items-center space-x-2">
|
|
51
|
+
<Switch checked={checked} onCheckedChange={setChecked} id="controlled" />
|
|
52
|
+
<Label htmlFor="controlled">Notifications</Label>
|
|
53
|
+
</div>
|
|
54
|
+
<p className="text-sm text-muted-foreground">
|
|
55
|
+
Notifications are {checked ? 'enabled' : 'disabled'}
|
|
56
|
+
</p>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const Checked: Story = {
|
|
63
|
+
args: {
|
|
64
|
+
checked: true,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const Disabled: Story = {
|
|
69
|
+
render: () => (
|
|
70
|
+
<div className="space-y-4">
|
|
71
|
+
<div className="flex items-center space-x-2">
|
|
72
|
+
<Switch disabled id="disabled-off" />
|
|
73
|
+
<Label htmlFor="disabled-off" className="opacity-50">Disabled (off)</Label>
|
|
74
|
+
</div>
|
|
75
|
+
<div className="flex items-center space-x-2">
|
|
76
|
+
<Switch disabled defaultChecked id="disabled-on" />
|
|
77
|
+
<Label htmlFor="disabled-on" className="opacity-50">Disabled (on)</Label>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
),
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const FormExample: Story = {
|
|
84
|
+
render: () => {
|
|
85
|
+
const [settings, setSettings] = useState({
|
|
86
|
+
marketing: false,
|
|
87
|
+
security: true,
|
|
88
|
+
updates: true,
|
|
89
|
+
});
|
|
90
|
+
return (
|
|
91
|
+
<div className="w-full max-w-sm space-y-4">
|
|
92
|
+
<h3 className="text-lg font-heading">Email Preferences</h3>
|
|
93
|
+
<div className="space-y-4">
|
|
94
|
+
<div className="flex items-center justify-between">
|
|
95
|
+
<div className="space-y-0.5">
|
|
96
|
+
<Label htmlFor="marketing">Marketing emails</Label>
|
|
97
|
+
<p className="text-sm text-muted-foreground">Receive emails about new products</p>
|
|
98
|
+
</div>
|
|
99
|
+
<Switch
|
|
100
|
+
id="marketing"
|
|
101
|
+
checked={settings.marketing}
|
|
102
|
+
onCheckedChange={(checked) => setSettings({ ...settings, marketing: checked })}
|
|
103
|
+
/>
|
|
104
|
+
</div>
|
|
105
|
+
<div className="flex items-center justify-between">
|
|
106
|
+
<div className="space-y-0.5">
|
|
107
|
+
<Label htmlFor="security">Security emails</Label>
|
|
108
|
+
<p className="text-sm text-muted-foreground">Receive emails about security</p>
|
|
109
|
+
</div>
|
|
110
|
+
<Switch
|
|
111
|
+
id="security"
|
|
112
|
+
checked={settings.security}
|
|
113
|
+
onCheckedChange={(checked) => setSettings({ ...settings, security: checked })}
|
|
114
|
+
/>
|
|
115
|
+
</div>
|
|
116
|
+
<div className="flex items-center justify-between">
|
|
117
|
+
<div className="space-y-0.5">
|
|
118
|
+
<Label htmlFor="updates">Product updates</Label>
|
|
119
|
+
<p className="text-sm text-muted-foreground">Receive emails about updates</p>
|
|
120
|
+
</div>
|
|
121
|
+
<Switch
|
|
122
|
+
id="updates"
|
|
123
|
+
checked={settings.updates}
|
|
124
|
+
onCheckedChange={(checked) => setSettings({ ...settings, updates: checked })}
|
|
125
|
+
/>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
);
|
|
130
|
+
},
|
|
131
|
+
};
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Icon } from '@iconify/react';
|
|
4
|
+
import { Tabs, TabsList, TabsTrigger, TabsContent } from '../tabs';
|
|
5
|
+
import { Label } from '../label';
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof Tabs> = {
|
|
8
|
+
title: 'UI/Tabs',
|
|
9
|
+
component: Tabs,
|
|
10
|
+
parameters: {
|
|
11
|
+
layout: 'centered',
|
|
12
|
+
},
|
|
13
|
+
tags: ['autodocs'],
|
|
14
|
+
argTypes: {
|
|
15
|
+
value: {
|
|
16
|
+
control: 'text',
|
|
17
|
+
description: 'Active tab value',
|
|
18
|
+
},
|
|
19
|
+
defaultValue: {
|
|
20
|
+
control: 'text',
|
|
21
|
+
description: 'Default active tab',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default meta;
|
|
27
|
+
type Story = StoryObj<typeof meta>;
|
|
28
|
+
|
|
29
|
+
export const Default: Story = {
|
|
30
|
+
render: () => (
|
|
31
|
+
<Tabs defaultValue="account" className="w-[400px]">
|
|
32
|
+
<TabsList>
|
|
33
|
+
<TabsTrigger value="account">Account</TabsTrigger>
|
|
34
|
+
<TabsTrigger value="password">Password</TabsTrigger>
|
|
35
|
+
</TabsList>
|
|
36
|
+
<TabsContent value="account">
|
|
37
|
+
<div className="space-y-2 p-4">
|
|
38
|
+
<h3 className="text-lg text-foreground font-heading">Account</h3>
|
|
39
|
+
<p className="text-sm text-muted-foreground font-body">
|
|
40
|
+
Make changes to your account here. Click save when you're done.
|
|
41
|
+
</p>
|
|
42
|
+
</div>
|
|
43
|
+
</TabsContent>
|
|
44
|
+
<TabsContent value="password">
|
|
45
|
+
<div className="space-y-2 p-4">
|
|
46
|
+
<h3 className="text-lg text-foreground font-heading">Password</h3>
|
|
47
|
+
<p className="text-sm text-muted-foreground font-body">
|
|
48
|
+
Change your password here. After saving, you'll be logged out.
|
|
49
|
+
</p>
|
|
50
|
+
</div>
|
|
51
|
+
</TabsContent>
|
|
52
|
+
</Tabs>
|
|
53
|
+
),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const Controlled: Story = {
|
|
57
|
+
render: () => {
|
|
58
|
+
const [value, setValue] = useState('tab1');
|
|
59
|
+
return (
|
|
60
|
+
<div className="space-y-4">
|
|
61
|
+
<Tabs value={value} onValueChange={setValue} className="w-[400px]">
|
|
62
|
+
<TabsList>
|
|
63
|
+
<TabsTrigger value="tab1">Overview</TabsTrigger>
|
|
64
|
+
<TabsTrigger value="tab2">Analytics</TabsTrigger>
|
|
65
|
+
<TabsTrigger value="tab3">Reports</TabsTrigger>
|
|
66
|
+
</TabsList>
|
|
67
|
+
<TabsContent value="tab1">
|
|
68
|
+
<div className="p-4">Overview content</div>
|
|
69
|
+
</TabsContent>
|
|
70
|
+
<TabsContent value="tab2">
|
|
71
|
+
<div className="p-4">Analytics content</div>
|
|
72
|
+
</TabsContent>
|
|
73
|
+
<TabsContent value="tab3">
|
|
74
|
+
<div className="p-4">Reports content</div>
|
|
75
|
+
</TabsContent>
|
|
76
|
+
</Tabs>
|
|
77
|
+
<p className="text-sm text-muted-foreground">Active tab: {value}</p>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const ControlledWithId: Story = {
|
|
84
|
+
render: () => {
|
|
85
|
+
const [value, setValue] = useState('photos');
|
|
86
|
+
return (
|
|
87
|
+
<div className="space-y-4">
|
|
88
|
+
<Tabs
|
|
89
|
+
value={value}
|
|
90
|
+
onValueChange={setValue}
|
|
91
|
+
className="w-[400px]"
|
|
92
|
+
>
|
|
93
|
+
<TabsList>
|
|
94
|
+
<TabsTrigger id="photos">Photos</TabsTrigger>
|
|
95
|
+
<TabsTrigger id="music">Music</TabsTrigger>
|
|
96
|
+
<TabsTrigger id="videos">Videos</TabsTrigger>
|
|
97
|
+
</TabsList>
|
|
98
|
+
<TabsContent id="photos">
|
|
99
|
+
<div className="p-4">Your photos collection</div>
|
|
100
|
+
</TabsContent>
|
|
101
|
+
<TabsContent id="music">
|
|
102
|
+
<div className="p-4">Your music library</div>
|
|
103
|
+
</TabsContent>
|
|
104
|
+
<TabsContent id="videos">
|
|
105
|
+
<div className="p-4">Your video collection</div>
|
|
106
|
+
</TabsContent>
|
|
107
|
+
</Tabs>
|
|
108
|
+
<p className="text-sm text-muted-foreground">
|
|
109
|
+
Active tab: {value}
|
|
110
|
+
</p>
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export const WithForms: Story = {
|
|
117
|
+
render: () => (
|
|
118
|
+
<Tabs defaultValue="account" className="w-[400px]">
|
|
119
|
+
<TabsList className="grid w-full grid-cols-2">
|
|
120
|
+
<TabsTrigger value="account">Account</TabsTrigger>
|
|
121
|
+
<TabsTrigger value="password">Password</TabsTrigger>
|
|
122
|
+
</TabsList>
|
|
123
|
+
<TabsContent value="account">
|
|
124
|
+
<div className="space-y-4 p-4 border border-divider rounded-lg mt-2">
|
|
125
|
+
<div className="space-y-2">
|
|
126
|
+
<h3 className="text-lg font-heading">Account</h3>
|
|
127
|
+
<p className="text-sm text-muted-foreground font-body">
|
|
128
|
+
Make changes to your account here.
|
|
129
|
+
</p>
|
|
130
|
+
</div>
|
|
131
|
+
<div className="space-y-2">
|
|
132
|
+
<Label htmlFor="name">Name</Label>
|
|
133
|
+
<input
|
|
134
|
+
id="name"
|
|
135
|
+
defaultValue="John Doe"
|
|
136
|
+
className="flex h-9 w-full rounded-md border border-divider bg-transparent px-3 py-1 text-sm"
|
|
137
|
+
/>
|
|
138
|
+
</div>
|
|
139
|
+
<div className="space-y-2">
|
|
140
|
+
<Label htmlFor="username">Username</Label>
|
|
141
|
+
<input
|
|
142
|
+
id="username"
|
|
143
|
+
defaultValue="@johndoe"
|
|
144
|
+
className="flex h-9 w-full rounded-md border border-divider bg-transparent px-3 py-1 text-sm"
|
|
145
|
+
/>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</TabsContent>
|
|
149
|
+
<TabsContent value="password">
|
|
150
|
+
<div className="space-y-4 p-4 border border-divider rounded-lg mt-2">
|
|
151
|
+
<div className="space-y-2">
|
|
152
|
+
<h3 className="text-lg font-heading">Password</h3>
|
|
153
|
+
<p className="text-sm text-muted-foreground font-body">Change your password here.</p>
|
|
154
|
+
</div>
|
|
155
|
+
<div className="space-y-2">
|
|
156
|
+
<Label htmlFor="current">Current password</Label>
|
|
157
|
+
<input
|
|
158
|
+
id="current"
|
|
159
|
+
type="password"
|
|
160
|
+
className="flex h-9 w-full rounded-md border border-divider bg-transparent px-3 py-1 text-sm"
|
|
161
|
+
/>
|
|
162
|
+
</div>
|
|
163
|
+
<div className="space-y-2">
|
|
164
|
+
<Label htmlFor="new">New password</Label>
|
|
165
|
+
<input
|
|
166
|
+
id="new"
|
|
167
|
+
type="password"
|
|
168
|
+
className="flex h-9 w-full rounded-md border border-divider bg-transparent px-3 py-1 text-sm"
|
|
169
|
+
/>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
</TabsContent>
|
|
173
|
+
</Tabs>
|
|
174
|
+
),
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
export const Disabled: Story = {
|
|
178
|
+
render: () => (
|
|
179
|
+
<Tabs defaultValue="active" className="w-[400px]">
|
|
180
|
+
<TabsList>
|
|
181
|
+
<TabsTrigger value="active">Active</TabsTrigger>
|
|
182
|
+
<TabsTrigger value="disabled" disabled>
|
|
183
|
+
Disabled
|
|
184
|
+
</TabsTrigger>
|
|
185
|
+
<TabsTrigger value="another">Another</TabsTrigger>
|
|
186
|
+
</TabsList>
|
|
187
|
+
<TabsContent value="active">
|
|
188
|
+
<div className="p-4">Active tab content</div>
|
|
189
|
+
</TabsContent>
|
|
190
|
+
<TabsContent value="disabled">
|
|
191
|
+
<div className="p-4">This content is not accessible</div>
|
|
192
|
+
</TabsContent>
|
|
193
|
+
<TabsContent value="another">
|
|
194
|
+
<div className="p-4">Another tab content</div>
|
|
195
|
+
</TabsContent>
|
|
196
|
+
</Tabs>
|
|
197
|
+
),
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
export const WithIcons: Story = {
|
|
201
|
+
name: 'With Icons (Pill Style)',
|
|
202
|
+
render: () => (
|
|
203
|
+
<Tabs defaultValue="chats" className="w-[350px]">
|
|
204
|
+
<TabsList className="w-full">
|
|
205
|
+
<TabsTrigger value="chats" className="flex-1">
|
|
206
|
+
<Icon icon="gravity-ui:comment" className="size-4" />
|
|
207
|
+
Chats
|
|
208
|
+
</TabsTrigger>
|
|
209
|
+
<TabsTrigger value="emails" className="flex-1">
|
|
210
|
+
<Icon icon="gravity-ui:envelope" className="size-4" />
|
|
211
|
+
Emails
|
|
212
|
+
</TabsTrigger>
|
|
213
|
+
</TabsList>
|
|
214
|
+
<TabsContent value="chats">
|
|
215
|
+
<div className="p-4">Your chat messages</div>
|
|
216
|
+
</TabsContent>
|
|
217
|
+
<TabsContent value="emails">
|
|
218
|
+
<div className="p-4">Your email inbox</div>
|
|
219
|
+
</TabsContent>
|
|
220
|
+
</Tabs>
|
|
221
|
+
),
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
export const PillStyleTextOnly: Story = {
|
|
225
|
+
name: 'Pill Style (Text Only)',
|
|
226
|
+
render: () => (
|
|
227
|
+
<Tabs defaultValue="account" className="w-[300px]">
|
|
228
|
+
<TabsList className="w-full">
|
|
229
|
+
<TabsTrigger value="account" className="flex-1">
|
|
230
|
+
Account
|
|
231
|
+
</TabsTrigger>
|
|
232
|
+
<TabsTrigger value="password" className="flex-1">
|
|
233
|
+
Password
|
|
234
|
+
</TabsTrigger>
|
|
235
|
+
</TabsList>
|
|
236
|
+
<TabsContent value="account">
|
|
237
|
+
<div className="p-4">Account settings</div>
|
|
238
|
+
</TabsContent>
|
|
239
|
+
<TabsContent value="password">
|
|
240
|
+
<div className="p-4">Password settings</div>
|
|
241
|
+
</TabsContent>
|
|
242
|
+
</Tabs>
|
|
243
|
+
),
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
export const ThreeTabs: Story = {
|
|
247
|
+
render: () => (
|
|
248
|
+
<Tabs defaultValue="inbox" className="w-[450px]">
|
|
249
|
+
<TabsList className="w-full">
|
|
250
|
+
<TabsTrigger value="inbox" className="flex-1">
|
|
251
|
+
<Icon icon="gravity-ui:envelope-open" className="size-4" />
|
|
252
|
+
Inbox
|
|
253
|
+
</TabsTrigger>
|
|
254
|
+
<TabsTrigger value="sent" className="flex-1">
|
|
255
|
+
<Icon icon="gravity-ui:arrow-up-from-square" className="size-4" />
|
|
256
|
+
Sent
|
|
257
|
+
</TabsTrigger>
|
|
258
|
+
<TabsTrigger value="drafts" className="flex-1">
|
|
259
|
+
<Icon icon="gravity-ui:file" className="size-4" />
|
|
260
|
+
Drafts
|
|
261
|
+
</TabsTrigger>
|
|
262
|
+
</TabsList>
|
|
263
|
+
<TabsContent value="inbox">
|
|
264
|
+
<div className="p-4">Inbox messages</div>
|
|
265
|
+
</TabsContent>
|
|
266
|
+
<TabsContent value="sent">
|
|
267
|
+
<div className="p-4">Sent messages</div>
|
|
268
|
+
</TabsContent>
|
|
269
|
+
<TabsContent value="drafts">
|
|
270
|
+
<div className="p-4">Draft messages</div>
|
|
271
|
+
</TabsContent>
|
|
272
|
+
</Tabs>
|
|
273
|
+
),
|
|
274
|
+
};
|
|
275
|
+
|