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
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
|
|
2
|
+
import { Select, NativeSelect, type SelectState } from './select';
|
|
3
|
+
|
|
4
|
+
const defaultOptions = [
|
|
5
|
+
{ value: 'option1', label: 'Option 1' },
|
|
6
|
+
{ value: 'option2', label: 'Option 2' },
|
|
7
|
+
{ value: 'option3', label: 'Option 3' },
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
const countryOptions = [
|
|
11
|
+
{ value: 'us', label: 'United States' },
|
|
12
|
+
{ value: 'ca', label: 'Canada' },
|
|
13
|
+
{ value: 'uk', label: 'United Kingdom' },
|
|
14
|
+
{ value: 'de', label: 'Germany' },
|
|
15
|
+
{ value: 'fr', label: 'France' },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
const meta: Meta<typeof Select> = {
|
|
19
|
+
title: 'Components/Forms/Select',
|
|
20
|
+
component: Select,
|
|
21
|
+
tags: ['autodocs'],
|
|
22
|
+
parameters: {
|
|
23
|
+
docs: {
|
|
24
|
+
description: {
|
|
25
|
+
component: 'A dropdown select component for choosing from a list of options.',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
argTypes: {
|
|
30
|
+
placeholder: { control: 'text' },
|
|
31
|
+
label: { control: 'text' },
|
|
32
|
+
error: { control: 'text' },
|
|
33
|
+
disabled: { control: 'boolean' },
|
|
34
|
+
state: {
|
|
35
|
+
control: 'select',
|
|
36
|
+
options: ['default', 'hover', 'focus', 'open', 'error', 'disabled'],
|
|
37
|
+
description: 'Visual state for documentation',
|
|
38
|
+
table: {
|
|
39
|
+
defaultValue: { summary: 'default' },
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export default meta;
|
|
46
|
+
type Story = StoryObj<typeof meta>;
|
|
47
|
+
|
|
48
|
+
export const Default: Story = {
|
|
49
|
+
args: {
|
|
50
|
+
options: defaultOptions,
|
|
51
|
+
placeholder: 'Select an option',
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const WithLabel: Story = {
|
|
56
|
+
args: {
|
|
57
|
+
options: countryOptions,
|
|
58
|
+
label: 'Country',
|
|
59
|
+
placeholder: 'Select a country',
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const WithDefaultValue: Story = {
|
|
64
|
+
args: {
|
|
65
|
+
options: countryOptions,
|
|
66
|
+
label: 'Country',
|
|
67
|
+
defaultValue: 'us',
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const WithError: Story = {
|
|
72
|
+
args: {
|
|
73
|
+
options: countryOptions,
|
|
74
|
+
label: 'Country',
|
|
75
|
+
placeholder: 'Select a country',
|
|
76
|
+
error: 'Please select a country',
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const Disabled: Story = {
|
|
81
|
+
args: {
|
|
82
|
+
options: countryOptions,
|
|
83
|
+
label: 'Country',
|
|
84
|
+
defaultValue: 'us',
|
|
85
|
+
disabled: true,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const WithDisabledOption: Story = {
|
|
90
|
+
args: {
|
|
91
|
+
options: [
|
|
92
|
+
{ value: 'available', label: 'Available' },
|
|
93
|
+
{ value: 'limited', label: 'Limited Stock' },
|
|
94
|
+
{ value: 'outofstock', label: 'Out of Stock', disabled: true },
|
|
95
|
+
],
|
|
96
|
+
label: 'Availability',
|
|
97
|
+
placeholder: 'Select availability',
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const AllStates: Story = {
|
|
102
|
+
render: () => (
|
|
103
|
+
<div className="space-y-6 w-72">
|
|
104
|
+
<Select options={defaultOptions} placeholder="Default select" />
|
|
105
|
+
<Select options={countryOptions} label="With Label" placeholder="Select..." />
|
|
106
|
+
<Select options={countryOptions} label="With Value" defaultValue="uk" />
|
|
107
|
+
<Select options={countryOptions} label="With Error" error="Required field" />
|
|
108
|
+
<Select options={countryOptions} label="Disabled" defaultValue="us" disabled />
|
|
109
|
+
</div>
|
|
110
|
+
),
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// Native Select stories
|
|
114
|
+
export const NativeSelectDefault: StoryObj<typeof NativeSelect> = {
|
|
115
|
+
render: () => (
|
|
116
|
+
<NativeSelect options={countryOptions} label="Native Select" />
|
|
117
|
+
),
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export const NativeSelectWithError: StoryObj<typeof NativeSelect> = {
|
|
121
|
+
render: () => (
|
|
122
|
+
<NativeSelect
|
|
123
|
+
options={countryOptions}
|
|
124
|
+
label="Native Select"
|
|
125
|
+
error="Please select an option"
|
|
126
|
+
/>
|
|
127
|
+
),
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// State Matrix - Visual documentation of all states
|
|
131
|
+
export const StateMatrix: Story = {
|
|
132
|
+
render: () => {
|
|
133
|
+
const states: SelectState[] = ['default', 'hover', 'focus', 'open', 'error', 'disabled'];
|
|
134
|
+
return (
|
|
135
|
+
<div className="space-y-6 w-80">
|
|
136
|
+
<h3 className="text-sm font-medium text-foreground-muted">Select States</h3>
|
|
137
|
+
<div className="grid grid-cols-1 gap-4">
|
|
138
|
+
{states.map((state) => (
|
|
139
|
+
<div key={state} className="flex items-center gap-4">
|
|
140
|
+
<span className="text-xs font-medium text-foreground-muted uppercase w-20">{state}</span>
|
|
141
|
+
<Select
|
|
142
|
+
state={state}
|
|
143
|
+
options={defaultOptions}
|
|
144
|
+
placeholder="Select option"
|
|
145
|
+
label={state === 'error' ? undefined : undefined}
|
|
146
|
+
error={state === 'error' ? 'Error message' : undefined}
|
|
147
|
+
/>
|
|
148
|
+
</div>
|
|
149
|
+
))}
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
);
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Responsive Matrix - Mobile, Tablet, Desktop
|
|
157
|
+
export const ResponsiveMatrix: Story = {
|
|
158
|
+
render: () => (
|
|
159
|
+
<div className="space-y-8">
|
|
160
|
+
{/* Mobile */}
|
|
161
|
+
<div>
|
|
162
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
|
|
163
|
+
<div className="w-[375px] border border-dashed border-border p-4 space-y-4">
|
|
164
|
+
<Select options={countryOptions} label="Country" placeholder="Select..." />
|
|
165
|
+
<Select options={defaultOptions} label="Option" placeholder="Choose..." />
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
{/* Tablet */}
|
|
169
|
+
<div>
|
|
170
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
|
|
171
|
+
<div className="w-[768px] border border-dashed border-border p-4">
|
|
172
|
+
<div className="grid grid-cols-2 gap-4">
|
|
173
|
+
<Select options={countryOptions} label="Country" placeholder="Select country..." />
|
|
174
|
+
<Select options={defaultOptions} label="Category" placeholder="Select category..." />
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
{/* Desktop */}
|
|
179
|
+
<div>
|
|
180
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
|
|
181
|
+
<div className="w-[1280px] border border-dashed border-border p-4">
|
|
182
|
+
<div className="grid grid-cols-4 gap-4">
|
|
183
|
+
<Select options={countryOptions} label="Country" defaultValue="us" />
|
|
184
|
+
<Select options={defaultOptions} label="Default" placeholder="Select..." />
|
|
185
|
+
<Select options={countryOptions} label="With Error" error="Required" />
|
|
186
|
+
<Select options={countryOptions} label="Disabled" defaultValue="uk" disabled />
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
),
|
|
192
|
+
};
|
|
@@ -4,6 +4,23 @@ import { forwardRef, useState, useRef, useEffect } from "react";
|
|
|
4
4
|
import { ChevronDown, Check } from "lucide-react";
|
|
5
5
|
import { cn } from "@/lib/utils";
|
|
6
6
|
|
|
7
|
+
export type SelectState = "default" | "hover" | "focus" | "open" | "error" | "disabled";
|
|
8
|
+
|
|
9
|
+
// State styles for Storybook/Figma visualization
|
|
10
|
+
const getStateStyles = (state?: SelectState) => {
|
|
11
|
+
if (!state || state === "default") return "";
|
|
12
|
+
|
|
13
|
+
const stateMap: Record<string, string> = {
|
|
14
|
+
hover: "border-border-hover",
|
|
15
|
+
focus: "border-input-focus",
|
|
16
|
+
open: "border-input-focus",
|
|
17
|
+
error: "border-error",
|
|
18
|
+
disabled: "opacity-50 cursor-not-allowed",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
return stateMap[state] || "";
|
|
22
|
+
};
|
|
23
|
+
|
|
7
24
|
interface SelectOption {
|
|
8
25
|
value: string;
|
|
9
26
|
label: string;
|
|
@@ -20,6 +37,8 @@ interface SelectProps {
|
|
|
20
37
|
error?: string;
|
|
21
38
|
disabled?: boolean;
|
|
22
39
|
className?: string;
|
|
40
|
+
/** Visual state for Storybook/Figma documentation */
|
|
41
|
+
state?: SelectState;
|
|
23
42
|
}
|
|
24
43
|
|
|
25
44
|
export function Select({
|
|
@@ -32,7 +51,11 @@ export function Select({
|
|
|
32
51
|
error,
|
|
33
52
|
disabled = false,
|
|
34
53
|
className,
|
|
54
|
+
state,
|
|
35
55
|
}: SelectProps) {
|
|
56
|
+
const isDisabled = disabled || state === "disabled";
|
|
57
|
+
const hasError = error || state === "error";
|
|
58
|
+
const isOpenState = state === "open";
|
|
36
59
|
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
37
60
|
const [isOpen, setIsOpen] = useState(false);
|
|
38
61
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
@@ -80,15 +103,16 @@ export function Select({
|
|
|
80
103
|
<div className="relative">
|
|
81
104
|
<button
|
|
82
105
|
type="button"
|
|
83
|
-
onClick={() => !
|
|
84
|
-
disabled={
|
|
106
|
+
onClick={() => !isDisabled && setIsOpen(!isOpen)}
|
|
107
|
+
disabled={isDisabled}
|
|
85
108
|
className={cn(
|
|
86
109
|
"flex w-full items-center justify-between border border-input-border bg-input px-4 py-3",
|
|
87
110
|
"text-left text-foreground transition-colors duration-200",
|
|
88
111
|
"focus:border-input-focus focus:outline-none",
|
|
89
112
|
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
90
|
-
|
|
91
|
-
isOpen && "border-input-focus"
|
|
113
|
+
hasError && "border-error",
|
|
114
|
+
(isOpen || isOpenState) && "border-input-focus",
|
|
115
|
+
getStateStyles(state)
|
|
92
116
|
)}
|
|
93
117
|
>
|
|
94
118
|
<span className={cn(!selectedOption && "text-input-placeholder")}>
|
|
@@ -97,7 +121,7 @@ export function Select({
|
|
|
97
121
|
<ChevronDown
|
|
98
122
|
className={cn(
|
|
99
123
|
"h-4 w-4 text-foreground-muted transition-transform duration-200",
|
|
100
|
-
isOpen && "rotate-180"
|
|
124
|
+
(isOpen || isOpenState) && "rotate-180"
|
|
101
125
|
)}
|
|
102
126
|
/>
|
|
103
127
|
</button>
|
|
@@ -136,15 +160,36 @@ export function Select({
|
|
|
136
160
|
);
|
|
137
161
|
}
|
|
138
162
|
|
|
163
|
+
export type NativeSelectState = "default" | "hover" | "focus" | "error" | "disabled";
|
|
164
|
+
|
|
165
|
+
// State styles for NativeSelect Storybook/Figma visualization
|
|
166
|
+
const getNativeSelectStateStyles = (state?: NativeSelectState) => {
|
|
167
|
+
if (!state || state === "default") return "";
|
|
168
|
+
|
|
169
|
+
const stateMap: Record<string, string> = {
|
|
170
|
+
hover: "border-border-hover",
|
|
171
|
+
focus: "border-input-focus",
|
|
172
|
+
error: "border-error",
|
|
173
|
+
disabled: "opacity-50 cursor-not-allowed",
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
return stateMap[state] || "";
|
|
177
|
+
};
|
|
178
|
+
|
|
139
179
|
// Native Select for forms
|
|
140
180
|
interface NativeSelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> {
|
|
141
181
|
label?: string;
|
|
142
182
|
error?: string;
|
|
143
183
|
options: SelectOption[];
|
|
184
|
+
/** Visual state for Storybook/Figma documentation */
|
|
185
|
+
state?: NativeSelectState;
|
|
144
186
|
}
|
|
145
187
|
|
|
146
188
|
export const NativeSelect = forwardRef<HTMLSelectElement, NativeSelectProps>(
|
|
147
|
-
({ className, label, error, options, ...props }, ref) => {
|
|
189
|
+
({ className, label, error, options, state, disabled, ...props }, ref) => {
|
|
190
|
+
const isDisabled = disabled || state === "disabled";
|
|
191
|
+
const hasError = error || state === "error";
|
|
192
|
+
|
|
148
193
|
return (
|
|
149
194
|
<div className="w-full">
|
|
150
195
|
{label && (
|
|
@@ -155,12 +200,14 @@ export const NativeSelect = forwardRef<HTMLSelectElement, NativeSelectProps>(
|
|
|
155
200
|
<div className="relative">
|
|
156
201
|
<select
|
|
157
202
|
ref={ref}
|
|
203
|
+
disabled={isDisabled}
|
|
158
204
|
className={cn(
|
|
159
205
|
"w-full appearance-none border border-input-border bg-input px-4 py-3 pr-10",
|
|
160
206
|
"text-foreground transition-colors duration-200",
|
|
161
207
|
"focus:border-input-focus focus:outline-none",
|
|
162
208
|
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
163
|
-
|
|
209
|
+
hasError && "border-error",
|
|
210
|
+
getNativeSelectStateStyles(state),
|
|
164
211
|
className
|
|
165
212
|
)}
|
|
166
213
|
{...props}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
|
|
2
|
+
import { Skeleton, SkeletonText, SkeletonAvatar, SkeletonCard, SkeletonTable } from './skeleton';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Skeleton> = {
|
|
5
|
+
title: 'Components/Feedback/Skeleton',
|
|
6
|
+
component: Skeleton,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
parameters: {
|
|
9
|
+
docs: {
|
|
10
|
+
description: {
|
|
11
|
+
component: 'Skeleton loading placeholders for content that is loading.',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
argTypes: {
|
|
16
|
+
variant: {
|
|
17
|
+
control: 'select',
|
|
18
|
+
options: ['text', 'circular', 'rectangular'],
|
|
19
|
+
},
|
|
20
|
+
width: { control: 'text' },
|
|
21
|
+
height: { control: 'text' },
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default meta;
|
|
26
|
+
type Story = StoryObj<typeof meta>;
|
|
27
|
+
|
|
28
|
+
export const Default: Story = {
|
|
29
|
+
args: {
|
|
30
|
+
className: 'h-4 w-full',
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const Text: Story = {
|
|
35
|
+
args: {
|
|
36
|
+
variant: 'text',
|
|
37
|
+
width: '200px',
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const Circular: Story = {
|
|
42
|
+
args: {
|
|
43
|
+
variant: 'circular',
|
|
44
|
+
width: 48,
|
|
45
|
+
height: 48,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const Rectangular: Story = {
|
|
50
|
+
args: {
|
|
51
|
+
variant: 'rectangular',
|
|
52
|
+
width: '100%',
|
|
53
|
+
height: 200,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const TextLines: StoryObj<typeof SkeletonText> = {
|
|
58
|
+
render: () => (
|
|
59
|
+
<div className="w-80">
|
|
60
|
+
<SkeletonText lines={3} />
|
|
61
|
+
</div>
|
|
62
|
+
),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const AvatarSkeleton: StoryObj<typeof SkeletonAvatar> = {
|
|
66
|
+
render: () => (
|
|
67
|
+
<div className="flex items-center gap-4">
|
|
68
|
+
<SkeletonAvatar size="sm" />
|
|
69
|
+
<SkeletonAvatar size="md" />
|
|
70
|
+
<SkeletonAvatar size="lg" />
|
|
71
|
+
</div>
|
|
72
|
+
),
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const CardSkeleton: StoryObj<typeof SkeletonCard> = {
|
|
76
|
+
render: () => (
|
|
77
|
+
<div className="w-72">
|
|
78
|
+
<SkeletonCard />
|
|
79
|
+
</div>
|
|
80
|
+
),
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const TableSkeleton: StoryObj<typeof SkeletonTable> = {
|
|
84
|
+
render: () => (
|
|
85
|
+
<div className="w-full">
|
|
86
|
+
<SkeletonTable rows={5} columns={4} />
|
|
87
|
+
</div>
|
|
88
|
+
),
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const ProfileSkeleton: Story = {
|
|
92
|
+
render: () => (
|
|
93
|
+
<div className="flex items-center gap-4 w-80">
|
|
94
|
+
<SkeletonAvatar size="lg" />
|
|
95
|
+
<div className="flex-1 space-y-2">
|
|
96
|
+
<Skeleton className="h-4 w-3/4" />
|
|
97
|
+
<Skeleton className="h-3 w-1/2" />
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
),
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export const ListSkeleton: Story = {
|
|
104
|
+
render: () => (
|
|
105
|
+
<div className="space-y-4 w-96">
|
|
106
|
+
{[1, 2, 3].map((i) => (
|
|
107
|
+
<div key={i} className="flex items-center gap-4">
|
|
108
|
+
<SkeletonAvatar size="md" />
|
|
109
|
+
<div className="flex-1 space-y-2">
|
|
110
|
+
<Skeleton className="h-4 w-full" />
|
|
111
|
+
<Skeleton className="h-3 w-2/3" />
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
))}
|
|
115
|
+
</div>
|
|
116
|
+
),
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export const GridSkeleton: Story = {
|
|
120
|
+
render: () => (
|
|
121
|
+
<div className="grid grid-cols-3 gap-4">
|
|
122
|
+
{[1, 2, 3].map((i) => (
|
|
123
|
+
<SkeletonCard key={i} />
|
|
124
|
+
))}
|
|
125
|
+
</div>
|
|
126
|
+
),
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Responsive Matrix - Mobile, Tablet, Desktop
|
|
130
|
+
export const ResponsiveMatrix: Story = {
|
|
131
|
+
render: () => (
|
|
132
|
+
<div className="space-y-8">
|
|
133
|
+
{/* Mobile */}
|
|
134
|
+
<div>
|
|
135
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
|
|
136
|
+
<div className="w-[375px] border border-dashed border-border p-4 space-y-4">
|
|
137
|
+
<div className="flex items-center gap-4">
|
|
138
|
+
<SkeletonAvatar size="md" />
|
|
139
|
+
<div className="flex-1 space-y-2">
|
|
140
|
+
<Skeleton className="h-4 w-3/4" />
|
|
141
|
+
<Skeleton className="h-3 w-1/2" />
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
<SkeletonCard />
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
{/* Tablet */}
|
|
148
|
+
<div>
|
|
149
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
|
|
150
|
+
<div className="w-[768px] border border-dashed border-border p-4">
|
|
151
|
+
<div className="grid grid-cols-2 gap-4">
|
|
152
|
+
<SkeletonCard />
|
|
153
|
+
<SkeletonCard />
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
{/* Desktop */}
|
|
158
|
+
<div>
|
|
159
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
|
|
160
|
+
<div className="w-[1280px] border border-dashed border-border p-4">
|
|
161
|
+
<SkeletonTable rows={4} columns={5} />
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
),
|
|
166
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
|
|
2
|
+
import { Slider, type SliderState } from './slider';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Slider> = {
|
|
5
|
+
title: 'Components/Forms/Slider',
|
|
6
|
+
component: Slider,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
parameters: {
|
|
9
|
+
docs: {
|
|
10
|
+
description: {
|
|
11
|
+
component: 'A slider input for selecting numeric values within a range.',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
argTypes: {
|
|
16
|
+
min: { control: 'number' },
|
|
17
|
+
max: { control: 'number' },
|
|
18
|
+
step: { control: 'number' },
|
|
19
|
+
defaultValue: { control: 'number' },
|
|
20
|
+
label: { control: 'text' },
|
|
21
|
+
showValue: { control: 'boolean' },
|
|
22
|
+
disabled: { control: 'boolean' },
|
|
23
|
+
state: {
|
|
24
|
+
control: 'select',
|
|
25
|
+
options: ['default', 'hover', 'focus', 'active', 'disabled'],
|
|
26
|
+
description: 'Visual state for documentation',
|
|
27
|
+
table: {
|
|
28
|
+
defaultValue: { summary: 'default' },
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default meta;
|
|
35
|
+
type Story = StoryObj<typeof meta>;
|
|
36
|
+
|
|
37
|
+
export const Default: Story = {
|
|
38
|
+
args: {
|
|
39
|
+
defaultValue: 50,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const WithLabel: Story = {
|
|
44
|
+
args: {
|
|
45
|
+
label: 'Volume',
|
|
46
|
+
defaultValue: 75,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const WithValue: Story = {
|
|
51
|
+
args: {
|
|
52
|
+
label: 'Brightness',
|
|
53
|
+
showValue: true,
|
|
54
|
+
defaultValue: 60,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const CustomRange: Story = {
|
|
59
|
+
args: {
|
|
60
|
+
label: 'Price Range',
|
|
61
|
+
min: 0,
|
|
62
|
+
max: 1000,
|
|
63
|
+
step: 50,
|
|
64
|
+
defaultValue: 500,
|
|
65
|
+
showValue: true,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const Disabled: Story = {
|
|
70
|
+
args: {
|
|
71
|
+
label: 'Disabled Slider',
|
|
72
|
+
defaultValue: 30,
|
|
73
|
+
disabled: true,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const AllStates: Story = {
|
|
78
|
+
render: () => (
|
|
79
|
+
<div className="space-y-8 w-80">
|
|
80
|
+
<Slider defaultValue={50} />
|
|
81
|
+
<Slider label="With Label" defaultValue={75} />
|
|
82
|
+
<Slider label="With Value Display" showValue defaultValue={60} />
|
|
83
|
+
<Slider label="Custom Step (10)" step={10} showValue defaultValue={40} />
|
|
84
|
+
<Slider label="Disabled" disabled defaultValue={30} />
|
|
85
|
+
</div>
|
|
86
|
+
),
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// State Matrix - Visual documentation of all states
|
|
90
|
+
export const StateMatrix: Story = {
|
|
91
|
+
render: () => {
|
|
92
|
+
const states: SliderState[] = ['default', 'hover', 'focus', 'active', 'disabled'];
|
|
93
|
+
return (
|
|
94
|
+
<div className="space-y-6 w-80">
|
|
95
|
+
<h3 className="text-sm font-medium text-foreground-muted">Slider States</h3>
|
|
96
|
+
<div className="space-y-6">
|
|
97
|
+
{states.map((state) => (
|
|
98
|
+
<div key={state} className="flex items-center gap-4">
|
|
99
|
+
<span className="text-xs font-medium text-foreground-muted uppercase w-20">{state}</span>
|
|
100
|
+
<Slider state={state} defaultValue={50} label={state} />
|
|
101
|
+
</div>
|
|
102
|
+
))}
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
);
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Responsive Matrix - Mobile, Tablet, Desktop
|
|
110
|
+
export const ResponsiveMatrix: Story = {
|
|
111
|
+
render: () => (
|
|
112
|
+
<div className="space-y-8">
|
|
113
|
+
{/* Mobile */}
|
|
114
|
+
<div>
|
|
115
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
|
|
116
|
+
<div className="w-[375px] border border-dashed border-border p-4 space-y-6">
|
|
117
|
+
<Slider label="Volume" showValue defaultValue={75} />
|
|
118
|
+
<Slider label="Brightness" showValue defaultValue={50} />
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
{/* Tablet */}
|
|
122
|
+
<div>
|
|
123
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
|
|
124
|
+
<div className="w-[768px] border border-dashed border-border p-4">
|
|
125
|
+
<div className="grid grid-cols-2 gap-8">
|
|
126
|
+
<Slider label="Bass" showValue defaultValue={60} />
|
|
127
|
+
<Slider label="Treble" showValue defaultValue={40} />
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
{/* Desktop */}
|
|
132
|
+
<div>
|
|
133
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
|
|
134
|
+
<div className="w-[1280px] border border-dashed border-border p-4">
|
|
135
|
+
<div className="grid grid-cols-4 gap-8">
|
|
136
|
+
<Slider label="Volume" showValue defaultValue={80} />
|
|
137
|
+
<Slider label="Balance" showValue defaultValue={50} min={-50} max={50} />
|
|
138
|
+
<Slider label="Fade" showValue defaultValue={0} min={-10} max={10} />
|
|
139
|
+
<Slider label="Disabled" disabled defaultValue={30} />
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
),
|
|
145
|
+
};
|