sonance-brand-mcp 1.1.4 → 1.2.2

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 (73) hide show
  1. package/dist/assets/BRAND_GUIDELINES.md +0 -8
  2. package/dist/assets/components/accordion.stories.tsx +310 -0
  3. package/dist/assets/components/accordion.tsx +56 -30
  4. package/dist/assets/components/alert.stories.tsx +199 -0
  5. package/dist/assets/components/autocomplete.stories.tsx +307 -0
  6. package/dist/assets/components/autocomplete.tsx +28 -4
  7. package/dist/assets/components/avatar.stories.tsx +175 -0
  8. package/dist/assets/components/badge.stories.tsx +258 -0
  9. package/dist/assets/components/breadcrumbs.stories.tsx +175 -0
  10. package/dist/assets/components/button.stories.tsx +362 -0
  11. package/dist/assets/components/button.tsx +48 -3
  12. package/dist/assets/components/calendar.stories.tsx +247 -0
  13. package/dist/assets/components/card.stories.tsx +275 -0
  14. package/dist/assets/components/card.tsx +26 -1
  15. package/dist/assets/components/checkbox-group.stories.tsx +281 -0
  16. package/dist/assets/components/checkbox.stories.tsx +160 -0
  17. package/dist/assets/components/checkbox.tsx +32 -4
  18. package/dist/assets/components/code.stories.tsx +265 -0
  19. package/dist/assets/components/date-input.stories.tsx +278 -0
  20. package/dist/assets/components/date-input.tsx +24 -2
  21. package/dist/assets/components/date-picker.stories.tsx +337 -0
  22. package/dist/assets/components/date-picker.tsx +28 -4
  23. package/dist/assets/components/date-range-picker.stories.tsx +340 -0
  24. package/dist/assets/components/dialog.stories.tsx +285 -0
  25. package/dist/assets/components/divider.stories.tsx +176 -0
  26. package/dist/assets/components/drawer.stories.tsx +216 -0
  27. package/dist/assets/components/dropdown.stories.tsx +342 -0
  28. package/dist/assets/components/dropdown.tsx +55 -10
  29. package/dist/assets/components/form.stories.tsx +372 -0
  30. package/dist/assets/components/image.stories.tsx +348 -0
  31. package/dist/assets/components/input-otp.stories.tsx +336 -0
  32. package/dist/assets/components/input-otp.tsx +24 -2
  33. package/dist/assets/components/input.stories.tsx +223 -0
  34. package/dist/assets/components/input.tsx +27 -2
  35. package/dist/assets/components/kbd.stories.tsx +272 -0
  36. package/dist/assets/components/link.stories.tsx +199 -0
  37. package/dist/assets/components/link.tsx +50 -1
  38. package/dist/assets/components/listbox.stories.tsx +287 -0
  39. package/dist/assets/components/listbox.tsx +30 -7
  40. package/dist/assets/components/navbar.stories.tsx +218 -0
  41. package/dist/assets/components/number-input.stories.tsx +295 -0
  42. package/dist/assets/components/number-input.tsx +30 -8
  43. package/dist/assets/components/pagination.stories.tsx +280 -0
  44. package/dist/assets/components/pagination.tsx +45 -21
  45. package/dist/assets/components/popover.stories.tsx +219 -0
  46. package/dist/assets/components/progress.stories.tsx +153 -0
  47. package/dist/assets/components/radio-group.stories.tsx +187 -0
  48. package/dist/assets/components/radio-group.tsx +30 -6
  49. package/dist/assets/components/range-calendar.stories.tsx +334 -0
  50. package/dist/assets/components/scroll-shadow.stories.tsx +335 -0
  51. package/dist/assets/components/select.stories.tsx +192 -0
  52. package/dist/assets/components/select.tsx +54 -7
  53. package/dist/assets/components/skeleton.stories.tsx +166 -0
  54. package/dist/assets/components/slider.stories.tsx +145 -0
  55. package/dist/assets/components/slider.tsx +43 -8
  56. package/dist/assets/components/spacer.stories.tsx +216 -0
  57. package/dist/assets/components/spinner.stories.tsx +149 -0
  58. package/dist/assets/components/switch.stories.tsx +170 -0
  59. package/dist/assets/components/switch.tsx +29 -4
  60. package/dist/assets/components/table.stories.tsx +322 -0
  61. package/dist/assets/components/tabs.stories.tsx +306 -0
  62. package/dist/assets/components/tabs.tsx +25 -4
  63. package/dist/assets/components/textarea.stories.tsx +103 -0
  64. package/dist/assets/components/textarea.tsx +27 -3
  65. package/dist/assets/components/theme-toggle.stories.tsx +248 -0
  66. package/dist/assets/components/time-input.stories.tsx +365 -0
  67. package/dist/assets/components/time-input.tsx +25 -3
  68. package/dist/assets/components/toast.stories.tsx +195 -0
  69. package/dist/assets/components/tooltip.stories.tsx +226 -0
  70. package/dist/assets/components/user.stories.tsx +274 -0
  71. package/dist/assets/logo-manifest.json +0 -18
  72. package/dist/index.js +2142 -85
  73. package/package.json +1 -1
