@snowcone-app/ui 0.1.43 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +18 -4
  3. package/dist/index.cjs +5 -2
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.js +5 -2
  6. package/dist/index.js.map +1 -1
  7. package/package.json +9 -5
  8. package/src/components/CanvasIsolationBoundary.tsx +202 -0
  9. package/src/components/LoadingOverlayPrism.tsx +251 -0
  10. package/src/composed/AddToCart.tsx +229 -0
  11. package/src/composed/ArtAlignment.tsx +703 -0
  12. package/src/composed/ArtSelector.tsx +290 -0
  13. package/src/composed/ArtworkCustomizer.tsx +212 -0
  14. package/src/composed/CanvasEditor.tsx +79 -0
  15. package/src/composed/ColorPicker.tsx +111 -0
  16. package/src/composed/CurrentSelectionDisplay.tsx +86 -0
  17. package/src/composed/HeroProductImage.tsx +1079 -0
  18. package/src/composed/Lightbox.index.ts +2 -0
  19. package/src/composed/Lightbox.tsx +230 -0
  20. package/src/composed/PlacementClipShapeSelector.tsx +88 -0
  21. package/src/composed/PlacementTabs.tsx +179 -0
  22. package/src/composed/ProductCard.tsx +298 -0
  23. package/src/composed/ProductGallery.tsx +54 -0
  24. package/src/composed/ProductImage.tsx +129 -0
  25. package/src/composed/ProductList.tsx +147 -0
  26. package/src/composed/ProductOptions.tsx +305 -0
  27. package/src/composed/RealtimeMockup.tsx +121 -0
  28. package/src/composed/TileCount.tsx +348 -0
  29. package/src/composed/carousels/HeroCarousel.tsx +240 -0
  30. package/src/composed/carousels/MobileProductCarousel.tsx +1002 -0
  31. package/src/composed/carousels/index.ts +11 -0
  32. package/src/composed/carousels/types.ts +58 -0
  33. package/src/composed/grids/MasonryGrid.tsx +238 -0
  34. package/src/composed/grids/index.ts +9 -0
  35. package/src/composed/search/CurrentRefinements.tsx +80 -0
  36. package/src/composed/search/Filters.tsx +49 -0
  37. package/src/composed/search/FiltersButton.tsx +57 -0
  38. package/src/composed/search/FiltersDrawer.tsx +375 -0
  39. package/src/composed/search/ProductGrid.tsx +118 -0
  40. package/src/composed/search/ProductHit.tsx +56 -0
  41. package/src/composed/search/SearchBox.tsx +109 -0
  42. package/src/composed/search/SearchProvider.tsx +136 -0
  43. package/src/composed/search/facetConfig.ts +16 -0
  44. package/src/composed/search/index.ts +22 -0
  45. package/src/composed/search/meilisearchAdapter.ts +20 -0
  46. package/src/composed/search/types.ts +22 -0
  47. package/src/composed/zoom/EnhancedImageViewer.tsx +505 -0
  48. package/src/composed/zoom/ResponsiveZoom.tsx +134 -0
  49. package/src/composed/zoom/ZoomOverlay.tsx +194 -0
  50. package/src/composed/zoom/index.ts +12 -0
  51. package/src/composed/zoom/types.ts +12 -0
  52. package/src/design-system/ColorPalette.tsx +126 -0
  53. package/src/design-system/ColorSwatch.tsx +49 -0
  54. package/src/design-system/DesignSystemPage.tsx +130 -0
  55. package/src/design-system/ThemeSwitcher.tsx +181 -0
  56. package/src/design-system/TypographyScale.tsx +106 -0
  57. package/src/design-system/index.ts +5 -0
  58. package/src/ecommerce/stories/HeroProductImage.stories.tsx +66 -0
  59. package/src/ecommerce/stories/PDPHeroGallery.stories.tsx +105 -0
  60. package/src/ecommerce/stories/PDPInfoPanel.stories.tsx +472 -0
  61. package/src/ecommerce/stories/PDPLayout.stories.tsx +365 -0
  62. package/src/hooks/useBrand.ts +41 -0
  63. package/src/hooks/useCanvasContext.ts +127 -0
  64. package/src/hooks/useDeviceDetection.ts +64 -0
  65. package/src/hooks/useFocusTrap.ts +70 -0
  66. package/src/hooks/useImagePreloader.ts +268 -0
  67. package/src/hooks/useImageTransition.ts +608 -0
  68. package/src/hooks/usePlacementsProcessor.ts +74 -0
  69. package/src/hooks/useProductGallery.ts +193 -0
  70. package/src/hooks/useProductPage.ts +467 -0
  71. package/src/hooks/useRenderGuard.ts +96 -0
  72. package/src/hooks/useScrollDirection.ts +196 -0
  73. package/src/hooks/viewport/index.ts +25 -0
  74. package/src/hooks/viewport/useContainerWidth.ts +59 -0
  75. package/src/hooks/viewport/useMediaQuery.ts +52 -0
  76. package/src/hooks/viewport/useResponsiveImageCap.ts +149 -0
  77. package/src/hooks/viewport/useViewportDimensions.ts +135 -0
  78. package/src/hooks/viewport/useWideMonitorMode.ts +150 -0
  79. package/src/hooks/visibility/index.ts +15 -0
  80. package/src/hooks/visibility/observerPool.ts +150 -0
  81. package/src/index.ts +240 -0
  82. package/src/layouts/hero-zoom/HeroShrinkLayout.tsx +209 -0
  83. package/src/layouts/hero-zoom/HeroZoomLayout.tsx +351 -0
  84. package/src/layouts/hero-zoom/index.ts +30 -0
  85. package/src/layouts/hero-zoom/stories/HeroZoomLayout.stories.tsx +350 -0
  86. package/src/layouts/hero-zoom/types.ts +113 -0
  87. package/src/layouts/hero-zoom/useHeroZoomScales.ts +156 -0
  88. package/src/layouts/index.ts +9 -0
  89. package/src/layouts/pdp/EdgeBlurBox.tsx +210 -0
  90. package/src/layouts/pdp/ImageBlurExtension.tsx +215 -0
  91. package/src/layouts/pdp/ImageEdgeBlur.tsx +215 -0
  92. package/src/layouts/pdp/PDPLayout.tsx +246 -0
  93. package/src/layouts/pdp/SimpleImageBlur.tsx +140 -0
  94. package/src/layouts/pdp/index.ts +40 -0
  95. package/src/lib/env.ts +15 -0
  96. package/src/lib/locale.ts +167 -0
  97. package/src/lib/router.tsx +46 -0
  98. package/src/lib/utils.ts +6 -0
  99. package/src/lightbox/README.md +77 -0
  100. package/src/next/index.tsx +26 -0
  101. package/src/patterns/MockupPriorityProvider.tsx +1014 -0
  102. package/src/patterns/Product.tsx +850 -0
  103. package/src/patterns/ProductPageProvider.tsx +224 -0
  104. package/src/patterns/RealtimeProvider.tsx +1162 -0
  105. package/src/patterns/ShopProvider.tsx +603 -0
  106. package/src/personalization/PersonalizationBridge.tsx +235 -0
  107. package/src/personalization/PersonalizationContext.ts +29 -0
  108. package/src/personalization/PersonalizationInputs.tsx +110 -0
  109. package/src/personalization/PersonalizationProvider.tsx +407 -0
  110. package/src/personalization/canvas-stub.d.ts +22 -0
  111. package/src/personalization/index.ts +43 -0
  112. package/src/personalization/types.ts +48 -0
  113. package/src/personalization/usePersonalization.ts +32 -0
  114. package/src/personalization/usePersonalizationShimmer.ts +159 -0
  115. package/src/personalization/utils.ts +59 -0
  116. package/src/primitives/BrandLogo.tsx +65 -0
  117. package/src/primitives/BrandName.tsx +51 -0
  118. package/src/primitives/Button.tsx +123 -0
  119. package/src/primitives/ColorSwatch.tsx +221 -0
  120. package/src/primitives/DragHintAnimation.tsx +190 -0
  121. package/src/primitives/EdgeSwipeGuards.tsx +60 -0
  122. package/src/primitives/FloatingActionGroup.tsx +176 -0
  123. package/src/primitives/ProductPrice.tsx +171 -0
  124. package/src/primitives/ProgressiveBlur.tsx +295 -0
  125. package/src/primitives/ThemeToggle.tsx +125 -0
  126. package/src/primitives/__tests__/story-coverage.test.ts +98 -0
  127. package/src/primitives/accordion.tsx +280 -0
  128. package/src/primitives/badge.tsx +137 -0
  129. package/src/primitives/card.tsx +61 -0
  130. package/src/primitives/checkbox.tsx +56 -0
  131. package/src/primitives/collapsible.tsx +51 -0
  132. package/src/primitives/drawer.tsx +828 -0
  133. package/src/primitives/dropdown-menu.tsx +197 -0
  134. package/src/primitives/fieldset.tsx +73 -0
  135. package/src/primitives/index.ts +138 -0
  136. package/src/primitives/input.tsx +91 -0
  137. package/src/primitives/kbd.tsx +130 -0
  138. package/src/primitives/label.tsx +20 -0
  139. package/src/primitives/link.tsx +182 -0
  140. package/src/primitives/popover.tsx +80 -0
  141. package/src/primitives/radio-group.tsx +79 -0
  142. package/src/primitives/scroll-fade.tsx +159 -0
  143. package/src/primitives/select.tsx +170 -0
  144. package/src/primitives/separator.tsx +25 -0
  145. package/src/primitives/slider.tsx +221 -0
  146. package/src/primitives/spinner.tsx +72 -0
  147. package/src/primitives/stories/Accordion.stories.tsx +121 -0
  148. package/src/primitives/stories/Badge.stories.tsx +221 -0
  149. package/src/primitives/stories/Button.stories.tsx +185 -0
  150. package/src/primitives/stories/Card.stories.tsx +171 -0
  151. package/src/primitives/stories/Checkbox.stories.tsx +214 -0
  152. package/src/primitives/stories/Collapsible.stories.tsx +230 -0
  153. package/src/primitives/stories/Drawer.stories.tsx +378 -0
  154. package/src/primitives/stories/DropdownMenu.stories.tsx +182 -0
  155. package/src/primitives/stories/Fieldset.stories.tsx +212 -0
  156. package/src/primitives/stories/Input.stories.tsx +172 -0
  157. package/src/primitives/stories/Kbd.stories.tsx +183 -0
  158. package/src/primitives/stories/Label.stories.tsx +98 -0
  159. package/src/primitives/stories/Link.stories.tsx +260 -0
  160. package/src/primitives/stories/Popover.stories.tsx +178 -0
  161. package/src/primitives/stories/RadioGroup.stories.tsx +205 -0
  162. package/src/primitives/stories/Select.stories.tsx +222 -0
  163. package/src/primitives/stories/Separator.stories.tsx +134 -0
  164. package/src/primitives/stories/Slider.stories.tsx +203 -0
  165. package/src/primitives/stories/Spinner.stories.tsx +142 -0
  166. package/src/primitives/stories/Surface.stories.tsx +257 -0
  167. package/src/primitives/stories/Switch.stories.tsx +131 -0
  168. package/src/primitives/stories/Tabs.stories.tsx +275 -0
  169. package/src/primitives/stories/TextField.stories.tsx +139 -0
  170. package/src/primitives/stories/Textarea.stories.tsx +148 -0
  171. package/src/primitives/stories/Tooltip.stories.tsx +119 -0
  172. package/src/primitives/surface.tsx +86 -0
  173. package/src/primitives/switch.tsx +35 -0
  174. package/src/primitives/tabs.tsx +206 -0
  175. package/src/primitives/text-field.tsx +84 -0
  176. package/src/primitives/textarea.tsx +50 -0
  177. package/src/primitives/tooltip.tsx +58 -0
  178. package/src/services/CanvasExportService.ts +518 -0
  179. package/src/styles/base.css +380 -0
  180. package/src/styles/defaults.css +280 -0
  181. package/src/styles/globals.css +1242 -0
  182. package/src/styles/index.css +17 -0
  183. package/src/styles/ne-themes.css +4740 -0
  184. package/src/styles/tailwind.css +11 -0
  185. package/src/styles/tokens.css +117 -0
  186. package/src/styles/utilities.css +188 -0
  187. package/src/themes/apply-theme.ts +449 -0
  188. package/src/themes/getThemeStyles.ts +454 -0
  189. package/src/themes/index.ts +48 -0
  190. package/src/themes/oklch-theme.ts +283 -0
  191. package/src/themes/presets.ts +989 -0
  192. package/src/themes/types.ts +386 -0
  193. package/src/themes/useTheme.tsx +450 -0
  194. package/src/utils/dev-warnings.ts +161 -0
  195. package/src/utils/devWarnings.ts +153 -0
  196. package/dist/styles.css +0 -1
