@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.
Files changed (192) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +18 -4
  3. package/package.json +9 -5
  4. package/src/components/CanvasIsolationBoundary.tsx +202 -0
  5. package/src/components/LoadingOverlayPrism.tsx +251 -0
  6. package/src/composed/AddToCart.tsx +229 -0
  7. package/src/composed/ArtAlignment.tsx +703 -0
  8. package/src/composed/ArtSelector.tsx +290 -0
  9. package/src/composed/ArtworkCustomizer.tsx +212 -0
  10. package/src/composed/CanvasEditor.tsx +79 -0
  11. package/src/composed/ColorPicker.tsx +111 -0
  12. package/src/composed/CurrentSelectionDisplay.tsx +86 -0
  13. package/src/composed/HeroProductImage.tsx +1071 -0
  14. package/src/composed/Lightbox.index.ts +2 -0
  15. package/src/composed/Lightbox.tsx +230 -0
  16. package/src/composed/PlacementClipShapeSelector.tsx +88 -0
  17. package/src/composed/PlacementTabs.tsx +179 -0
  18. package/src/composed/ProductCard.tsx +298 -0
  19. package/src/composed/ProductGallery.tsx +54 -0
  20. package/src/composed/ProductImage.tsx +129 -0
  21. package/src/composed/ProductList.tsx +147 -0
  22. package/src/composed/ProductOptions.tsx +305 -0
  23. package/src/composed/RealtimeMockup.tsx +121 -0
  24. package/src/composed/TileCount.tsx +348 -0
  25. package/src/composed/carousels/HeroCarousel.tsx +240 -0
  26. package/src/composed/carousels/MobileProductCarousel.tsx +1002 -0
  27. package/src/composed/carousels/index.ts +11 -0
  28. package/src/composed/carousels/types.ts +58 -0
  29. package/src/composed/grids/MasonryGrid.tsx +238 -0
  30. package/src/composed/grids/index.ts +9 -0
  31. package/src/composed/search/CurrentRefinements.tsx +80 -0
  32. package/src/composed/search/Filters.tsx +49 -0
  33. package/src/composed/search/FiltersButton.tsx +57 -0
  34. package/src/composed/search/FiltersDrawer.tsx +375 -0
  35. package/src/composed/search/ProductGrid.tsx +118 -0
  36. package/src/composed/search/ProductHit.tsx +56 -0
  37. package/src/composed/search/SearchBox.tsx +109 -0
  38. package/src/composed/search/SearchProvider.tsx +136 -0
  39. package/src/composed/search/facetConfig.ts +16 -0
  40. package/src/composed/search/index.ts +22 -0
  41. package/src/composed/search/meilisearchAdapter.ts +20 -0
  42. package/src/composed/search/types.ts +22 -0
  43. package/src/composed/zoom/EnhancedImageViewer.tsx +505 -0
  44. package/src/composed/zoom/ResponsiveZoom.tsx +134 -0
  45. package/src/composed/zoom/ZoomOverlay.tsx +194 -0
  46. package/src/composed/zoom/index.ts +12 -0
  47. package/src/composed/zoom/types.ts +12 -0
  48. package/src/design-system/ColorPalette.tsx +126 -0
  49. package/src/design-system/ColorSwatch.tsx +49 -0
  50. package/src/design-system/DesignSystemPage.tsx +130 -0
  51. package/src/design-system/ThemeSwitcher.tsx +181 -0
  52. package/src/design-system/TypographyScale.tsx +106 -0
  53. package/src/design-system/index.ts +5 -0
  54. package/src/ecommerce/stories/HeroProductImage.stories.tsx +66 -0
  55. package/src/ecommerce/stories/PDPHeroGallery.stories.tsx +105 -0
  56. package/src/ecommerce/stories/PDPInfoPanel.stories.tsx +472 -0
  57. package/src/ecommerce/stories/PDPLayout.stories.tsx +365 -0
  58. package/src/hooks/useBrand.ts +41 -0
  59. package/src/hooks/useCanvasContext.ts +127 -0
  60. package/src/hooks/useDeviceDetection.ts +64 -0
  61. package/src/hooks/useFocusTrap.ts +70 -0
  62. package/src/hooks/useImagePreloader.ts +268 -0
  63. package/src/hooks/useImageTransition.ts +608 -0
  64. package/src/hooks/usePlacementsProcessor.ts +74 -0
  65. package/src/hooks/useProductGallery.ts +193 -0
  66. package/src/hooks/useProductPage.ts +467 -0
  67. package/src/hooks/useRenderGuard.ts +96 -0
  68. package/src/hooks/useScrollDirection.ts +196 -0
  69. package/src/hooks/viewport/index.ts +25 -0
  70. package/src/hooks/viewport/useContainerWidth.ts +59 -0
  71. package/src/hooks/viewport/useMediaQuery.ts +52 -0
  72. package/src/hooks/viewport/useResponsiveImageCap.ts +149 -0
  73. package/src/hooks/viewport/useViewportDimensions.ts +135 -0
  74. package/src/hooks/viewport/useWideMonitorMode.ts +150 -0
  75. package/src/hooks/visibility/index.ts +15 -0
  76. package/src/hooks/visibility/observerPool.ts +150 -0
  77. package/src/index.ts +240 -0
  78. package/src/layouts/hero-zoom/HeroShrinkLayout.tsx +209 -0
  79. package/src/layouts/hero-zoom/HeroZoomLayout.tsx +351 -0
  80. package/src/layouts/hero-zoom/index.ts +30 -0
  81. package/src/layouts/hero-zoom/stories/HeroZoomLayout.stories.tsx +350 -0
  82. package/src/layouts/hero-zoom/types.ts +113 -0
  83. package/src/layouts/hero-zoom/useHeroZoomScales.ts +156 -0
  84. package/src/layouts/index.ts +9 -0
  85. package/src/layouts/pdp/EdgeBlurBox.tsx +210 -0
  86. package/src/layouts/pdp/ImageBlurExtension.tsx +215 -0
  87. package/src/layouts/pdp/ImageEdgeBlur.tsx +215 -0
  88. package/src/layouts/pdp/PDPLayout.tsx +246 -0
  89. package/src/layouts/pdp/SimpleImageBlur.tsx +140 -0
  90. package/src/layouts/pdp/index.ts +40 -0
  91. package/src/lib/env.ts +15 -0
  92. package/src/lib/locale.ts +167 -0
  93. package/src/lib/router.tsx +46 -0
  94. package/src/lib/utils.ts +6 -0
  95. package/src/lightbox/README.md +77 -0
  96. package/src/next/index.tsx +26 -0
  97. package/src/patterns/MockupPriorityProvider.tsx +1014 -0
  98. package/src/patterns/Product.tsx +850 -0
  99. package/src/patterns/ProductPageProvider.tsx +224 -0
  100. package/src/patterns/RealtimeProvider.tsx +1162 -0
  101. package/src/patterns/ShopProvider.tsx +603 -0
  102. package/src/personalization/PersonalizationBridge.tsx +235 -0
  103. package/src/personalization/PersonalizationContext.ts +29 -0
  104. package/src/personalization/PersonalizationInputs.tsx +110 -0
  105. package/src/personalization/PersonalizationProvider.tsx +407 -0
  106. package/src/personalization/canvas-stub.d.ts +22 -0
  107. package/src/personalization/index.ts +43 -0
  108. package/src/personalization/types.ts +48 -0
  109. package/src/personalization/usePersonalization.ts +32 -0
  110. package/src/personalization/usePersonalizationShimmer.ts +159 -0
  111. package/src/personalization/utils.ts +59 -0
  112. package/src/primitives/BrandLogo.tsx +65 -0
  113. package/src/primitives/BrandName.tsx +51 -0
  114. package/src/primitives/Button.tsx +123 -0
  115. package/src/primitives/ColorSwatch.tsx +221 -0
  116. package/src/primitives/DragHintAnimation.tsx +190 -0
  117. package/src/primitives/EdgeSwipeGuards.tsx +60 -0
  118. package/src/primitives/FloatingActionGroup.tsx +176 -0
  119. package/src/primitives/ProductPrice.tsx +171 -0
  120. package/src/primitives/ProgressiveBlur.tsx +295 -0
  121. package/src/primitives/ThemeToggle.tsx +125 -0
  122. package/src/primitives/__tests__/story-coverage.test.ts +98 -0
  123. package/src/primitives/accordion.tsx +280 -0
  124. package/src/primitives/badge.tsx +137 -0
  125. package/src/primitives/card.tsx +61 -0
  126. package/src/primitives/checkbox.tsx +56 -0
  127. package/src/primitives/collapsible.tsx +51 -0
  128. package/src/primitives/drawer.tsx +828 -0
  129. package/src/primitives/dropdown-menu.tsx +197 -0
  130. package/src/primitives/fieldset.tsx +73 -0
  131. package/src/primitives/index.ts +138 -0
  132. package/src/primitives/input.tsx +91 -0
  133. package/src/primitives/kbd.tsx +130 -0
  134. package/src/primitives/label.tsx +20 -0
  135. package/src/primitives/link.tsx +182 -0
  136. package/src/primitives/popover.tsx +80 -0
  137. package/src/primitives/radio-group.tsx +79 -0
  138. package/src/primitives/scroll-fade.tsx +159 -0
  139. package/src/primitives/select.tsx +170 -0
  140. package/src/primitives/separator.tsx +25 -0
  141. package/src/primitives/slider.tsx +221 -0
  142. package/src/primitives/spinner.tsx +72 -0
  143. package/src/primitives/stories/Accordion.stories.tsx +121 -0
  144. package/src/primitives/stories/Badge.stories.tsx +221 -0
  145. package/src/primitives/stories/Button.stories.tsx +185 -0
  146. package/src/primitives/stories/Card.stories.tsx +171 -0
  147. package/src/primitives/stories/Checkbox.stories.tsx +214 -0
  148. package/src/primitives/stories/Collapsible.stories.tsx +230 -0
  149. package/src/primitives/stories/Drawer.stories.tsx +378 -0
  150. package/src/primitives/stories/DropdownMenu.stories.tsx +182 -0
  151. package/src/primitives/stories/Fieldset.stories.tsx +212 -0
  152. package/src/primitives/stories/Input.stories.tsx +172 -0
  153. package/src/primitives/stories/Kbd.stories.tsx +183 -0
  154. package/src/primitives/stories/Label.stories.tsx +98 -0
  155. package/src/primitives/stories/Link.stories.tsx +260 -0
  156. package/src/primitives/stories/Popover.stories.tsx +178 -0
  157. package/src/primitives/stories/RadioGroup.stories.tsx +205 -0
  158. package/src/primitives/stories/Select.stories.tsx +222 -0
  159. package/src/primitives/stories/Separator.stories.tsx +134 -0
  160. package/src/primitives/stories/Slider.stories.tsx +203 -0
  161. package/src/primitives/stories/Spinner.stories.tsx +142 -0
  162. package/src/primitives/stories/Surface.stories.tsx +257 -0
  163. package/src/primitives/stories/Switch.stories.tsx +131 -0
  164. package/src/primitives/stories/Tabs.stories.tsx +275 -0
  165. package/src/primitives/stories/TextField.stories.tsx +139 -0
  166. package/src/primitives/stories/Textarea.stories.tsx +148 -0
  167. package/src/primitives/stories/Tooltip.stories.tsx +119 -0
  168. package/src/primitives/surface.tsx +86 -0
  169. package/src/primitives/switch.tsx +35 -0
  170. package/src/primitives/tabs.tsx +206 -0
  171. package/src/primitives/text-field.tsx +84 -0
  172. package/src/primitives/textarea.tsx +50 -0
  173. package/src/primitives/tooltip.tsx +58 -0
  174. package/src/services/CanvasExportService.ts +518 -0
  175. package/src/styles/base.css +380 -0
  176. package/src/styles/defaults.css +280 -0
  177. package/src/styles/globals.css +1242 -0
  178. package/src/styles/index.css +17 -0
  179. package/src/styles/ne-themes.css +4740 -0
  180. package/src/styles/tailwind.css +11 -0
  181. package/src/styles/tokens.css +117 -0
  182. package/src/styles/utilities.css +188 -0
  183. package/src/themes/apply-theme.ts +449 -0
  184. package/src/themes/getThemeStyles.ts +454 -0
  185. package/src/themes/index.ts +48 -0
  186. package/src/themes/oklch-theme.ts +283 -0
  187. package/src/themes/presets.ts +989 -0
  188. package/src/themes/types.ts +386 -0
  189. package/src/themes/useTheme.tsx +450 -0
  190. package/src/utils/dev-warnings.ts +161 -0
  191. package/src/utils/devWarnings.ts +153 -0
  192. package/dist/styles.css +0 -1