@@ -3,6 +3,35 @@
3
3
  import { forwardRef, useState, useRef, useCallback } from "react";
4
4
  import { cn } from "@/lib/utils";
5
5
 
6
+ export type SliderState = "default" | "hover" | "focus" | "active" | "disabled";
7
+
8
+ // State styles for Storybook/Figma visualization
9
+ const getTrackStateStyles = (state?: SliderState) => {
10
+ if (!state || state === "default") return "";
11
+
12
+ const stateMap: Record<string, string> = {
13
+ hover: "",
14
+ focus: "ring-2 ring-border-focus ring-offset-2 ring-offset-background",
15
+ active: "",
16
+ disabled: "opacity-50 cursor-not-allowed",
17
+ };
18
+
19
+ return stateMap[state] || "";
20
+ };
21
+
22
+ const getThumbStateStyles = (state?: SliderState) => {
23
+ if (!state || state === "default") return "";
24
+
25
+ const stateMap: Record<string, string> = {
26
+ hover: "scale-110",
27
+ focus: "",
28
+ active: "scale-110",
29
+ disabled: "hover:scale-100",
30
+ };
31
+
32
+ return stateMap[state] || "";
33
+ };
34
+
6
35
  interface SliderProps {
7
36
  value?: number;
8
37
  defaultValue?: number;
@@ -14,6 +43,8 @@ interface SliderProps {
14
43
  showValue?: boolean;
15
44
  disabled?: boolean;
16
45
  className?: string;
46
+ /** Visual state for Storybook/Figma documentation */
47
+ state?: SliderState;
17
48
  }
18
49
 
19
50
  export const Slider = forwardRef<HTMLDivElement, SliderProps>(
@@ -29,9 +60,11 @@ export const Slider = forwardRef<HTMLDivElement, SliderProps>(
29
60
  showValue = false,
30
61
  disabled = false,
31
62
  className,
63
+ state,
32
64
  },
33
65
  ref
34
66
  ) => {
67
+ const isDisabled = disabled || state === "disabled";
35
68
  const [internalValue, setInternalValue] = useState(defaultValue);
36
69
  const trackRef = useRef<HTMLDivElement>(null);
37
70
 
@@ -40,7 +73,7 @@ export const Slider = forwardRef<HTMLDivElement, SliderProps>(
40
73
 
41
74
  const updateValue = useCallback(
42
75
  (clientX: number) => {
43
- if (!trackRef.current || disabled) return;
76
+ if (!trackRef.current || isDisabled) return;
44
77
 
45
78
  const rect = trackRef.current.getBoundingClientRect();
46
79
  const percent = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
@@ -51,11 +84,11 @@ export const Slider = forwardRef<HTMLDivElement, SliderProps>(
51
84
  setInternalValue(clampedValue);
52
85
  onValueChange?.(clampedValue);
53
86
  },
54
- [disabled, min, max, step, onValueChange]
87
+ [isDisabled, min, max, step, onValueChange]
55
88
  );
56
89
 
57
90
  const handleMouseDown = (e: React.MouseEvent) => {
58
- if (disabled) return;
91
+ if (isDisabled) return;
59
92
 
60
93
  updateValue(e.clientX);
61
94
 
@@ -73,7 +106,7 @@ export const Slider = forwardRef<HTMLDivElement, SliderProps>(
73
106
  };
74
107
 
75
108
  const handleKeyDown = (e: React.KeyboardEvent) => {
76
- if (disabled) return;
109
+ if (isDisabled) return;
77
110
 
78
111
  let newValue = value;
79
112
  switch (e.key) {
@@ -120,14 +153,15 @@ export const Slider = forwardRef<HTMLDivElement, SliderProps>(
120
153
  aria-valuemin={min}
121
154
  aria-valuemax={max}
122
155
  aria-valuenow={value}
123
- aria-disabled={disabled}
124
- tabIndex={disabled ? -1 : 0}
156
+ aria-disabled={isDisabled}
157
+ tabIndex={isDisabled ? -1 : 0}
125
158
  onMouseDown={handleMouseDown}
126
159
  onKeyDown={handleKeyDown}
127
160
  className={cn(
128
161
  "relative h-2 w-full cursor-pointer rounded-full bg-background-secondary",
129
162
  "focus:outline-none focus:ring-2 focus:ring-border-focus focus:ring-offset-2 focus:ring-offset-background",
130
- disabled && "cursor-not-allowed opacity-50"
163
+ isDisabled && "cursor-not-allowed opacity-50",
164
+ getTrackStateStyles(state)
131
165
  )}
132
166
  >
133
167
  {/* Filled track */}
@@ -141,7 +175,8 @@ export const Slider = forwardRef<HTMLDivElement, SliderProps>(
141
175
  "absolute top-1/2 h-5 w-5 -translate-x-1/2 -translate-y-1/2 rounded-full",
142
176
  "border-2 border-primary bg-background shadow-sm transition-all",
143
177
  "hover:scale-110",
144
- disabled && "hover:scale-100"
178
+ isDisabled && "hover:scale-100",
179
+ getThumbStateStyles(state)
145
180
  )}
146
181
  style={{ left: `${percentage}%` }}
147
182
  />
@@ -0,0 +1,216 @@
1
+ import type { Meta, StoryObj } from '@storybook/nextjs-vite';
2
+ import { Spacer, SpacerXS, SpacerSM, SpacerMD, SpacerLG, SpacerXL, FlexSpacer } from './spacer';
3
+
4
+ const meta: Meta<typeof Spacer> = {
5
+ title: 'Components/Utilities/Spacer',
6
+ component: Spacer,
7
+ tags: ['autodocs'],
8
+ parameters: {
9
+ docs: {
10
+ description: {
11
+ component: 'A utility component for adding consistent spacing between elements.',
12
+ },
13
+ },
14
+ },
15
+ argTypes: {
16
+ x: { control: { type: 'number', min: 0, max: 20 } },
17
+ y: { control: { type: 'number', min: 0, max: 20 } },
18
+ },
19
+ };
20
+
21
+ export default meta;
22
+ type Story = StoryObj<typeof meta>;
23
+
24
+ export const Vertical: Story = {
25
+ render: () => (
26
+ <div className="border border-dashed border-border p-4 w-64">
27
+ <div className="p-2 bg-primary text-primary-foreground text-center text-sm">Block 1</div>
28
+ <Spacer y={4} />
29
+ <div className="p-2 bg-primary text-primary-foreground text-center text-sm">Block 2</div>
30
+ <Spacer y={8} />
31
+ <div className="p-2 bg-primary text-primary-foreground text-center text-sm">Block 3</div>
32
+ </div>
33
+ ),
34
+ };
35
+
36
+ export const Horizontal: Story = {
37
+ render: () => (
38
+ <div className="flex items-center border border-dashed border-border p-4">
39
+ <div className="p-2 bg-primary text-primary-foreground text-center text-sm">A</div>
40
+ <Spacer x={4} />
41
+ <div className="p-2 bg-primary text-primary-foreground text-center text-sm">B</div>
42
+ <Spacer x={8} />
43
+ <div className="p-2 bg-primary text-primary-foreground text-center text-sm">C</div>
44
+ </div>
45
+ ),
46
+ };
47
+
48
+ export const PresetSizes: Story = {
49
+ render: () => (
50
+ <div className="border border-dashed border-border p-4 w-64">
51
+ <div className="p-2 bg-secondary text-foreground text-center text-xs">SpacerXS (8px)</div>
52
+ <SpacerXS />
53
+ <div className="p-2 bg-secondary text-foreground text-center text-xs">SpacerSM (16px)</div>
54
+ <SpacerSM />
55
+ <div className="p-2 bg-secondary text-foreground text-center text-xs">SpacerMD (24px)</div>
56
+ <SpacerMD />
57
+ <div className="p-2 bg-secondary text-foreground text-center text-xs">SpacerLG (32px)</div>
58
+ <SpacerLG />
59
+ <div className="p-2 bg-secondary text-foreground text-center text-xs">SpacerXL (48px)</div>
60
+ <SpacerXL />
61
+ <div className="p-2 bg-secondary text-foreground text-center text-xs">End</div>
62
+ </div>
63
+ ),
64
+ };
65
+
66
+ export const FlexSpacerExample: Story = {
67
+ render: () => (
68
+ <div className="flex items-center border border-border p-4 w-96">
69
+ <span className="text-foreground font-medium">Logo</span>
70
+ <FlexSpacer />
71
+ <button className="px-3 py-1 text-sm text-foreground-muted hover:text-foreground">About</button>
72
+ <Spacer x={2} />
73
+ <button className="px-3 py-1 text-sm text-foreground-muted hover:text-foreground">Contact</button>
74
+ <Spacer x={4} />
75
+ <button className="px-3 py-1 text-sm bg-primary text-primary-foreground">Sign In</button>
76
+ </div>
77
+ ),
78
+ };
79
+
80
+ export const CardLayout: Story = {
81
+ render: () => (
82
+ <div className="border border-border p-6 w-80">
83
+ <h3 className="text-lg font-medium text-foreground">Card Title</h3>
84
+ <SpacerXS />
85
+ <p className="text-sm text-foreground-muted">A subtitle or meta information</p>
86
+ <SpacerMD />
87
+ <p className="text-foreground-secondary">
88
+ This is the main content of the card. Notice how spacers provide consistent rhythm between elements.
89
+ </p>
90
+ <SpacerLG />
91
+ <div className="flex">
92
+ <button className="text-sm text-foreground-muted hover:text-foreground">Cancel</button>
93
+ <FlexSpacer />
94
+ <button className="px-4 py-2 bg-primary text-primary-foreground text-sm">Confirm</button>
95
+ </div>
96
+ </div>
97
+ ),
98
+ };
99
+
100
+ export const ResponsiveSpacing: Story = {
101
+ render: () => (
102
+ <div className="space-y-4">
103
+ <p className="text-xs text-foreground-muted">
104
+ Spacer uses a 4px base unit. y=1 is 4px, y=4 is 16px, etc.
105
+ </p>
106
+ <div className="border border-dashed border-border p-4 w-64">
107
+ {[1, 2, 4, 6, 8, 12].map((size) => (
108
+ <div key={size}>
109
+ <div className="p-2 bg-secondary text-foreground text-center text-xs">
110
+ y={size} ({size * 4}px)
111
+ </div>
112
+ <Spacer y={size} />
113
+ </div>
114
+ ))}
115
+ <div className="p-2 bg-secondary text-foreground text-center text-xs">End</div>
116
+ </div>
117
+ </div>
118
+ ),
119
+ };
120
+
121
+ export const FormLayout: Story = {
122
+ render: () => (
123
+ <form className="w-80 border border-border p-6">
124
+ <h2 className="text-lg font-medium text-foreground">Contact Form</h2>
125
+ <SpacerMD />
126
+
127
+ <label className="block text-sm font-medium text-foreground">Name</label>
128
+ <SpacerXS />
129
+ <input
130
+ type="text"
131
+ className="w-full px-3 py-2 border border-border bg-background text-foreground"
132
+ placeholder="Enter your name"
133
+ />
134
+
135
+ <SpacerSM />
136
+
137
+ <label className="block text-sm font-medium text-foreground">Email</label>
138
+ <SpacerXS />
139
+ <input
140
+ type="email"
141
+ className="w-full px-3 py-2 border border-border bg-background text-foreground"
142
+ placeholder="Enter your email"
143
+ />
144
+
145
+ <SpacerSM />
146
+
147
+ <label className="block text-sm font-medium text-foreground">Message</label>
148
+ <SpacerXS />
149
+ <textarea
150
+ className="w-full px-3 py-2 border border-border bg-background text-foreground"
151
+ placeholder="Your message"
152
+ rows={3}
153
+ />
154
+
155
+ <SpacerLG />
156
+
157
+ <button
158
+ type="submit"
159
+ className="w-full px-4 py-2 bg-primary text-primary-foreground"
160
+ >
161
+ Send Message
162
+ </button>
163
+ </form>
164
+ ),
165
+ };
166
+
167
+ // Responsive Matrix - Mobile, Tablet, Desktop
168
+ export const ResponsiveMatrix: Story = {
169
+ render: () => (
170
+ <div className="space-y-8">
171
+ {/* Mobile */}
172
+ <div>
173
+ <h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
174
+ <div className="w-[375px] border border-dashed border-border p-4">
175
+ <div className="p-2 bg-primary text-primary-foreground text-center text-sm">Header</div>
176
+ <SpacerMD />
177
+ <div className="p-2 bg-secondary text-foreground text-center text-sm">Content</div>
178
+ <SpacerLG />
179
+ <div className="p-2 bg-primary text-primary-foreground text-center text-sm">Footer</div>
180
+ </div>
181
+ </div>
182
+ {/* Tablet */}
183
+ <div>
184
+ <h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
185
+ <div className="w-[768px] border border-dashed border-border p-4">
186
+ <div className="flex items-center">
187
+ <span className="text-foreground font-medium">Logo</span>
188
+ <FlexSpacer />
189
+ <button className="px-3 py-1 text-sm text-foreground-muted">About</button>
190
+ <Spacer x={2} />
191
+ <button className="px-3 py-1 text-sm text-foreground-muted">Contact</button>
192
+ <Spacer x={4} />
193
+ <button className="px-3 py-1 bg-primary text-primary-foreground text-sm">Sign In</button>
194
+ </div>
195
+ </div>
196
+ </div>
197
+ {/* Desktop */}
198
+ <div>
199
+ <h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
200
+ <div className="w-[1280px] border border-dashed border-border p-4">
201
+ <div className="flex items-center">
202
+ <span className="text-foreground font-medium">Sonance</span>
203
+ <Spacer x={8} />
204
+ <button className="px-3 py-1 text-sm text-foreground-muted">Products</button>
205
+ <Spacer x={4} />
206
+ <button className="px-3 py-1 text-sm text-foreground-muted">Support</button>
207
+ <Spacer x={4} />
208
+ <button className="px-3 py-1 text-sm text-foreground-muted">About</button>
209
+ <FlexSpacer />
210
+ <button className="px-4 py-2 bg-primary text-primary-foreground text-sm">Get Started</button>
211
+ </div>
212
+ </div>
213
+ </div>
214
+ </div>
215
+ ),
216
+ };
@@ -0,0 +1,149 @@
1
+ import type { Meta, StoryObj } from '@storybook/nextjs-vite';
2
+ import { Spinner, DotsSpinner } from './spinner';
3
+
4
+ const meta: Meta<typeof Spinner> = {
5
+ title: 'Components/Feedback/Spinner',
6
+ component: Spinner,
7
+ tags: ['autodocs'],
8
+ parameters: {
9
+ docs: {
10
+ description: {
11
+ component: 'Loading spinner components for indicating async operations.',
12
+ },
13
+ },
14
+ },
15
+ argTypes: {
16
+ size: {
17
+ control: 'select',
18
+ options: ['sm', 'md', 'lg', 'xl'],
19
+ },
20
+ label: { control: 'text' },
21
+ },
22
+ };
23
+
24
+ export default meta;
25
+ type Story = StoryObj<typeof meta>;
26
+
27
+ export const Default: Story = {
28
+ args: {},
29
+ };
30
+
31
+ export const WithLabel: Story = {
32
+ args: {
33
+ label: 'Loading...',
34
+ },
35
+ };
36
+
37
+ export const AllSizes: Story = {
38
+ render: () => (
39
+ <div className="flex items-end gap-6">
40
+ <Spinner size="sm" />
41
+ <Spinner size="md" />
42
+ <Spinner size="lg" />
43
+ <Spinner size="xl" />
44
+ </div>
45
+ ),
46
+ };
47
+
48
+ export const AllSizesWithLabels: Story = {
49
+ render: () => (
50
+ <div className="flex items-start gap-8">
51
+ <Spinner size="sm" label="Small" />
52
+ <Spinner size="md" label="Medium" />
53
+ <Spinner size="lg" label="Large" />
54
+ <Spinner size="xl" label="Extra Large" />
55
+ </div>
56
+ ),
57
+ };
58
+
59
+ // Dots Spinner Stories
60
+ export const Dots: StoryObj<typeof DotsSpinner> = {
61
+ render: () => <DotsSpinner />,
62
+ };
63
+
64
+ export const DotsSizes: StoryObj<typeof DotsSpinner> = {
65
+ render: () => (
66
+ <div className="flex items-center gap-8">
67
+ <DotsSpinner size="sm" />
68
+ <DotsSpinner size="md" />
69
+ <DotsSpinner size="lg" />
70
+ </div>
71
+ ),
72
+ };
73
+
74
+ export const LoadingStates: Story = {
75
+ render: () => (
76
+ <div className="space-y-8">
77
+ <div className="flex items-center gap-4">
78
+ <Spinner size="sm" />
79
+ <span className="text-foreground-secondary">Loading content...</span>
80
+ </div>
81
+ <div className="flex items-center gap-4">
82
+ <DotsSpinner />
83
+ <span className="text-foreground-secondary">Processing...</span>
84
+ </div>
85
+ <div className="text-center p-8 border border-border rounded">
86
+ <Spinner size="lg" label="Loading data" />
87
+ </div>
88
+ </div>
89
+ ),
90
+ };
91
+
92
+ export const ButtonLoading: Story = {
93
+ render: () => (
94
+ <button className="inline-flex items-center gap-2 bg-primary text-primary-foreground px-6 py-3">
95
+ <Spinner size="sm" />
96
+ <span>Processing...</span>
97
+ </button>
98
+ ),
99
+ };
100
+
101
+ // Responsive Matrix - Mobile, Tablet, Desktop
102
+ export const ResponsiveMatrix: Story = {
103
+ render: () => (
104
+ <div className="space-y-8">
105
+ {/* Mobile */}
106
+ <div>
107
+ <h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
108
+ <div className="w-[375px] border border-dashed border-border p-4">
109
+ <div className="text-center p-8 border border-border rounded">
110
+ <Spinner size="md" label="Loading..." />
111
+ </div>
112
+ </div>
113
+ </div>
114
+ {/* Tablet */}
115
+ <div>
116
+ <h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
117
+ <div className="w-[768px] border border-dashed border-border p-4">
118
+ <div className="flex justify-center items-center gap-8">
119
+ <Spinner size="sm" label="Small" />
120
+ <Spinner size="md" label="Medium" />
121
+ <DotsSpinner size="md" />
122
+ </div>
123
+ </div>
124
+ </div>
125
+ {/* Desktop */}
126
+ <div>
127
+ <h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
128
+ <div className="w-[1280px] border border-dashed border-border p-4">
129
+ <div className="flex justify-between items-center">
130
+ <div className="flex items-center gap-4">
131
+ <Spinner size="sm" />
132
+ <span className="text-foreground-secondary">Loading content...</span>
133
+ </div>
134
+ <div className="flex items-center gap-8">
135
+ <Spinner size="sm" />
136
+ <Spinner size="md" />
137
+ <Spinner size="lg" />
138
+ <Spinner size="xl" />
139
+ </div>
140
+ <div className="flex items-center gap-4">
141
+ <DotsSpinner />
142
+ <span className="text-foreground-secondary">Processing...</span>
143
+ </div>
144
+ </div>
145
+ </div>
146
+ </div>
147
+ </div>
148
+ ),
149
+ };
@@ -0,0 +1,170 @@
1
+ import type { Meta, StoryObj } from '@storybook/nextjs-vite';
2
+ import { Switch } from './switch';
3
+
4
+ const meta: Meta<typeof Switch> = {
5
+ title: 'Components/Forms/Switch',
6
+ component: Switch,
7
+ tags: ['autodocs'],
8
+ parameters: {
9
+ docs: {
10
+ description: {
11
+ component: 'A toggle switch for binary settings. Ideal for on/off states.',
12
+ },
13
+ },
14
+ },
15
+ argTypes: {
16
+ label: {
17
+ control: 'text',
18
+ description: 'Label text',
19
+ },
20
+ description: {
21
+ control: 'text',
22
+ description: 'Description text below the label',
23
+ },
24
+ checked: {
25
+ control: 'boolean',
26
+ description: 'Checked state',
27
+ },
28
+ disabled: {
29
+ control: 'boolean',
30
+ description: 'Disabled state',
31
+ },
32
+ },
33
+ };
34
+
35
+ export default meta;
36
+ type Story = StoryObj<typeof meta>;
37
+
38
+ // Default
39
+ export const Default: Story = {
40
+ args: {},
41
+ };
42
+
43
+ // With Label
44
+ export const WithLabel: Story = {
45
+ args: {
46
+ label: 'Enable notifications',
47
+ },
48
+ };
49
+
50
+ // With Description
51
+ export const WithDescription: Story = {
52
+ args: {
53
+ label: 'Dark mode',
54
+ description: 'Switch between light and dark themes',
55
+ },
56
+ };
57
+
58
+ // Checked
59
+ export const Checked: Story = {
60
+ args: {
61
+ label: 'Enabled',
62
+ defaultChecked: true,
63
+ },
64
+ };
65
+
66
+ // Disabled
67
+ export const Disabled: Story = {
68
+ args: {
69
+ label: 'Disabled switch',
70
+ disabled: true,
71
+ },
72
+ };
73
+
74
+ // Disabled Checked
75
+ export const DisabledChecked: Story = {
76
+ args: {
77
+ label: 'Disabled enabled',
78
+ disabled: true,
79
+ defaultChecked: true,
80
+ },
81
+ };
82
+
83
+ // All States
84
+ export const AllStates: Story = {
85
+ render: () => (
86
+ <div className="space-y-4">
87
+ <Switch label="Off" />
88
+ <Switch label="On" defaultChecked />
89
+ <Switch label="With description" description="Additional context for this setting" />
90
+ <Switch label="Disabled" disabled />
91
+ <Switch label="Disabled on" disabled defaultChecked />
92
+ </div>
93
+ ),
94
+ };
95
+
96
+ // Settings Example
97
+ export const SettingsExample: Story = {
98
+ render: () => (
99
+ <div className="space-y-6 w-96">
100
+ <h3 className="text-lg font-medium text-foreground">Settings</h3>
101
+ <div className="space-y-4 divide-y divide-divider">
102
+ <Switch
103
+ label="Push notifications"
104
+ description="Receive push notifications on your device"
105
+ defaultChecked
106
+ />
107
+ <div className="pt-4">
108
+ <Switch
109
+ label="Email digest"
110
+ description="Weekly summary of your activity"
111
+ />
112
+ </div>
113
+ <div className="pt-4">
114
+ <Switch
115
+ label="Auto-update"
116
+ description="Automatically install updates"
117
+ defaultChecked
118
+ />
119
+ </div>
120
+ <div className="pt-4">
121
+ <Switch
122
+ label="Analytics"
123
+ description="Help improve the product with usage data"
124
+ />
125
+ </div>
126
+ </div>
127
+ </div>
128
+ ),
129
+ };
130
+
131
+ // Responsive Matrix - Mobile, Tablet, Desktop
132
+ export const ResponsiveMatrix: Story = {
133
+ render: () => (
134
+ <div className="space-y-8">
135
+ {/* Mobile */}
136
+ <div>
137
+ <h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
138
+ <div className="w-[375px] border border-dashed border-border p-4 space-y-4">
139
+ <Switch label="Dark mode" description="Enable dark theme" />
140
+ <Switch label="Notifications" defaultChecked />
141
+ <Switch label="Auto-save" defaultChecked />
142
+ </div>
143
+ </div>
144
+ {/* Tablet */}
145
+ <div>
146
+ <h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
147
+ <div className="w-[768px] border border-dashed border-border p-4">
148
+ <div className="grid grid-cols-2 gap-4">
149
+ <Switch label="Email notifications" description="Receive email updates" defaultChecked />
150
+ <Switch label="Push notifications" description="Mobile app alerts" />
151
+ <Switch label="Weekly digest" description="Summary of activity" defaultChecked />
152
+ <Switch label="Marketing emails" description="Product announcements" />
153
+ </div>
154
+ </div>
155
+ </div>
156
+ {/* Desktop */}
157
+ <div>
158
+ <h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
159
+ <div className="w-[1280px] border border-dashed border-border p-4">
160
+ <div className="grid grid-cols-4 gap-4">
161
+ <Switch label="Off" />
162
+ <Switch label="On" defaultChecked />
163
+ <Switch label="Disabled" disabled />
164
+ <Switch label="Disabled on" disabled defaultChecked />
165
+ </div>
166
+ </div>
167
+ </div>
168
+ </div>
169
+ ),
170
+ };