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.
- package/dist/assets/BRAND_GUIDELINES.md +0 -8
- package/dist/assets/components/accordion.stories.tsx +310 -0
- package/dist/assets/components/accordion.tsx +56 -30
- package/dist/assets/components/alert.stories.tsx +199 -0
- package/dist/assets/components/autocomplete.stories.tsx +307 -0
- package/dist/assets/components/autocomplete.tsx +28 -4
- package/dist/assets/components/avatar.stories.tsx +175 -0
- package/dist/assets/components/badge.stories.tsx +258 -0
- package/dist/assets/components/breadcrumbs.stories.tsx +175 -0
- package/dist/assets/components/button.stories.tsx +362 -0
- package/dist/assets/components/button.tsx +48 -3
- package/dist/assets/components/calendar.stories.tsx +247 -0
- package/dist/assets/components/card.stories.tsx +275 -0
- package/dist/assets/components/card.tsx +26 -1
- package/dist/assets/components/checkbox-group.stories.tsx +281 -0
- package/dist/assets/components/checkbox.stories.tsx +160 -0
- package/dist/assets/components/checkbox.tsx +32 -4
- package/dist/assets/components/code.stories.tsx +265 -0
- package/dist/assets/components/date-input.stories.tsx +278 -0
- package/dist/assets/components/date-input.tsx +24 -2
- package/dist/assets/components/date-picker.stories.tsx +337 -0
- package/dist/assets/components/date-picker.tsx +28 -4
- package/dist/assets/components/date-range-picker.stories.tsx +340 -0
- package/dist/assets/components/dialog.stories.tsx +285 -0
- package/dist/assets/components/divider.stories.tsx +176 -0
- package/dist/assets/components/drawer.stories.tsx +216 -0
- package/dist/assets/components/dropdown.stories.tsx +342 -0
- package/dist/assets/components/dropdown.tsx +55 -10
- package/dist/assets/components/form.stories.tsx +372 -0
- package/dist/assets/components/image.stories.tsx +348 -0
- package/dist/assets/components/input-otp.stories.tsx +336 -0
- package/dist/assets/components/input-otp.tsx +24 -2
- package/dist/assets/components/input.stories.tsx +223 -0
- package/dist/assets/components/input.tsx +27 -2
- package/dist/assets/components/kbd.stories.tsx +272 -0
- package/dist/assets/components/link.stories.tsx +199 -0
- package/dist/assets/components/link.tsx +50 -1
- package/dist/assets/components/listbox.stories.tsx +287 -0
- package/dist/assets/components/listbox.tsx +30 -7
- package/dist/assets/components/navbar.stories.tsx +218 -0
- package/dist/assets/components/number-input.stories.tsx +295 -0
- package/dist/assets/components/number-input.tsx +30 -8
- package/dist/assets/components/pagination.stories.tsx +280 -0
- package/dist/assets/components/pagination.tsx +45 -21
- package/dist/assets/components/popover.stories.tsx +219 -0
- package/dist/assets/components/progress.stories.tsx +153 -0
- package/dist/assets/components/radio-group.stories.tsx +187 -0
- package/dist/assets/components/radio-group.tsx +30 -6
- package/dist/assets/components/range-calendar.stories.tsx +334 -0
- package/dist/assets/components/scroll-shadow.stories.tsx +335 -0
- package/dist/assets/components/select.stories.tsx +192 -0
- package/dist/assets/components/select.tsx +54 -7
- package/dist/assets/components/skeleton.stories.tsx +166 -0
- package/dist/assets/components/slider.stories.tsx +145 -0
- package/dist/assets/components/slider.tsx +43 -8
- package/dist/assets/components/spacer.stories.tsx +216 -0
- package/dist/assets/components/spinner.stories.tsx +149 -0
- package/dist/assets/components/switch.stories.tsx +170 -0
- package/dist/assets/components/switch.tsx +29 -4
- package/dist/assets/components/table.stories.tsx +322 -0
- package/dist/assets/components/tabs.stories.tsx +306 -0
- package/dist/assets/components/tabs.tsx +25 -4
- package/dist/assets/components/textarea.stories.tsx +103 -0
- package/dist/assets/components/textarea.tsx +27 -3
- package/dist/assets/components/theme-toggle.stories.tsx +248 -0
- package/dist/assets/components/time-input.stories.tsx +365 -0
- package/dist/assets/components/time-input.tsx +25 -3
- package/dist/assets/components/toast.stories.tsx +195 -0
- package/dist/assets/components/tooltip.stories.tsx +226 -0
- package/dist/assets/components/user.stories.tsx +274 -0
- package/dist/assets/logo-manifest.json +0 -18
- package/dist/index.js +2142 -85
- 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 ||
|
|
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
|
-
[
|
|
87
|
+
[isDisabled, min, max, step, onValueChange]
|
|
55
88
|
);
|
|
56
89
|
|
|
57
90
|
const handleMouseDown = (e: React.MouseEvent) => {
|
|
58
|
-
if (
|
|
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 (
|
|
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={
|
|
124
|
-
tabIndex={
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
};
|