@@ -0,0 +1,185 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { Icon } from '@iconify/react';
3
+ import { Button } from '../button';
4
+
5
+ const meta: Meta<typeof Button> = {
6
+ title: 'UI/Button',
7
+ component: Button,
8
+ parameters: {
9
+ layout: 'centered',
10
+ },
11
+ tags: ['autodocs'],
12
+ argTypes: {
13
+ variant: {
14
+ control: 'select',
15
+ options: ['default', 'primary', 'secondary', 'tertiary', 'field', 'ghost', 'destructive', 'outline', 'link'],
16
+ description: 'Button style variant',
17
+ },
18
+ size: {
19
+ control: 'select',
20
+ options: ['default', 'sm', 'md', 'lg', 'icon'],
21
+ description: 'Button size',
22
+ },
23
+ disabled: {
24
+ control: 'boolean',
25
+ description: 'Disable the button',
26
+ },
27
+ isPending: {
28
+ control: 'boolean',
29
+ description: 'HeroUI compatibility: Shows loading state',
30
+ },
31
+ onClick: { action: 'clicked' },
32
+ onPress: { action: 'pressed (HeroUI compat)' },
33
+ },
34
+ };
35
+
36
+ export default meta;
37
+ type Story = StoryObj<typeof meta>;
38
+
39
+ // Basic variants
40
+ export const Default: Story = {
41
+ args: {
42
+ children: 'Default Button',
43
+ variant: 'default',
44
+ },
45
+ };
46
+
47
+ export const Primary: Story = {
48
+ args: {
49
+ children: 'Primary Button',
50
+ variant: 'primary',
51
+ },
52
+ };
53
+
54
+ export const Secondary: Story = {
55
+ args: {
56
+ children: 'Secondary Button',
57
+ variant: 'secondary',
58
+ },
59
+ };
60
+
61
+ export const Tertiary: Story = {
62
+ args: {
63
+ children: 'Tertiary Button',
64
+ variant: 'tertiary',
65
+ },
66
+ };
67
+
68
+ export const Field: Story = {
69
+ args: {
70
+ children: 'Field Button',
71
+ variant: 'field',
72
+ },
73
+ };
74
+
75
+ export const Ghost: Story = {
76
+ args: {
77
+ children: 'Ghost Button',
78
+ variant: 'ghost',
79
+ },
80
+ };
81
+
82
+ export const Destructive: Story = {
83
+ args: {
84
+ children: 'Delete',
85
+ variant: 'destructive',
86
+ },
87
+ };
88
+
89
+ export const Outline: Story = {
90
+ args: {
91
+ children: 'Outline Button',
92
+ variant: 'outline',
93
+ },
94
+ };
95
+
96
+ export const Link: Story = {
97
+ args: {
98
+ children: 'Link Button',
99
+ variant: 'link',
100
+ },
101
+ };
102
+
103
+ // Sizes
104
+ export const Small: Story = {
105
+ args: {
106
+ children: 'Small',
107
+ size: 'sm',
108
+ },
109
+ };
110
+
111
+ export const Medium: Story = {
112
+ args: {
113
+ children: 'Medium',
114
+ size: 'md',
115
+ },
116
+ };
117
+
118
+ export const Large: Story = {
119
+ args: {
120
+ children: 'Large',
121
+ size: 'lg',
122
+ },
123
+ };
124
+
125
+ // With Icons
126
+ export const WithIcon: Story = {
127
+ args: {
128
+ children: (
129
+ <>
130
+ <Icon icon="gravity-ui:plus" className="size-4" />
131
+ Add Item
132
+ </>
133
+ ),
134
+ },
135
+ };
136
+
137
+ export const IconOnly: Story = {
138
+ args: {
139
+ children: <Icon icon="gravity-ui:gear" className="size-5" />,
140
+ size: 'icon',
141
+ variant: 'ghost',
142
+ },
143
+ };
144
+
145
+ // States
146
+ export const Disabled: Story = {
147
+ args: {
148
+ children: 'Disabled',
149
+ disabled: true,
150
+ },
151
+ };
152
+
153
+ export const Pending: Story = {
154
+ args: {
155
+ children: 'Loading...',
156
+ isPending: true,
157
+ },
158
+ };
159
+
160
+ // All Variants Gallery
161
+ export const AllVariants: Story = {
162
+ render: () => (
163
+ <div className="flex flex-col gap-4">
164
+ <div className="flex flex-wrap gap-2">
165
+ <Button variant="default">Default</Button>
166
+ <Button variant="primary">Primary</Button>
167
+ <Button variant="secondary">Secondary</Button>
168
+ <Button variant="tertiary">Tertiary</Button>
169
+ <Button variant="field">Field</Button>
170
+ <Button variant="ghost">Ghost</Button>
171
+ <Button variant="destructive">Destructive</Button>
172
+ <Button variant="outline">Outline</Button>
173
+ <Button variant="link">Link</Button>
174
+ </div>
175
+ <div className="flex flex-wrap items-center gap-2">
176
+ <Button size="sm">Small</Button>
177
+ <Button size="default">Default</Button>
178
+ <Button size="lg">Large</Button>
179
+ <Button size="icon">
180
+ <Icon icon="gravity-ui:star" className="size-5" />
181
+ </Button>
182
+ </div>
183
+ </div>
184
+ ),
185
+ };
@@ -0,0 +1,171 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { Icon } from '@iconify/react';
3
+ import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from '../card';
4
+ import { Button } from '../button';
5
+ import { Label } from '../label';
6
+ import { Input } from '../input';
7
+
8
+ const meta: Meta<typeof Card> = {
9
+ title: 'UI/Card',
10
+ component: Card,
11
+ parameters: {
12
+ layout: 'centered',
13
+ },
14
+ tags: ['autodocs'],
15
+ };
16
+
17
+ export default meta;
18
+ type Story = StoryObj<typeof meta>;
19
+
20
+ export const Default: Story = {
21
+ render: () => (
22
+ <Card className="w-[350px]">
23
+ <CardHeader>
24
+ <CardTitle>Card Title</CardTitle>
25
+ <CardDescription>Card description goes here.</CardDescription>
26
+ </CardHeader>
27
+ <CardContent>
28
+ <p>Card content goes here. This is where the main information is displayed.</p>
29
+ </CardContent>
30
+ <CardFooter>
31
+ <Button>Action</Button>
32
+ </CardFooter>
33
+ </Card>
34
+ ),
35
+ };
36
+
37
+ export const WithForm: Story = {
38
+ render: () => (
39
+ <Card className="w-[350px]">
40
+ <CardHeader>
41
+ <CardTitle>Create Account</CardTitle>
42
+ <CardDescription>Enter your details to create a new account.</CardDescription>
43
+ </CardHeader>
44
+ <CardContent className="space-y-4">
45
+ <div className="space-y-1.5">
46
+ <Label htmlFor="name">Name</Label>
47
+ <Input id="name" placeholder="John Doe" />
48
+ </div>
49
+ <div className="space-y-1.5">
50
+ <Label htmlFor="email">Email</Label>
51
+ <Input id="email" type="email" placeholder="john@example.com" />
52
+ </div>
53
+ <div className="space-y-1.5">
54
+ <Label htmlFor="password">Password</Label>
55
+ <Input id="password" type="password" />
56
+ </div>
57
+ </CardContent>
58
+ <CardFooter className="flex justify-between">
59
+ <Button variant="secondary">Cancel</Button>
60
+ <Button>Create</Button>
61
+ </CardFooter>
62
+ </Card>
63
+ ),
64
+ };
65
+
66
+ export const Notification: Story = {
67
+ render: () => (
68
+ <Card className="w-[380px]">
69
+ <CardHeader>
70
+ <CardTitle>Notifications</CardTitle>
71
+ <CardDescription>You have 3 unread messages.</CardDescription>
72
+ </CardHeader>
73
+ <CardContent className="space-y-4">
74
+ {[
75
+ { title: 'Your call has been confirmed.', time: '1 hour ago' },
76
+ { title: 'You have a new message!', time: '2 hours ago' },
77
+ { title: 'Your subscription is expiring soon!', time: '5 hours ago' },
78
+ ].map((notification, i) => (
79
+ <div key={i} className="flex items-start gap-4">
80
+ <div className="bg-primary/10 flex h-8 w-8 items-center justify-center rounded-full">
81
+ <Icon icon="gravity-ui:bell" className="text-primary size-4" />
82
+ </div>
83
+ <div className="space-y-1">
84
+ <p className="text-sm leading-none font-label">{notification.title}</p>
85
+ <p className="text-muted-foreground text-sm">{notification.time}</p>
86
+ </div>
87
+ </div>
88
+ ))}
89
+ </CardContent>
90
+ <CardFooter>
91
+ <Button className="w-full" variant="secondary">
92
+ <Icon icon="gravity-ui:check" className="mr-2 size-4" />
93
+ Mark all as read
94
+ </Button>
95
+ </CardFooter>
96
+ </Card>
97
+ ),
98
+ };
99
+
100
+ export const Stats: Story = {
101
+ render: () => (
102
+ <div className="grid gap-4 md:grid-cols-3">
103
+ <Card>
104
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
105
+ <CardTitle className="text-sm font-label">Total Revenue</CardTitle>
106
+ <Icon icon="gravity-ui:dollar" className="text-muted-foreground size-4" />
107
+ </CardHeader>
108
+ <CardContent>
109
+ <div className="text-2xl font-bold">$45,231.89</div>
110
+ <p className="text-muted-foreground text-xs">+20.1% from last month</p>
111
+ </CardContent>
112
+ </Card>
113
+ <Card>
114
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
115
+ <CardTitle className="text-sm font-label">Subscriptions</CardTitle>
116
+ <Icon icon="gravity-ui:persons" className="text-muted-foreground size-4" />
117
+ </CardHeader>
118
+ <CardContent>
119
+ <div className="text-2xl font-bold">+2,350</div>
120
+ <p className="text-muted-foreground text-xs">+180.1% from last month</p>
121
+ </CardContent>
122
+ </Card>
123
+ <Card>
124
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
125
+ <CardTitle className="text-sm font-label">Active Now</CardTitle>
126
+ <Icon icon="gravity-ui:chart-line" className="text-muted-foreground size-4" />
127
+ </CardHeader>
128
+ <CardContent>
129
+ <div className="text-2xl font-bold">+573</div>
130
+ <p className="text-muted-foreground text-xs">+201 since last hour</p>
131
+ </CardContent>
132
+ </Card>
133
+ </div>
134
+ ),
135
+ };
136
+
137
+ export const Product: Story = {
138
+ render: () => (
139
+ <Card className="w-[300px] overflow-hidden">
140
+ <div className="bg-muted flex aspect-square items-center justify-center">
141
+ <Icon icon="gravity-ui:t-shirt" className="text-muted-foreground size-24" />
142
+ </div>
143
+ <CardHeader>
144
+ <CardTitle>Classic T-Shirt</CardTitle>
145
+ <CardDescription>Comfortable cotton blend</CardDescription>
146
+ </CardHeader>
147
+ <CardContent>
148
+ <div className="flex items-center justify-between">
149
+ <span className="text-2xl font-bold">$29.99</span>
150
+ <span className="text-muted-foreground text-sm line-through">$39.99</span>
151
+ </div>
152
+ </CardContent>
153
+ <CardFooter>
154
+ <Button className="w-full">
155
+ <Icon icon="gravity-ui:shopping-cart" className="mr-2 size-4" />
156
+ Add to Cart
157
+ </Button>
158
+ </CardFooter>
159
+ </Card>
160
+ ),
161
+ };
162
+
163
+ export const Simple: Story = {
164
+ render: () => (
165
+ <Card className="w-[350px]">
166
+ <CardContent className="pt-6">
167
+ <p className="text-muted-foreground text-center">A simple card with just content, no header or footer.</p>
168
+ </CardContent>
169
+ </Card>
170
+ ),
171
+ };
@@ -0,0 +1,214 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { useState } from 'react';
3
+ import { Checkbox } from '../checkbox';
4
+ import { Label } from '../label';
5
+ import { Card, CardContent, CardHeader, CardTitle } from '../card';
6
+
7
+ const meta: Meta<typeof Checkbox> = {
8
+ title: 'UI/Checkbox',
9
+ component: Checkbox,
10
+ parameters: {
11
+ layout: 'centered',
12
+ },
13
+ tags: ['autodocs'],
14
+ argTypes: {
15
+ checked: {
16
+ control: 'boolean',
17
+ description: 'Checked state',
18
+ },
19
+ disabled: {
20
+ control: 'boolean',
21
+ description: 'Disable the checkbox',
22
+ },
23
+ onCheckedChange: {
24
+ control: false,
25
+ description: 'Callback when checked state changes',
26
+ },
27
+ },
28
+ };
29
+
30
+ export default meta;
31
+ type Story = StoryObj<typeof meta>;
32
+
33
+ export const Default: Story = {
34
+ render: () => (
35
+ <div className="flex items-center space-x-2">
36
+ <Checkbox id="terms" />
37
+ <Label htmlFor="terms">Accept terms and conditions</Label>
38
+ </div>
39
+ ),
40
+ };
41
+
42
+ export const Checked: Story = {
43
+ render: () => (
44
+ <div className="flex items-center space-x-2">
45
+ <Checkbox id="checked" defaultChecked />
46
+ <Label htmlFor="checked">Checked by default</Label>
47
+ </div>
48
+ ),
49
+ };
50
+
51
+ export const Controlled: Story = {
52
+ render: () => {
53
+ const [checked, setChecked] = useState(false);
54
+
55
+ return (
56
+ <div className="space-y-4">
57
+ <div className="flex items-center space-x-2">
58
+ <Checkbox
59
+ id="controlled"
60
+ checked={checked}
61
+ onCheckedChange={(value) => setChecked(value === true)}
62
+ />
63
+ <Label htmlFor="controlled">Subscribe to newsletter</Label>
64
+ </div>
65
+ <p className="text-sm text-muted-foreground">Checked: {checked.toString()}</p>
66
+ </div>
67
+ );
68
+ },
69
+ };
70
+
71
+ export const DefaultChecked: Story = {
72
+ render: () => (
73
+ <div className="flex items-center space-x-2">
74
+ <Checkbox id="default-checked" defaultChecked />
75
+ <Label htmlFor="default-checked">Default checked</Label>
76
+ </div>
77
+ ),
78
+ };
79
+
80
+ export const Indeterminate: Story = {
81
+ render: () => {
82
+ const [checked, setChecked] = useState<boolean | 'indeterminate'>('indeterminate');
83
+
84
+ return (
85
+ <div className="space-y-4">
86
+ <div className="flex items-center space-x-2">
87
+ <Checkbox id="indeterminate" checked={checked} onCheckedChange={setChecked} />
88
+ <Label htmlFor="indeterminate">Select all</Label>
89
+ </div>
90
+ <p className="text-sm text-muted-foreground">State: {String(checked)}</p>
91
+ </div>
92
+ );
93
+ },
94
+ };
95
+
96
+ export const Disabled: Story = {
97
+ render: () => (
98
+ <div className="space-y-4">
99
+ <div className="flex items-center space-x-2">
100
+ <Checkbox id="disabled-off" disabled />
101
+ <Label htmlFor="disabled-off" className="opacity-50">
102
+ Disabled (unchecked)
103
+ </Label>
104
+ </div>
105
+ <div className="flex items-center space-x-2">
106
+ <Checkbox id="disabled-on" disabled defaultChecked />
107
+ <Label htmlFor="disabled-on" className="opacity-50">
108
+ Disabled (checked)
109
+ </Label>
110
+ </div>
111
+ </div>
112
+ ),
113
+ };
114
+
115
+ export const WithDescription: Story = {
116
+ render: () => (
117
+ <div className="items-top flex space-x-2">
118
+ <Checkbox id="terms2" />
119
+ <div className="grid gap-1.5 leading-none">
120
+ <Label htmlFor="terms2">Accept terms and conditions</Label>
121
+ <p className="text-sm text-muted-foreground">
122
+ You agree to our Terms of Service and Privacy Policy.
123
+ </p>
124
+ </div>
125
+ </div>
126
+ ),
127
+ };
128
+
129
+ export const FormExample: Story = {
130
+ render: () => {
131
+ const [preferences, setPreferences] = useState({
132
+ marketing: true,
133
+ notifications: true,
134
+ updates: false,
135
+ });
136
+
137
+ return (
138
+ <div className="w-full max-w-sm space-y-4">
139
+ <h3 className="text-lg font-heading">Email Preferences</h3>
140
+ <div className="space-y-4">
141
+ <div className="flex items-start space-x-3">
142
+ <Checkbox
143
+ id="marketing"
144
+ checked={preferences.marketing}
145
+ onCheckedChange={(checked) =>
146
+ setPreferences({ ...preferences, marketing: checked as boolean })
147
+ }
148
+ />
149
+ <div className="space-y-1">
150
+ <Label htmlFor="marketing">Marketing emails</Label>
151
+ <p className="text-sm text-muted-foreground">
152
+ Receive emails about new products, features, and more.
153
+ </p>
154
+ </div>
155
+ </div>
156
+ <div className="flex items-start space-x-3">
157
+ <Checkbox
158
+ id="notifications"
159
+ checked={preferences.notifications}
160
+ onCheckedChange={(checked) =>
161
+ setPreferences({ ...preferences, notifications: checked as boolean })
162
+ }
163
+ />
164
+ <div className="space-y-1">
165
+ <Label htmlFor="notifications">Notification emails</Label>
166
+ <p className="text-sm text-muted-foreground">
167
+ Receive emails about your account activity.
168
+ </p>
169
+ </div>
170
+ </div>
171
+ <div className="flex items-start space-x-3">
172
+ <Checkbox
173
+ id="updates"
174
+ checked={preferences.updates}
175
+ onCheckedChange={(checked) =>
176
+ setPreferences({ ...preferences, updates: checked as boolean })
177
+ }
178
+ />
179
+ <div className="space-y-1">
180
+ <Label htmlFor="updates">Product updates</Label>
181
+ <p className="text-sm text-muted-foreground">
182
+ Receive emails about product updates and new features.
183
+ </p>
184
+ </div>
185
+ </div>
186
+ </div>
187
+ </div>
188
+ );
189
+ },
190
+ };
191
+
192
+ export const OnCard: Story = {
193
+ render: () => (
194
+ <Card className="w-[350px]">
195
+ <CardHeader>
196
+ <CardTitle>Preferences</CardTitle>
197
+ </CardHeader>
198
+ <CardContent className="space-y-4">
199
+ <div className="flex items-center space-x-2">
200
+ <Checkbox id="card-emails" defaultChecked />
201
+ <Label htmlFor="card-emails">Receive emails</Label>
202
+ </div>
203
+ <div className="flex items-center space-x-2">
204
+ <Checkbox id="card-sms" />
205
+ <Label htmlFor="card-sms">Receive SMS</Label>
206
+ </div>
207
+ <div className="flex items-center space-x-2">
208
+ <Checkbox id="card-push" defaultChecked />
209
+ <Label htmlFor="card-push">Push notifications</Label>
210
+ </div>
211
+ </CardContent>
212
+ </Card>
213
+ ),
214
+ };