@snowcone-app/ui 0.1.42 → 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 +33 -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,182 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { useState } from 'react';
3
+ import { Icon } from '@iconify/react';
4
+ import {
5
+ DropdownMenu,
6
+ DropdownMenuTrigger,
7
+ DropdownMenuContent,
8
+ DropdownMenuItem,
9
+ DropdownMenuCheckboxItem,
10
+ DropdownMenuRadioItem,
11
+ DropdownMenuLabel,
12
+ DropdownMenuSeparator,
13
+ DropdownMenuShortcut,
14
+ DropdownMenuGroup,
15
+ DropdownMenuSub,
16
+ DropdownMenuSubContent,
17
+ DropdownMenuSubTrigger,
18
+ DropdownMenuRadioGroup,
19
+ } from '../dropdown-menu';
20
+ import { Button } from '../button';
21
+
22
+ const meta: Meta<typeof DropdownMenu> = {
23
+ title: 'UI/DropdownMenu',
24
+ component: DropdownMenu,
25
+ parameters: {
26
+ layout: 'centered',
27
+ },
28
+ tags: ['autodocs'],
29
+ };
30
+
31
+ export default meta;
32
+ type Story = StoryObj<typeof meta>;
33
+
34
+ export const Default: Story = {
35
+ render: () => (
36
+ <DropdownMenu>
37
+ <DropdownMenuTrigger asChild>
38
+ <Button variant="secondary">Open Menu</Button>
39
+ </DropdownMenuTrigger>
40
+ <DropdownMenuContent className="w-56">
41
+ <DropdownMenuLabel>My Account</DropdownMenuLabel>
42
+ <DropdownMenuGroup>
43
+ <DropdownMenuItem>
44
+ <Icon icon="gravity-ui:person" />
45
+ Profile
46
+ <DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
47
+ </DropdownMenuItem>
48
+ <DropdownMenuItem>
49
+ <Icon icon="gravity-ui:gear" />
50
+ Settings
51
+ <DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
52
+ </DropdownMenuItem>
53
+ </DropdownMenuGroup>
54
+ <DropdownMenuSeparator />
55
+ <DropdownMenuItem>
56
+ <Icon icon="gravity-ui:arrow-right-from-square" />
57
+ Log out
58
+ <DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
59
+ </DropdownMenuItem>
60
+ </DropdownMenuContent>
61
+ </DropdownMenu>
62
+ ),
63
+ };
64
+
65
+ export const WithCheckboxItems: Story = {
66
+ render: () => {
67
+ const [showStatusBar, setShowStatusBar] = useState(true);
68
+ const [showActivityBar, setShowActivityBar] = useState(false);
69
+ const [showPanel, setShowPanel] = useState(false);
70
+
71
+ return (
72
+ <DropdownMenu>
73
+ <DropdownMenuTrigger asChild>
74
+ <Button variant="secondary">View Options</Button>
75
+ </DropdownMenuTrigger>
76
+ <DropdownMenuContent className="w-56">
77
+ <DropdownMenuLabel>Appearance</DropdownMenuLabel>
78
+ <DropdownMenuCheckboxItem checked={showStatusBar} onCheckedChange={setShowStatusBar}>
79
+ Status Bar
80
+ </DropdownMenuCheckboxItem>
81
+ <DropdownMenuCheckboxItem checked={showActivityBar} onCheckedChange={setShowActivityBar}>
82
+ Activity Bar
83
+ </DropdownMenuCheckboxItem>
84
+ <DropdownMenuCheckboxItem checked={showPanel} onCheckedChange={setShowPanel}>
85
+ Panel
86
+ </DropdownMenuCheckboxItem>
87
+ </DropdownMenuContent>
88
+ </DropdownMenu>
89
+ );
90
+ },
91
+ };
92
+
93
+ export const WithRadioItems: Story = {
94
+ render: () => {
95
+ const [position, setPosition] = useState('bottom');
96
+
97
+ return (
98
+ <DropdownMenu>
99
+ <DropdownMenuTrigger asChild>
100
+ <Button variant="secondary">Panel Position</Button>
101
+ </DropdownMenuTrigger>
102
+ <DropdownMenuContent className="w-56">
103
+ <DropdownMenuLabel>Panel Position</DropdownMenuLabel>
104
+ <DropdownMenuRadioGroup value={position} onValueChange={setPosition}>
105
+ <DropdownMenuRadioItem value="top">Top</DropdownMenuRadioItem>
106
+ <DropdownMenuRadioItem value="bottom">Bottom</DropdownMenuRadioItem>
107
+ <DropdownMenuRadioItem value="right">Right</DropdownMenuRadioItem>
108
+ </DropdownMenuRadioGroup>
109
+ </DropdownMenuContent>
110
+ </DropdownMenu>
111
+ );
112
+ },
113
+ };
114
+
115
+ export const WithSubMenu: Story = {
116
+ render: () => (
117
+ <DropdownMenu>
118
+ <DropdownMenuTrigger asChild>
119
+ <Button variant="secondary">More Options</Button>
120
+ </DropdownMenuTrigger>
121
+ <DropdownMenuContent className="w-56">
122
+ <DropdownMenuItem>
123
+ <Icon icon="gravity-ui:file" />
124
+ New File
125
+ </DropdownMenuItem>
126
+ <DropdownMenuSub>
127
+ <DropdownMenuSubTrigger>
128
+ <Icon icon="gravity-ui:persons" />
129
+ Invite users
130
+ </DropdownMenuSubTrigger>
131
+ <DropdownMenuSubContent>
132
+ <DropdownMenuItem>
133
+ <Icon icon="gravity-ui:envelope" />
134
+ Email
135
+ </DropdownMenuItem>
136
+ <DropdownMenuItem>
137
+ <Icon icon="gravity-ui:comment" />
138
+ Message
139
+ </DropdownMenuItem>
140
+ <DropdownMenuSeparator />
141
+ <DropdownMenuItem>
142
+ <Icon icon="gravity-ui:circle-plus" />
143
+ More...
144
+ </DropdownMenuItem>
145
+ </DropdownMenuSubContent>
146
+ </DropdownMenuSub>
147
+ <DropdownMenuSeparator />
148
+ <DropdownMenuItem>
149
+ <Icon icon="gravity-ui:gear" />
150
+ Settings
151
+ </DropdownMenuItem>
152
+ </DropdownMenuContent>
153
+ </DropdownMenu>
154
+ ),
155
+ };
156
+
157
+ export const ContextMenuStyle: Story = {
158
+ render: () => (
159
+ <DropdownMenu>
160
+ <DropdownMenuTrigger asChild>
161
+ <Button variant="ghost" size="icon">
162
+ <Icon icon="gravity-ui:ellipsis" className="size-5" />
163
+ </Button>
164
+ </DropdownMenuTrigger>
165
+ <DropdownMenuContent align="end">
166
+ <DropdownMenuItem>
167
+ <Icon icon="gravity-ui:pencil" />
168
+ Edit
169
+ </DropdownMenuItem>
170
+ <DropdownMenuItem>
171
+ <Icon icon="gravity-ui:copy" />
172
+ Duplicate
173
+ </DropdownMenuItem>
174
+ <DropdownMenuSeparator />
175
+ <DropdownMenuItem className="text-danger">
176
+ <Icon icon="gravity-ui:trash-bin" />
177
+ Delete
178
+ </DropdownMenuItem>
179
+ </DropdownMenuContent>
180
+ </DropdownMenu>
181
+ ),
182
+ };
@@ -0,0 +1,212 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { Fieldset, Legend } from '../fieldset';
3
+ import { TextField } from '../text-field';
4
+ import { Input } from '../input';
5
+ import { Label } from '../label';
6
+ import { RadioGroup, Radio } from '../radio-group';
7
+ import { Checkbox } from '../checkbox';
8
+ import { Card, CardContent, CardHeader, CardTitle } from '../card';
9
+ import { Button } from '../button';
10
+
11
+ const meta: Meta<typeof Fieldset> = {
12
+ title: 'UI/Fieldset',
13
+ component: Fieldset,
14
+ parameters: {
15
+ layout: 'centered',
16
+ },
17
+ tags: ['autodocs'],
18
+ argTypes: {
19
+ disabled: {
20
+ control: 'boolean',
21
+ description: 'When true, all form elements inside will be disabled',
22
+ },
23
+ },
24
+ };
25
+
26
+ export default meta;
27
+ type Story = StoryObj<typeof meta>;
28
+
29
+ export const Default: Story = {
30
+ render: () => (
31
+ <Fieldset className="w-[300px]">
32
+ <Legend>Personal Information</Legend>
33
+ <TextField>
34
+ <Label>First Name</Label>
35
+ <Input placeholder="John" />
36
+ </TextField>
37
+ <TextField>
38
+ <Label>Last Name</Label>
39
+ <Input placeholder="Doe" />
40
+ </TextField>
41
+ </Fieldset>
42
+ ),
43
+ };
44
+
45
+ export const WithRadioGroup: Story = {
46
+ render: () => (
47
+ <Fieldset className="w-[300px]">
48
+ <Legend>Notification Preferences</Legend>
49
+ <RadioGroup defaultValue="email">
50
+ <div className="flex items-center space-x-2">
51
+ <Radio value="email" id="pref-email" />
52
+ <Label htmlFor="pref-email">Email</Label>
53
+ </div>
54
+ <div className="flex items-center space-x-2">
55
+ <Radio value="sms" id="pref-sms" />
56
+ <Label htmlFor="pref-sms">SMS</Label>
57
+ </div>
58
+ <div className="flex items-center space-x-2">
59
+ <Radio value="push" id="pref-push" />
60
+ <Label htmlFor="pref-push">Push Notifications</Label>
61
+ </div>
62
+ </RadioGroup>
63
+ </Fieldset>
64
+ ),
65
+ };
66
+
67
+ export const WithCheckboxes: Story = {
68
+ render: () => (
69
+ <Fieldset className="w-[300px]">
70
+ <Legend>Interests</Legend>
71
+ <div className="space-y-2">
72
+ <div className="flex items-center space-x-2">
73
+ <Checkbox id="interest-tech" />
74
+ <Label htmlFor="interest-tech">Technology</Label>
75
+ </div>
76
+ <div className="flex items-center space-x-2">
77
+ <Checkbox id="interest-design" />
78
+ <Label htmlFor="interest-design">Design</Label>
79
+ </div>
80
+ <div className="flex items-center space-x-2">
81
+ <Checkbox id="interest-business" />
82
+ <Label htmlFor="interest-business">Business</Label>
83
+ </div>
84
+ </div>
85
+ </Fieldset>
86
+ ),
87
+ };
88
+
89
+ export const Disabled: Story = {
90
+ render: () => (
91
+ <Fieldset className="w-[300px]" disabled>
92
+ <Legend>Disabled Fieldset</Legend>
93
+ <TextField>
94
+ <Label>Name</Label>
95
+ <Input placeholder="Cannot edit" />
96
+ </TextField>
97
+ <TextField>
98
+ <Label>Email</Label>
99
+ <Input type="email" placeholder="Cannot edit" />
100
+ </TextField>
101
+ </Fieldset>
102
+ ),
103
+ };
104
+
105
+ export const MultipleFieldsets: Story = {
106
+ render: () => (
107
+ <form className="w-[350px] space-y-6">
108
+ <Fieldset>
109
+ <Legend>Account Information</Legend>
110
+ <TextField>
111
+ <Label>Username</Label>
112
+ <Input placeholder="Choose a username" />
113
+ </TextField>
114
+ <TextField>
115
+ <Label>Email</Label>
116
+ <Input type="email" placeholder="your@email.com" />
117
+ </TextField>
118
+ </Fieldset>
119
+
120
+ <Fieldset>
121
+ <Legend>Security</Legend>
122
+ <TextField>
123
+ <Label>Password</Label>
124
+ <Input type="password" placeholder="Create password" />
125
+ </TextField>
126
+ <TextField>
127
+ <Label>Confirm Password</Label>
128
+ <Input type="password" placeholder="Confirm password" />
129
+ </TextField>
130
+ </Fieldset>
131
+
132
+ <Fieldset>
133
+ <Legend>Preferences</Legend>
134
+ <div className="flex items-center space-x-2">
135
+ <Checkbox id="newsletter" />
136
+ <Label htmlFor="newsletter">Subscribe to newsletter</Label>
137
+ </div>
138
+ <div className="flex items-center space-x-2">
139
+ <Checkbox id="terms" />
140
+ <Label htmlFor="terms">I agree to the terms</Label>
141
+ </div>
142
+ </Fieldset>
143
+ </form>
144
+ ),
145
+ };
146
+
147
+ export const OnCard: Story = {
148
+ render: () => (
149
+ <Card className="w-[400px]">
150
+ <CardHeader>
151
+ <CardTitle>Registration Form</CardTitle>
152
+ </CardHeader>
153
+ <CardContent className="space-y-6">
154
+ <Fieldset>
155
+ <Legend>Contact Details</Legend>
156
+ <TextField>
157
+ <Label>Full Name</Label>
158
+ <Input placeholder="Enter your name" />
159
+ </TextField>
160
+ <TextField>
161
+ <Label>Phone</Label>
162
+ <Input type="tel" placeholder="+1 (555) 000-0000" />
163
+ </TextField>
164
+ </Fieldset>
165
+
166
+ <Fieldset>
167
+ <Legend>Communication</Legend>
168
+ <RadioGroup defaultValue="email">
169
+ <div className="flex items-center space-x-2">
170
+ <Radio value="email" id="comm-email" />
171
+ <Label htmlFor="comm-email">Contact via Email</Label>
172
+ </div>
173
+ <div className="flex items-center space-x-2">
174
+ <Radio value="phone" id="comm-phone" />
175
+ <Label htmlFor="comm-phone">Contact via Phone</Label>
176
+ </div>
177
+ </RadioGroup>
178
+ </Fieldset>
179
+
180
+ <Button className="w-full">Submit</Button>
181
+ </CardContent>
182
+ </Card>
183
+ ),
184
+ };
185
+
186
+ export const DisabledExample: Story = {
187
+ name: 'Disabled Example',
188
+ render: () => (
189
+ <div className="w-[350px] space-y-4">
190
+ <p className="text-sm text-foreground/60 mb-4">
191
+ Fieldset is used for semantic grouping of form controls.
192
+ Use the <code>disabled</code> prop to disable all child form elements.
193
+ </p>
194
+
195
+ <Fieldset>
196
+ <Legend>Normal Fieldset</Legend>
197
+ <TextField>
198
+ <Label>Input</Label>
199
+ <Input placeholder="Editable" />
200
+ </TextField>
201
+ </Fieldset>
202
+
203
+ <Fieldset disabled>
204
+ <Legend>Disabled Fieldset</Legend>
205
+ <TextField>
206
+ <Label>Input</Label>
207
+ <Input placeholder="Disabled" />
208
+ </TextField>
209
+ </Fieldset>
210
+ </div>
211
+ ),
212
+ };
@@ -0,0 +1,172 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { Icon } from '@iconify/react';
3
+ import { Input } from '../input';
4
+ import { Label } from '../label';
5
+ import { Card, CardContent, CardHeader, CardTitle } from '../card';
6
+
7
+ const meta: Meta<typeof Input> = {
8
+ title: 'UI/Input',
9
+ component: Input,
10
+ parameters: {
11
+ layout: 'centered',
12
+ },
13
+ tags: ['autodocs'],
14
+ argTypes: {
15
+ type: {
16
+ control: 'select',
17
+ options: ['text', 'email', 'password', 'number', 'search', 'tel', 'url'],
18
+ },
19
+ disabled: {
20
+ control: 'boolean',
21
+ description: 'Disables the input',
22
+ },
23
+ isInvalid: {
24
+ control: 'boolean',
25
+ description: 'Shows error state',
26
+ },
27
+ },
28
+ };
29
+
30
+ export default meta;
31
+ type Story = StoryObj<typeof meta>;
32
+
33
+ export const Default: Story = {
34
+ args: {
35
+ placeholder: 'Enter text...',
36
+ },
37
+ decorators: [
38
+ (Story) => (
39
+ <div className="w-[300px]">
40
+ <Story />
41
+ </div>
42
+ ),
43
+ ],
44
+ };
45
+
46
+ export const WithLabel: Story = {
47
+ render: () => (
48
+ <div className="flex w-full max-w-sm flex-col gap-1.5">
49
+ <Label htmlFor="email">Email</Label>
50
+ <Input type="email" id="email" placeholder="Enter your email" />
51
+ </div>
52
+ ),
53
+ };
54
+
55
+ export const WithStartContent: Story = {
56
+ render: () => (
57
+ <div className="w-[300px]">
58
+ <Input
59
+ placeholder="Search..."
60
+ startContent={<Icon icon="gravity-ui:magnifier" className="size-4" />}
61
+ />
62
+ </div>
63
+ ),
64
+ };
65
+
66
+ export const WithEndContent: Story = {
67
+ render: () => (
68
+ <div className="w-[300px]">
69
+ <Input
70
+ type="password"
71
+ placeholder="Enter password"
72
+ endContent={<Icon icon="gravity-ui:eye" className="size-4 cursor-pointer" />}
73
+ />
74
+ </div>
75
+ ),
76
+ };
77
+
78
+ export const WithBothContent: Story = {
79
+ render: () => (
80
+ <div className="w-[300px]">
81
+ <Input
82
+ type="text"
83
+ placeholder="Amount"
84
+ startContent={<span className="text-sm">$</span>}
85
+ endContent={<span className="text-sm">.00</span>}
86
+ />
87
+ </div>
88
+ ),
89
+ };
90
+
91
+ export const Invalid: Story = {
92
+ render: () => (
93
+ <div className="flex w-full max-w-sm flex-col gap-1.5">
94
+ <Label htmlFor="email-invalid">Email</Label>
95
+ <Input type="email" id="email-invalid" placeholder="Enter email" isInvalid />
96
+ <p className="text-sm text-danger">Please enter a valid email address.</p>
97
+ </div>
98
+ ),
99
+ };
100
+
101
+ export const Disabled: Story = {
102
+ args: {
103
+ placeholder: 'Disabled input',
104
+ disabled: true,
105
+ },
106
+ decorators: [
107
+ (Story) => (
108
+ <div className="w-[300px]">
109
+ <Story />
110
+ </div>
111
+ ),
112
+ ],
113
+ };
114
+
115
+
116
+ export const File: Story = {
117
+ render: () => (
118
+ <div className="flex w-full max-w-sm flex-col gap-1.5">
119
+ <Label htmlFor="file">Upload File</Label>
120
+ <Input id="file" type="file" />
121
+ </div>
122
+ ),
123
+ };
124
+
125
+ export const FormExample: Story = {
126
+ render: () => (
127
+ <form className="w-full max-w-sm space-y-4">
128
+ <div className="space-y-1.5">
129
+ <Label htmlFor="name">Full Name</Label>
130
+ <Input id="name" placeholder="John Doe" />
131
+ </div>
132
+ <div className="space-y-1.5">
133
+ <Label htmlFor="form-email">Email</Label>
134
+ <Input
135
+ id="form-email"
136
+ type="email"
137
+ placeholder="john@example.com"
138
+ startContent={<Icon icon="gravity-ui:envelope" className="size-4" />}
139
+ />
140
+ </div>
141
+ <div className="space-y-1.5">
142
+ <Label htmlFor="phone">Phone</Label>
143
+ <Input
144
+ id="phone"
145
+ type="tel"
146
+ placeholder="+1 (555) 000-0000"
147
+ startContent={<Icon icon="gravity-ui:smartphone" className="size-4" />}
148
+ />
149
+ </div>
150
+ </form>
151
+ ),
152
+ };
153
+
154
+ export const OnCard: Story = {
155
+ render: () => (
156
+ <Card className="w-[350px]">
157
+ <CardHeader>
158
+ <CardTitle>Contact Info</CardTitle>
159
+ </CardHeader>
160
+ <CardContent className="space-y-4">
161
+ <div className="space-y-1.5">
162
+ <Label htmlFor="card-name">Name</Label>
163
+ <Input id="card-name" placeholder="John Doe" />
164
+ </div>
165
+ <div className="space-y-1.5">
166
+ <Label htmlFor="card-email">Email</Label>
167
+ <Input id="card-email" type="email" placeholder="john@example.com" />
168
+ </div>
169
+ </CardContent>
170
+ </Card>
171
+ ),
172
+ };