@@ -0,0 +1,178 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { useState } from 'react';
3
+ import { Popover, PopoverTrigger, PopoverContent } from '../popover';
4
+ import { Button } from '../button';
5
+ import { Label } from '../label';
6
+ import { Input } from '../input';
7
+
8
+ const meta: Meta<typeof Popover> = {
9
+ title: 'UI/Popover',
10
+ component: Popover,
11
+ parameters: {
12
+ layout: 'centered',
13
+ },
14
+ tags: ['autodocs'],
15
+ argTypes: {
16
+ open: {
17
+ control: 'boolean',
18
+ description: 'Whether the popover is open',
19
+ },
20
+ modal: {
21
+ control: 'boolean',
22
+ description: 'Whether the popover is modal',
23
+ },
24
+ },
25
+ };
26
+
27
+ export default meta;
28
+ type Story = StoryObj<typeof meta>;
29
+
30
+ export const Default: Story = {
31
+ render: () => (
32
+ <Popover>
33
+ <PopoverTrigger asChild>
34
+ <Button variant="secondary">Open Popover</Button>
35
+ </PopoverTrigger>
36
+ <PopoverContent>
37
+ <div className="grid gap-4">
38
+ <div className="space-y-2">
39
+ <h4 className="font-label leading-none">Dimensions</h4>
40
+ <p className="text-sm text-muted-foreground">Set the dimensions for the layer.</p>
41
+ </div>
42
+ <div className="grid gap-2">
43
+ <div className="grid grid-cols-3 items-center gap-4">
44
+ <Label htmlFor="width">Width</Label>
45
+ <Input
46
+ id="width"
47
+ defaultValue="100%"
48
+ className="col-span-2"
49
+ />
50
+ </div>
51
+ <div className="grid grid-cols-3 items-center gap-4">
52
+ <Label htmlFor="height">Height</Label>
53
+ <Input
54
+ id="height"
55
+ defaultValue="25px"
56
+ className="col-span-2"
57
+ />
58
+ </div>
59
+ </div>
60
+ </div>
61
+ </PopoverContent>
62
+ </Popover>
63
+ ),
64
+ };
65
+
66
+ export const Controlled: Story = {
67
+ render: () => {
68
+ const [open, setOpen] = useState(false);
69
+ return (
70
+ <div className="space-y-4">
71
+ <Popover open={open} onOpenChange={setOpen}>
72
+ <PopoverTrigger asChild>
73
+ <Button variant="secondary">
74
+ {open ? 'Close' : 'Open'} Popover
75
+ </Button>
76
+ </PopoverTrigger>
77
+ <PopoverContent>
78
+ <p>This is a controlled popover.</p>
79
+ <Button onClick={() => setOpen(false)} className="mt-2" size="sm">
80
+ Close
81
+ </Button>
82
+ </PopoverContent>
83
+ </Popover>
84
+ <p className="text-sm text-muted-foreground">Open: {open.toString()}</p>
85
+ </div>
86
+ );
87
+ },
88
+ };
89
+
90
+ export const ControlledState: Story = {
91
+ render: () => {
92
+ const [isOpen, setIsOpen] = useState(false);
93
+ return (
94
+ <div className="space-y-4">
95
+ <Popover open={isOpen} onOpenChange={setIsOpen}>
96
+ <PopoverTrigger asChild>
97
+ <Button variant="secondary">Toggle Popover</Button>
98
+ </PopoverTrigger>
99
+ <PopoverContent>
100
+ <p>Controlled state example</p>
101
+ </PopoverContent>
102
+ </Popover>
103
+ <p className="text-sm text-muted-foreground">isOpen: {isOpen.toString()}</p>
104
+ </div>
105
+ );
106
+ },
107
+ };
108
+
109
+ export const Positions: Story = {
110
+ render: () => (
111
+ <div className="flex items-center gap-8">
112
+ <Popover>
113
+ <PopoverTrigger asChild>
114
+ <Button variant="secondary">Top</Button>
115
+ </PopoverTrigger>
116
+ <PopoverContent side="top" className="w-auto">
117
+ <p>Top popover</p>
118
+ </PopoverContent>
119
+ </Popover>
120
+
121
+ <Popover>
122
+ <PopoverTrigger asChild>
123
+ <Button variant="secondary">Right</Button>
124
+ </PopoverTrigger>
125
+ <PopoverContent side="right" className="w-auto">
126
+ <p>Right popover</p>
127
+ </PopoverContent>
128
+ </Popover>
129
+
130
+ <Popover>
131
+ <PopoverTrigger asChild>
132
+ <Button variant="secondary">Bottom</Button>
133
+ </PopoverTrigger>
134
+ <PopoverContent side="bottom" className="w-auto">
135
+ <p>Bottom popover</p>
136
+ </PopoverContent>
137
+ </Popover>
138
+
139
+ <Popover>
140
+ <PopoverTrigger asChild>
141
+ <Button variant="secondary">Left</Button>
142
+ </PopoverTrigger>
143
+ <PopoverContent side="left" className="w-auto">
144
+ <p>Left popover</p>
145
+ </PopoverContent>
146
+ </Popover>
147
+ </div>
148
+ ),
149
+ };
150
+
151
+ export const WithForm: Story = {
152
+ render: () => (
153
+ <Popover>
154
+ <PopoverTrigger asChild>
155
+ <Button>Update Settings</Button>
156
+ </PopoverTrigger>
157
+ <PopoverContent className="w-80">
158
+ <div className="grid gap-4">
159
+ <div className="space-y-2">
160
+ <h4 className="font-label leading-none">Settings</h4>
161
+ <p className="text-sm text-muted-foreground">Configure your preferences.</p>
162
+ </div>
163
+ <div className="grid gap-2">
164
+ <div className="grid gap-1">
165
+ <Label htmlFor="name">Name</Label>
166
+ <Input id="name" placeholder="Enter name" />
167
+ </div>
168
+ <div className="grid gap-1">
169
+ <Label htmlFor="email">Email</Label>
170
+ <Input id="email" type="email" placeholder="Enter email" />
171
+ </div>
172
+ </div>
173
+ <Button className="w-full">Save</Button>
174
+ </div>
175
+ </PopoverContent>
176
+ </Popover>
177
+ ),
178
+ };
@@ -0,0 +1,205 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { useState } from 'react';
3
+ import { RadioGroup, RadioGroupItem, Radio } from '../radio-group';
4
+ import { Label } from '../label';
5
+ import { Card, CardContent, CardHeader, CardTitle } from '../card';
6
+
7
+ const meta: Meta<typeof RadioGroup> = {
8
+ title: 'UI/RadioGroup',
9
+ component: RadioGroup,
10
+ parameters: {
11
+ layout: 'centered',
12
+ },
13
+ tags: ['autodocs'],
14
+ argTypes: {
15
+ value: {
16
+ control: 'text',
17
+ description: 'Selected value',
18
+ },
19
+ disabled: {
20
+ control: 'boolean',
21
+ description: 'Disable the radio group',
22
+ },
23
+ },
24
+ };
25
+
26
+ export default meta;
27
+ type Story = StoryObj<typeof meta>;
28
+
29
+ export const Default: Story = {
30
+ render: () => (
31
+ <RadioGroup defaultValue="option-one">
32
+ <div className="flex items-center space-x-2">
33
+ <RadioGroupItem value="option-one" id="option-one" />
34
+ <Label htmlFor="option-one">Option One</Label>
35
+ </div>
36
+ <div className="flex items-center space-x-2">
37
+ <RadioGroupItem value="option-two" id="option-two" />
38
+ <Label htmlFor="option-two">Option Two</Label>
39
+ </div>
40
+ </RadioGroup>
41
+ ),
42
+ };
43
+
44
+ export const Controlled: Story = {
45
+ render: () => {
46
+ const [value, setValue] = useState('comfortable');
47
+ return (
48
+ <div className="space-y-4">
49
+ <RadioGroup value={value} onValueChange={setValue}>
50
+ <div className="flex items-center space-x-2">
51
+ <RadioGroupItem value="default" id="r1" />
52
+ <Label htmlFor="r1">Default</Label>
53
+ </div>
54
+ <div className="flex items-center space-x-2">
55
+ <RadioGroupItem value="comfortable" id="r2" />
56
+ <Label htmlFor="r2">Comfortable</Label>
57
+ </div>
58
+ <div className="flex items-center space-x-2">
59
+ <RadioGroupItem value="compact" id="r3" />
60
+ <Label htmlFor="r3">Compact</Label>
61
+ </div>
62
+ </RadioGroup>
63
+ <p className="text-sm text-muted-foreground">Selected: {value}</p>
64
+ </div>
65
+ );
66
+ },
67
+ };
68
+
69
+ export const WithHorizontalOrientation: Story = {
70
+ render: () => {
71
+ const [value, setValue] = useState('medium');
72
+ return (
73
+ <div className="space-y-4">
74
+ <RadioGroup value={value} onValueChange={setValue} orientation="horizontal">
75
+ <Radio value="small" id="size-small">Small</Radio>
76
+ <Radio value="medium" id="size-medium">Medium</Radio>
77
+ <Radio value="large" id="size-large">Large</Radio>
78
+ </RadioGroup>
79
+ <p className="text-sm text-muted-foreground">Selected: {value}</p>
80
+ </div>
81
+ );
82
+ },
83
+ };
84
+
85
+ export const UsingRadioComponent: Story = {
86
+ render: () => (
87
+ <RadioGroup defaultValue="banana">
88
+ <Radio value="apple" id="fruit-apple">
89
+ Apple
90
+ </Radio>
91
+ <Radio value="banana" id="fruit-banana">
92
+ Banana
93
+ </Radio>
94
+ <Radio value="orange" id="fruit-orange">
95
+ Orange
96
+ </Radio>
97
+ </RadioGroup>
98
+ ),
99
+ };
100
+
101
+ export const Horizontal: Story = {
102
+ render: () => (
103
+ <RadioGroup defaultValue="center" className="flex flex-row gap-4">
104
+ <div className="flex items-center space-x-2">
105
+ <RadioGroupItem value="left" id="align-left" />
106
+ <Label htmlFor="align-left">Left</Label>
107
+ </div>
108
+ <div className="flex items-center space-x-2">
109
+ <RadioGroupItem value="center" id="align-center" />
110
+ <Label htmlFor="align-center">Center</Label>
111
+ </div>
112
+ <div className="flex items-center space-x-2">
113
+ <RadioGroupItem value="right" id="align-right" />
114
+ <Label htmlFor="align-right">Right</Label>
115
+ </div>
116
+ </RadioGroup>
117
+ ),
118
+ };
119
+
120
+ export const Disabled: Story = {
121
+ render: () => (
122
+ <RadioGroup defaultValue="option-one" disabled>
123
+ <div className="flex items-center space-x-2">
124
+ <RadioGroupItem value="option-one" id="d1" />
125
+ <Label htmlFor="d1" className="opacity-50">Option One</Label>
126
+ </div>
127
+ <div className="flex items-center space-x-2">
128
+ <RadioGroupItem value="option-two" id="d2" />
129
+ <Label htmlFor="d2" className="opacity-50">Option Two</Label>
130
+ </div>
131
+ </RadioGroup>
132
+ ),
133
+ };
134
+
135
+ export const FormExample: Story = {
136
+ render: () => {
137
+ const [plan, setPlan] = useState('pro');
138
+ return (
139
+ <div className="w-full max-w-md space-y-4">
140
+ <h3 className="text-lg font-heading">Select Plan</h3>
141
+ <RadioGroup value={plan} onValueChange={setPlan} className="space-y-3">
142
+ <div className="flex items-start space-x-3 p-4 border border-divider rounded-lg cursor-pointer hover:bg-muted">
143
+ <RadioGroupItem value="free" id="plan-free" className="mt-1" />
144
+ <div className="space-y-1">
145
+ <Label htmlFor="plan-free" className="font-label cursor-pointer">
146
+ Free
147
+ </Label>
148
+ <p className="text-sm text-muted-foreground">
149
+ Perfect for trying out our service. Limited features.
150
+ </p>
151
+ </div>
152
+ </div>
153
+ <div className="flex items-start space-x-3 p-4 border border-divider rounded-lg cursor-pointer hover:bg-muted">
154
+ <RadioGroupItem value="pro" id="plan-pro" className="mt-1" />
155
+ <div className="space-y-1">
156
+ <Label htmlFor="plan-pro" className="font-label cursor-pointer">
157
+ Pro - $9/month
158
+ </Label>
159
+ <p className="text-sm text-muted-foreground">
160
+ All features unlocked. Great for individuals.
161
+ </p>
162
+ </div>
163
+ </div>
164
+ <div className="flex items-start space-x-3 p-4 border border-divider rounded-lg cursor-pointer hover:bg-muted">
165
+ <RadioGroupItem value="enterprise" id="plan-enterprise" className="mt-1" />
166
+ <div className="space-y-1">
167
+ <Label htmlFor="plan-enterprise" className="font-label cursor-pointer">
168
+ Enterprise - Contact us
169
+ </Label>
170
+ <p className="text-sm text-muted-foreground">
171
+ Custom solutions for large teams.
172
+ </p>
173
+ </div>
174
+ </div>
175
+ </RadioGroup>
176
+ </div>
177
+ );
178
+ },
179
+ };
180
+
181
+ export const OnCard: Story = {
182
+ render: () => (
183
+ <Card className="w-[350px]">
184
+ <CardHeader>
185
+ <CardTitle>Notification Settings</CardTitle>
186
+ </CardHeader>
187
+ <CardContent>
188
+ <RadioGroup defaultValue="all" className="space-y-3">
189
+ <div className="flex items-center space-x-2">
190
+ <RadioGroupItem value="all" id="card-all" />
191
+ <Label htmlFor="card-all">All notifications</Label>
192
+ </div>
193
+ <div className="flex items-center space-x-2">
194
+ <RadioGroupItem value="important" id="card-important" />
195
+ <Label htmlFor="card-important">Important only</Label>
196
+ </div>
197
+ <div className="flex items-center space-x-2">
198
+ <RadioGroupItem value="none" id="card-none" />
199
+ <Label htmlFor="card-none">None</Label>
200
+ </div>
201
+ </RadioGroup>
202
+ </CardContent>
203
+ </Card>
204
+ ),
205
+ };
@@ -0,0 +1,222 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { useState } from 'react';
3
+ import {
4
+ Select,
5
+ SelectContent,
6
+ SelectGroup,
7
+ SelectItem,
8
+ SelectLabel,
9
+ SelectTrigger,
10
+ SelectValue,
11
+ SelectSeparator,
12
+ } from '../select';
13
+ import { Label } from '../label';
14
+ import { Card, CardContent, CardHeader, CardTitle } from '../card';
15
+
16
+ const meta: Meta<typeof Select> = {
17
+ title: 'UI/Select',
18
+ component: Select,
19
+ parameters: {
20
+ layout: 'centered',
21
+ },
22
+ tags: ['autodocs'],
23
+ };
24
+
25
+ export default meta;
26
+ type Story = StoryObj<typeof meta>;
27
+
28
+ export const Default: Story = {
29
+ render: () => (
30
+ <Select>
31
+ <SelectTrigger className="w-[180px]">
32
+ <SelectValue placeholder="Select a fruit" />
33
+ </SelectTrigger>
34
+ <SelectContent>
35
+ <SelectItem value="apple">Apple</SelectItem>
36
+ <SelectItem value="banana">Banana</SelectItem>
37
+ <SelectItem value="blueberry">Blueberry</SelectItem>
38
+ <SelectItem value="grapes">Grapes</SelectItem>
39
+ <SelectItem value="pineapple">Pineapple</SelectItem>
40
+ </SelectContent>
41
+ </Select>
42
+ ),
43
+ };
44
+
45
+ export const WithLabel: Story = {
46
+ render: () => (
47
+ <div className="grid w-full max-w-sm gap-1.5">
48
+ <Label htmlFor="framework">Framework</Label>
49
+ <Select>
50
+ <SelectTrigger id="framework">
51
+ <SelectValue placeholder="Select framework" />
52
+ </SelectTrigger>
53
+ <SelectContent>
54
+ <SelectItem value="next">Next.js</SelectItem>
55
+ <SelectItem value="remix">Remix</SelectItem>
56
+ <SelectItem value="astro">Astro</SelectItem>
57
+ <SelectItem value="gatsby">Gatsby</SelectItem>
58
+ </SelectContent>
59
+ </Select>
60
+ </div>
61
+ ),
62
+ };
63
+
64
+ export const Controlled: Story = {
65
+ render: () => {
66
+ const [value, setValue] = useState('');
67
+
68
+ return (
69
+ <div className="space-y-4">
70
+ <Select value={value} onValueChange={setValue}>
71
+ <SelectTrigger className="w-[180px]">
72
+ <SelectValue placeholder="Select a timezone" />
73
+ </SelectTrigger>
74
+ <SelectContent>
75
+ <SelectItem value="est">Eastern Time</SelectItem>
76
+ <SelectItem value="cst">Central Time</SelectItem>
77
+ <SelectItem value="mst">Mountain Time</SelectItem>
78
+ <SelectItem value="pst">Pacific Time</SelectItem>
79
+ </SelectContent>
80
+ </Select>
81
+ <p className="text-sm text-muted-foreground">Selected: {value || 'None'}</p>
82
+ </div>
83
+ );
84
+ },
85
+ };
86
+
87
+ export const WithGroups: Story = {
88
+ render: () => (
89
+ <Select>
90
+ <SelectTrigger className="w-[280px]">
91
+ <SelectValue placeholder="Select a timezone" />
92
+ </SelectTrigger>
93
+ <SelectContent>
94
+ <SelectGroup>
95
+ <SelectLabel>North America</SelectLabel>
96
+ <SelectItem value="est">Eastern Standard Time (EST)</SelectItem>
97
+ <SelectItem value="cst">Central Standard Time (CST)</SelectItem>
98
+ <SelectItem value="mst">Mountain Standard Time (MST)</SelectItem>
99
+ <SelectItem value="pst">Pacific Standard Time (PST)</SelectItem>
100
+ </SelectGroup>
101
+ <SelectSeparator />
102
+ <SelectGroup>
103
+ <SelectLabel>Europe</SelectLabel>
104
+ <SelectItem value="gmt">Greenwich Mean Time (GMT)</SelectItem>
105
+ <SelectItem value="cet">Central European Time (CET)</SelectItem>
106
+ <SelectItem value="eet">Eastern European Time (EET)</SelectItem>
107
+ </SelectGroup>
108
+ <SelectSeparator />
109
+ <SelectGroup>
110
+ <SelectLabel>Asia</SelectLabel>
111
+ <SelectItem value="jst">Japan Standard Time (JST)</SelectItem>
112
+ <SelectItem value="cst-china">China Standard Time (CST)</SelectItem>
113
+ <SelectItem value="ist">India Standard Time (IST)</SelectItem>
114
+ </SelectGroup>
115
+ </SelectContent>
116
+ </Select>
117
+ ),
118
+ };
119
+
120
+ export const Disabled: Story = {
121
+ render: () => (
122
+ <Select disabled>
123
+ <SelectTrigger className="w-[180px]">
124
+ <SelectValue placeholder="Disabled" />
125
+ </SelectTrigger>
126
+ <SelectContent>
127
+ <SelectItem value="option1">Option 1</SelectItem>
128
+ <SelectItem value="option2">Option 2</SelectItem>
129
+ </SelectContent>
130
+ </Select>
131
+ ),
132
+ };
133
+
134
+ export const WithDisabledItems: Story = {
135
+ render: () => (
136
+ <Select>
137
+ <SelectTrigger className="w-[180px]">
138
+ <SelectValue placeholder="Select plan" />
139
+ </SelectTrigger>
140
+ <SelectContent>
141
+ <SelectItem value="free">Free</SelectItem>
142
+ <SelectItem value="pro">Pro</SelectItem>
143
+ <SelectItem value="enterprise" disabled>
144
+ Enterprise (Coming Soon)
145
+ </SelectItem>
146
+ </SelectContent>
147
+ </Select>
148
+ ),
149
+ };
150
+
151
+ export const FormExample: Story = {
152
+ render: () => (
153
+ <form className="w-full max-w-sm space-y-4">
154
+ <div className="space-y-1.5">
155
+ <Label htmlFor="country">Country</Label>
156
+ <Select>
157
+ <SelectTrigger id="country">
158
+ <SelectValue placeholder="Select country" />
159
+ </SelectTrigger>
160
+ <SelectContent>
161
+ <SelectItem value="us">United States</SelectItem>
162
+ <SelectItem value="uk">United Kingdom</SelectItem>
163
+ <SelectItem value="ca">Canada</SelectItem>
164
+ <SelectItem value="au">Australia</SelectItem>
165
+ </SelectContent>
166
+ </Select>
167
+ </div>
168
+ <div className="space-y-1.5">
169
+ <Label htmlFor="language">Language</Label>
170
+ <Select defaultValue="en">
171
+ <SelectTrigger id="language">
172
+ <SelectValue />
173
+ </SelectTrigger>
174
+ <SelectContent>
175
+ <SelectItem value="en">English</SelectItem>
176
+ <SelectItem value="es">Spanish</SelectItem>
177
+ <SelectItem value="fr">French</SelectItem>
178
+ <SelectItem value="de">German</SelectItem>
179
+ </SelectContent>
180
+ </Select>
181
+ </div>
182
+ </form>
183
+ ),
184
+ };
185
+
186
+ export const OnCard: Story = {
187
+ render: () => (
188
+ <Card className="w-[350px]">
189
+ <CardHeader>
190
+ <CardTitle>Settings</CardTitle>
191
+ </CardHeader>
192
+ <CardContent className="space-y-4">
193
+ <div className="space-y-1.5">
194
+ <Label htmlFor="card-country">Country</Label>
195
+ <Select>
196
+ <SelectTrigger id="card-country">
197
+ <SelectValue placeholder="Select country" />
198
+ </SelectTrigger>
199
+ <SelectContent>
200
+ <SelectItem value="us">United States</SelectItem>
201
+ <SelectItem value="uk">United Kingdom</SelectItem>
202
+ <SelectItem value="ca">Canada</SelectItem>
203
+ </SelectContent>
204
+ </Select>
205
+ </div>
206
+ <div className="space-y-1.5">
207
+ <Label htmlFor="card-language">Language</Label>
208
+ <Select defaultValue="en">
209
+ <SelectTrigger id="card-language">
210
+ <SelectValue />
211
+ </SelectTrigger>
212
+ <SelectContent>
213
+ <SelectItem value="en">English</SelectItem>
214
+ <SelectItem value="es">Spanish</SelectItem>
215
+ <SelectItem value="fr">French</SelectItem>
216
+ </SelectContent>
217
+ </Select>
218
+ </div>
219
+ </CardContent>
220
+ </Card>
221
+ ),
222
+ };