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,334 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { addDays, addMonths } from 'date-fns';
|
|
4
|
+
import { RangeCalendar } from './range-calendar';
|
|
5
|
+
|
|
6
|
+
interface DateRange {
|
|
7
|
+
start: Date | undefined;
|
|
8
|
+
end: Date | undefined;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const meta: Meta<typeof RangeCalendar> = {
|
|
12
|
+
title: 'Components/Forms/RangeCalendar',
|
|
13
|
+
component: RangeCalendar,
|
|
14
|
+
tags: ['autodocs'],
|
|
15
|
+
parameters: {
|
|
16
|
+
docs: {
|
|
17
|
+
description: {
|
|
18
|
+
component: 'A calendar component for selecting date ranges. Displays two months side-by-side by default. Supports hover preview, min/max dates, and disabled dates.',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
argTypes: {
|
|
23
|
+
numberOfMonths: {
|
|
24
|
+
control: { type: 'number', min: 1, max: 3 },
|
|
25
|
+
description: 'Number of months to display',
|
|
26
|
+
table: {
|
|
27
|
+
defaultValue: { summary: '2' },
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default meta;
|
|
34
|
+
type Story = StoryObj<typeof meta>;
|
|
35
|
+
|
|
36
|
+
// Default
|
|
37
|
+
export const Default: Story = {
|
|
38
|
+
render: () => {
|
|
39
|
+
const [range, setRange] = useState<DateRange>({ start: undefined, end: undefined });
|
|
40
|
+
return (
|
|
41
|
+
<div className="space-y-4">
|
|
42
|
+
<RangeCalendar
|
|
43
|
+
value={range}
|
|
44
|
+
onValueChange={setRange}
|
|
45
|
+
/>
|
|
46
|
+
<div className="text-sm text-foreground-muted">
|
|
47
|
+
{range.start && <span>Start: {range.start.toLocaleDateString()}</span>}
|
|
48
|
+
{range.start && range.end && <span> | </span>}
|
|
49
|
+
{range.end && <span>End: {range.end.toLocaleDateString()}</span>}
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// With Selected Range
|
|
57
|
+
export const WithSelectedRange: Story = {
|
|
58
|
+
render: () => {
|
|
59
|
+
const today = new Date();
|
|
60
|
+
const [range, setRange] = useState<DateRange>({
|
|
61
|
+
start: today,
|
|
62
|
+
end: addDays(today, 7),
|
|
63
|
+
});
|
|
64
|
+
return (
|
|
65
|
+
<RangeCalendar
|
|
66
|
+
value={range}
|
|
67
|
+
onValueChange={setRange}
|
|
68
|
+
/>
|
|
69
|
+
);
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Single Month
|
|
74
|
+
export const SingleMonth: Story = {
|
|
75
|
+
render: () => {
|
|
76
|
+
const [range, setRange] = useState<DateRange>({ start: undefined, end: undefined });
|
|
77
|
+
return (
|
|
78
|
+
<RangeCalendar
|
|
79
|
+
value={range}
|
|
80
|
+
onValueChange={setRange}
|
|
81
|
+
numberOfMonths={1}
|
|
82
|
+
/>
|
|
83
|
+
);
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// Three Months
|
|
88
|
+
export const ThreeMonths: Story = {
|
|
89
|
+
render: () => {
|
|
90
|
+
const [range, setRange] = useState<DateRange>({ start: undefined, end: undefined });
|
|
91
|
+
return (
|
|
92
|
+
<RangeCalendar
|
|
93
|
+
value={range}
|
|
94
|
+
onValueChange={setRange}
|
|
95
|
+
numberOfMonths={3}
|
|
96
|
+
/>
|
|
97
|
+
);
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// With Min/Max Dates
|
|
102
|
+
export const WithMinMaxDates: Story = {
|
|
103
|
+
render: () => {
|
|
104
|
+
const [range, setRange] = useState<DateRange>({ start: undefined, end: undefined });
|
|
105
|
+
const today = new Date();
|
|
106
|
+
return (
|
|
107
|
+
<div className="space-y-4">
|
|
108
|
+
<p className="text-sm text-foreground-muted">
|
|
109
|
+
Selection limited to next 60 days
|
|
110
|
+
</p>
|
|
111
|
+
<RangeCalendar
|
|
112
|
+
value={range}
|
|
113
|
+
onValueChange={setRange}
|
|
114
|
+
minDate={today}
|
|
115
|
+
maxDate={addDays(today, 60)}
|
|
116
|
+
/>
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// With Disabled Dates (Weekends)
|
|
123
|
+
export const DisabledWeekends: Story = {
|
|
124
|
+
render: () => {
|
|
125
|
+
const [range, setRange] = useState<DateRange>({ start: undefined, end: undefined });
|
|
126
|
+
const isWeekend = (date: Date) => {
|
|
127
|
+
const day = date.getDay();
|
|
128
|
+
return day === 0 || day === 6;
|
|
129
|
+
};
|
|
130
|
+
return (
|
|
131
|
+
<div className="space-y-4">
|
|
132
|
+
<p className="text-sm text-foreground-muted">
|
|
133
|
+
Weekends are disabled (business days only)
|
|
134
|
+
</p>
|
|
135
|
+
<RangeCalendar
|
|
136
|
+
value={range}
|
|
137
|
+
onValueChange={setRange}
|
|
138
|
+
disabled={isWeekend}
|
|
139
|
+
/>
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// Hotel Booking Example
|
|
146
|
+
export const HotelBookingExample: Story = {
|
|
147
|
+
render: () => {
|
|
148
|
+
const [range, setRange] = useState<DateRange>({ start: undefined, end: undefined });
|
|
149
|
+
const today = new Date();
|
|
150
|
+
|
|
151
|
+
// Simulate blocked dates (already booked)
|
|
152
|
+
const blockedRanges = [
|
|
153
|
+
{ start: addDays(today, 5), end: addDays(today, 8) },
|
|
154
|
+
{ start: addDays(today, 15), end: addDays(today, 18) },
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
const isBlocked = (date: Date) => {
|
|
158
|
+
return blockedRanges.some(
|
|
159
|
+
blocked =>
|
|
160
|
+
date >= blocked.start && date <= blocked.end
|
|
161
|
+
);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const nights = range.start && range.end
|
|
165
|
+
? Math.ceil((range.end.getTime() - range.start.getTime()) / (1000 * 60 * 60 * 24))
|
|
166
|
+
: 0;
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<div className="space-y-4">
|
|
170
|
+
<div className="flex items-center gap-4 text-sm">
|
|
171
|
+
<div className="flex items-center gap-2">
|
|
172
|
+
<div className="w-4 h-4 bg-primary rounded-sm" />
|
|
173
|
+
<span>Selected</span>
|
|
174
|
+
</div>
|
|
175
|
+
<div className="flex items-center gap-2">
|
|
176
|
+
<div className="w-4 h-4 bg-primary/20 rounded-sm" />
|
|
177
|
+
<span>In Range</span>
|
|
178
|
+
</div>
|
|
179
|
+
<div className="flex items-center gap-2">
|
|
180
|
+
<div className="w-4 h-4 bg-secondary text-foreground-subtle text-xs flex items-center justify-center">x</div>
|
|
181
|
+
<span>Unavailable</span>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
<RangeCalendar
|
|
185
|
+
value={range}
|
|
186
|
+
onValueChange={setRange}
|
|
187
|
+
minDate={today}
|
|
188
|
+
maxDate={addMonths(today, 6)}
|
|
189
|
+
disabled={isBlocked}
|
|
190
|
+
/>
|
|
191
|
+
{nights > 0 && (
|
|
192
|
+
<div className="p-4 border border-border rounded-sm">
|
|
193
|
+
<div className="flex justify-between">
|
|
194
|
+
<span className="text-foreground-muted">Duration</span>
|
|
195
|
+
<span className="font-medium">{nights} night{nights !== 1 ? 's' : ''}</span>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
)}
|
|
199
|
+
</div>
|
|
200
|
+
);
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// Report Date Range Example
|
|
205
|
+
export const ReportDateRangeExample: Story = {
|
|
206
|
+
render: () => {
|
|
207
|
+
const [range, setRange] = useState<DateRange>({ start: undefined, end: undefined });
|
|
208
|
+
const today = new Date();
|
|
209
|
+
|
|
210
|
+
const presets = [
|
|
211
|
+
{ label: 'Last 7 days', start: addDays(today, -7), end: today },
|
|
212
|
+
{ label: 'Last 30 days', start: addDays(today, -30), end: today },
|
|
213
|
+
{ label: 'This month', start: new Date(today.getFullYear(), today.getMonth(), 1), end: today },
|
|
214
|
+
{ label: 'Last month', start: new Date(today.getFullYear(), today.getMonth() - 1, 1), end: new Date(today.getFullYear(), today.getMonth(), 0) },
|
|
215
|
+
];
|
|
216
|
+
|
|
217
|
+
return (
|
|
218
|
+
<div className="space-y-4">
|
|
219
|
+
<div className="flex gap-2 flex-wrap">
|
|
220
|
+
{presets.map(preset => (
|
|
221
|
+
<button
|
|
222
|
+
key={preset.label}
|
|
223
|
+
onClick={() => setRange({ start: preset.start, end: preset.end })}
|
|
224
|
+
className="px-3 py-1 text-sm border border-border rounded-sm hover:bg-secondary-hover transition-colors"
|
|
225
|
+
>
|
|
226
|
+
{preset.label}
|
|
227
|
+
</button>
|
|
228
|
+
))}
|
|
229
|
+
<button
|
|
230
|
+
onClick={() => setRange({ start: undefined, end: undefined })}
|
|
231
|
+
className="px-3 py-1 text-sm border border-border rounded-sm hover:bg-secondary-hover transition-colors text-foreground-muted"
|
|
232
|
+
>
|
|
233
|
+
Clear
|
|
234
|
+
</button>
|
|
235
|
+
</div>
|
|
236
|
+
<RangeCalendar
|
|
237
|
+
value={range}
|
|
238
|
+
onValueChange={setRange}
|
|
239
|
+
maxDate={today}
|
|
240
|
+
/>
|
|
241
|
+
{range.start && range.end && (
|
|
242
|
+
<p className="text-sm">
|
|
243
|
+
Selected: {range.start.toLocaleDateString()} - {range.end.toLocaleDateString()}
|
|
244
|
+
</p>
|
|
245
|
+
)}
|
|
246
|
+
</div>
|
|
247
|
+
);
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// All Configurations
|
|
252
|
+
export const AllConfigurations: Story = {
|
|
253
|
+
render: () => {
|
|
254
|
+
const [range1, setRange1] = useState<DateRange>({ start: undefined, end: undefined });
|
|
255
|
+
const [range2, setRange2] = useState<DateRange>({ start: undefined, end: undefined });
|
|
256
|
+
const [range3, setRange3] = useState<DateRange>({ start: undefined, end: undefined });
|
|
257
|
+
|
|
258
|
+
return (
|
|
259
|
+
<div className="space-y-8">
|
|
260
|
+
<div>
|
|
261
|
+
<h4 className="text-sm font-medium mb-2">Single Month</h4>
|
|
262
|
+
<RangeCalendar
|
|
263
|
+
value={range1}
|
|
264
|
+
onValueChange={setRange1}
|
|
265
|
+
numberOfMonths={1}
|
|
266
|
+
/>
|
|
267
|
+
</div>
|
|
268
|
+
<div>
|
|
269
|
+
<h4 className="text-sm font-medium mb-2">Two Months (Default)</h4>
|
|
270
|
+
<RangeCalendar
|
|
271
|
+
value={range2}
|
|
272
|
+
onValueChange={setRange2}
|
|
273
|
+
numberOfMonths={2}
|
|
274
|
+
/>
|
|
275
|
+
</div>
|
|
276
|
+
<div>
|
|
277
|
+
<h4 className="text-sm font-medium mb-2">Three Months</h4>
|
|
278
|
+
<RangeCalendar
|
|
279
|
+
value={range3}
|
|
280
|
+
onValueChange={setRange3}
|
|
281
|
+
numberOfMonths={3}
|
|
282
|
+
/>
|
|
283
|
+
</div>
|
|
284
|
+
</div>
|
|
285
|
+
);
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// Responsive Matrix - Mobile, Tablet, Desktop
|
|
290
|
+
export const ResponsiveMatrix: Story = {
|
|
291
|
+
render: () => {
|
|
292
|
+
const [mobile, setMobile] = useState<DateRange>({ start: undefined, end: undefined });
|
|
293
|
+
const [tablet, setTablet] = useState<DateRange>({ start: undefined, end: undefined });
|
|
294
|
+
const [desktop, setDesktop] = useState<DateRange>({ start: undefined, end: undefined });
|
|
295
|
+
|
|
296
|
+
return (
|
|
297
|
+
<div className="space-y-8">
|
|
298
|
+
{/* Mobile */}
|
|
299
|
+
<div>
|
|
300
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
|
|
301
|
+
<div className="w-[375px] border border-dashed border-border p-4 overflow-x-auto">
|
|
302
|
+
<RangeCalendar
|
|
303
|
+
value={mobile}
|
|
304
|
+
onValueChange={setMobile}
|
|
305
|
+
numberOfMonths={1}
|
|
306
|
+
/>
|
|
307
|
+
</div>
|
|
308
|
+
</div>
|
|
309
|
+
{/* Tablet */}
|
|
310
|
+
<div>
|
|
311
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
|
|
312
|
+
<div className="w-[768px] border border-dashed border-border p-4">
|
|
313
|
+
<RangeCalendar
|
|
314
|
+
value={tablet}
|
|
315
|
+
onValueChange={setTablet}
|
|
316
|
+
numberOfMonths={2}
|
|
317
|
+
/>
|
|
318
|
+
</div>
|
|
319
|
+
</div>
|
|
320
|
+
{/* Desktop */}
|
|
321
|
+
<div>
|
|
322
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
|
|
323
|
+
<div className="w-[1280px] border border-dashed border-border p-4">
|
|
324
|
+
<RangeCalendar
|
|
325
|
+
value={desktop}
|
|
326
|
+
onValueChange={setDesktop}
|
|
327
|
+
numberOfMonths={3}
|
|
328
|
+
/>
|
|
329
|
+
</div>
|
|
330
|
+
</div>
|
|
331
|
+
</div>
|
|
332
|
+
);
|
|
333
|
+
},
|
|
334
|
+
};
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
|
|
2
|
+
import { ScrollShadow } from './scroll-shadow';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof ScrollShadow> = {
|
|
5
|
+
title: 'Components/Utilities/ScrollShadow',
|
|
6
|
+
component: ScrollShadow,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
parameters: {
|
|
9
|
+
docs: {
|
|
10
|
+
description: {
|
|
11
|
+
component: 'A container that shows gradient shadows at scroll boundaries to indicate more content. Useful for scroll containers with fixed heights.',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
argTypes: {
|
|
16
|
+
orientation: {
|
|
17
|
+
control: 'select',
|
|
18
|
+
options: ['vertical', 'horizontal', 'both'],
|
|
19
|
+
description: 'Scroll direction to show shadows for',
|
|
20
|
+
table: {
|
|
21
|
+
defaultValue: { summary: 'vertical' },
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
size: {
|
|
25
|
+
control: 'select',
|
|
26
|
+
options: ['sm', 'md', 'lg'],
|
|
27
|
+
description: 'Size of the shadow gradient',
|
|
28
|
+
table: {
|
|
29
|
+
defaultValue: { summary: 'md' },
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
hideScrollbar: {
|
|
33
|
+
control: 'boolean',
|
|
34
|
+
description: 'Hide the scrollbar',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default meta;
|
|
40
|
+
type Story = StoryObj<typeof meta>;
|
|
41
|
+
|
|
42
|
+
const sampleContent = Array.from({ length: 20 }, (_, i) => (
|
|
43
|
+
<div key={i} className="p-4 border-b border-border">
|
|
44
|
+
<h4 className="font-medium">Item {i + 1}</h4>
|
|
45
|
+
<p className="text-sm text-foreground-muted">
|
|
46
|
+
This is sample content for item {i + 1}. Scroll to see the shadow effect.
|
|
47
|
+
</p>
|
|
48
|
+
</div>
|
|
49
|
+
));
|
|
50
|
+
|
|
51
|
+
const horizontalItems = Array.from({ length: 15 }, (_, i) => (
|
|
52
|
+
<div
|
|
53
|
+
key={i}
|
|
54
|
+
className="flex-shrink-0 w-48 h-32 p-4 border border-border rounded-sm bg-card"
|
|
55
|
+
>
|
|
56
|
+
<h4 className="font-medium">Card {i + 1}</h4>
|
|
57
|
+
<p className="text-sm text-foreground-muted">Scroll horizontally</p>
|
|
58
|
+
</div>
|
|
59
|
+
));
|
|
60
|
+
|
|
61
|
+
// Vertical (Default)
|
|
62
|
+
export const Vertical: Story = {
|
|
63
|
+
render: () => (
|
|
64
|
+
<ScrollShadow
|
|
65
|
+
orientation="vertical"
|
|
66
|
+
className="h-64 w-80 border border-border rounded-sm"
|
|
67
|
+
>
|
|
68
|
+
{sampleContent}
|
|
69
|
+
</ScrollShadow>
|
|
70
|
+
),
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Horizontal
|
|
74
|
+
export const Horizontal: Story = {
|
|
75
|
+
render: () => (
|
|
76
|
+
<ScrollShadow
|
|
77
|
+
orientation="horizontal"
|
|
78
|
+
className="w-96 border border-border rounded-sm"
|
|
79
|
+
>
|
|
80
|
+
<div className="flex gap-4 p-4">
|
|
81
|
+
{horizontalItems}
|
|
82
|
+
</div>
|
|
83
|
+
</ScrollShadow>
|
|
84
|
+
),
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// Both Directions
|
|
88
|
+
export const Both: Story = {
|
|
89
|
+
render: () => (
|
|
90
|
+
<ScrollShadow
|
|
91
|
+
orientation="both"
|
|
92
|
+
className="h-64 w-80 border border-border rounded-sm"
|
|
93
|
+
>
|
|
94
|
+
<div className="w-[600px]">
|
|
95
|
+
{sampleContent}
|
|
96
|
+
<div className="p-4 text-sm text-foreground-muted">
|
|
97
|
+
This content extends beyond the container width to demonstrate horizontal scrolling.
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</ScrollShadow>
|
|
101
|
+
),
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// Small Shadow
|
|
105
|
+
export const SmallShadow: Story = {
|
|
106
|
+
render: () => (
|
|
107
|
+
<ScrollShadow
|
|
108
|
+
size="sm"
|
|
109
|
+
className="h-64 w-80 border border-border rounded-sm"
|
|
110
|
+
>
|
|
111
|
+
{sampleContent}
|
|
112
|
+
</ScrollShadow>
|
|
113
|
+
),
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// Medium Shadow (Default)
|
|
117
|
+
export const MediumShadow: Story = {
|
|
118
|
+
render: () => (
|
|
119
|
+
<ScrollShadow
|
|
120
|
+
size="md"
|
|
121
|
+
className="h-64 w-80 border border-border rounded-sm"
|
|
122
|
+
>
|
|
123
|
+
{sampleContent}
|
|
124
|
+
</ScrollShadow>
|
|
125
|
+
),
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Large Shadow
|
|
129
|
+
export const LargeShadow: Story = {
|
|
130
|
+
render: () => (
|
|
131
|
+
<ScrollShadow
|
|
132
|
+
size="lg"
|
|
133
|
+
className="h-64 w-80 border border-border rounded-sm"
|
|
134
|
+
>
|
|
135
|
+
{sampleContent}
|
|
136
|
+
</ScrollShadow>
|
|
137
|
+
),
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// Hidden Scrollbar
|
|
141
|
+
export const HiddenScrollbar: Story = {
|
|
142
|
+
render: () => (
|
|
143
|
+
<div className="space-y-4">
|
|
144
|
+
<p className="text-sm text-foreground-muted">
|
|
145
|
+
Scrollbar is hidden but scrolling still works
|
|
146
|
+
</p>
|
|
147
|
+
<ScrollShadow
|
|
148
|
+
hideScrollbar
|
|
149
|
+
className="h-64 w-80 border border-border rounded-sm"
|
|
150
|
+
>
|
|
151
|
+
{sampleContent}
|
|
152
|
+
</ScrollShadow>
|
|
153
|
+
</div>
|
|
154
|
+
),
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// Product List Example
|
|
158
|
+
export const ProductListExample: Story = {
|
|
159
|
+
render: () => {
|
|
160
|
+
const products = [
|
|
161
|
+
{ name: 'In-Wall Speaker VP66', price: '$599', category: 'Architectural' },
|
|
162
|
+
{ name: 'Soundbar SB500', price: '$799', category: 'Home Theater' },
|
|
163
|
+
{ name: 'Outdoor Speaker LS6', price: '$449', category: 'Landscape' },
|
|
164
|
+
{ name: 'Subwoofer SUB10', price: '$899', category: 'Performance' },
|
|
165
|
+
{ name: 'In-Ceiling Speaker VP68', price: '$649', category: 'Architectural' },
|
|
166
|
+
{ name: 'Garden Speaker GS6', price: '$399', category: 'Landscape' },
|
|
167
|
+
{ name: 'Invisible Speaker IS4', price: '$1,199', category: 'Invisible' },
|
|
168
|
+
{ name: 'Amplifier SA500', price: '$1,499', category: 'Electronics' },
|
|
169
|
+
{ name: 'In-Wall Speaker VP86', price: '$799', category: 'Architectural' },
|
|
170
|
+
{ name: 'Outdoor Rock Speaker RS6', price: '$549', category: 'Landscape' },
|
|
171
|
+
];
|
|
172
|
+
|
|
173
|
+
return (
|
|
174
|
+
<div className="w-80 border border-border rounded-sm">
|
|
175
|
+
<div className="p-4 border-b border-border">
|
|
176
|
+
<h3 className="font-medium">Products ({products.length})</h3>
|
|
177
|
+
</div>
|
|
178
|
+
<ScrollShadow className="h-64">
|
|
179
|
+
{products.map((product, i) => (
|
|
180
|
+
<div key={i} className="p-4 border-b border-border hover:bg-secondary-hover transition-colors cursor-pointer">
|
|
181
|
+
<div className="flex justify-between items-start">
|
|
182
|
+
<div>
|
|
183
|
+
<h4 className="font-medium text-sm">{product.name}</h4>
|
|
184
|
+
<p className="text-xs text-foreground-muted">{product.category}</p>
|
|
185
|
+
</div>
|
|
186
|
+
<span className="font-medium">{product.price}</span>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
))}
|
|
190
|
+
</ScrollShadow>
|
|
191
|
+
</div>
|
|
192
|
+
);
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// Image Gallery Example
|
|
197
|
+
export const ImageGalleryExample: Story = {
|
|
198
|
+
render: () => {
|
|
199
|
+
const images = Array.from({ length: 10 }, (_, i) => ({
|
|
200
|
+
id: i,
|
|
201
|
+
title: `Image ${i + 1}`,
|
|
202
|
+
}));
|
|
203
|
+
|
|
204
|
+
return (
|
|
205
|
+
<div className="space-y-2">
|
|
206
|
+
<h3 className="font-medium">Gallery</h3>
|
|
207
|
+
<ScrollShadow
|
|
208
|
+
orientation="horizontal"
|
|
209
|
+
hideScrollbar
|
|
210
|
+
className="w-full"
|
|
211
|
+
>
|
|
212
|
+
<div className="flex gap-3 p-1">
|
|
213
|
+
{images.map((image) => (
|
|
214
|
+
<div
|
|
215
|
+
key={image.id}
|
|
216
|
+
className="flex-shrink-0 w-32 h-32 bg-secondary rounded-sm flex items-center justify-center text-foreground-muted text-sm"
|
|
217
|
+
>
|
|
218
|
+
{image.title}
|
|
219
|
+
</div>
|
|
220
|
+
))}
|
|
221
|
+
</div>
|
|
222
|
+
</ScrollShadow>
|
|
223
|
+
</div>
|
|
224
|
+
);
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// Dropdown Menu Example
|
|
229
|
+
export const DropdownMenuExample: Story = {
|
|
230
|
+
render: () => {
|
|
231
|
+
const menuItems = [
|
|
232
|
+
'Dashboard',
|
|
233
|
+
'Products',
|
|
234
|
+
'Orders',
|
|
235
|
+
'Customers',
|
|
236
|
+
'Analytics',
|
|
237
|
+
'Reports',
|
|
238
|
+
'Settings',
|
|
239
|
+
'Help Center',
|
|
240
|
+
'Documentation',
|
|
241
|
+
'API Reference',
|
|
242
|
+
'System Status',
|
|
243
|
+
'Logout',
|
|
244
|
+
];
|
|
245
|
+
|
|
246
|
+
return (
|
|
247
|
+
<div className="w-48 border border-border rounded-sm shadow-lg bg-card">
|
|
248
|
+
<ScrollShadow className="max-h-64">
|
|
249
|
+
{menuItems.map((item, i) => (
|
|
250
|
+
<button
|
|
251
|
+
key={i}
|
|
252
|
+
className="w-full px-4 py-2 text-left text-sm hover:bg-secondary-hover transition-colors"
|
|
253
|
+
>
|
|
254
|
+
{item}
|
|
255
|
+
</button>
|
|
256
|
+
))}
|
|
257
|
+
</ScrollShadow>
|
|
258
|
+
</div>
|
|
259
|
+
);
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// All Sizes Comparison
|
|
264
|
+
export const AllSizesComparison: Story = {
|
|
265
|
+
render: () => (
|
|
266
|
+
<div className="flex gap-8">
|
|
267
|
+
<div className="space-y-2">
|
|
268
|
+
<p className="text-sm font-medium">Small</p>
|
|
269
|
+
<ScrollShadow size="sm" className="h-48 w-64 border border-border rounded-sm">
|
|
270
|
+
{sampleContent.slice(0, 10)}
|
|
271
|
+
</ScrollShadow>
|
|
272
|
+
</div>
|
|
273
|
+
<div className="space-y-2">
|
|
274
|
+
<p className="text-sm font-medium">Medium</p>
|
|
275
|
+
<ScrollShadow size="md" className="h-48 w-64 border border-border rounded-sm">
|
|
276
|
+
{sampleContent.slice(0, 10)}
|
|
277
|
+
</ScrollShadow>
|
|
278
|
+
</div>
|
|
279
|
+
<div className="space-y-2">
|
|
280
|
+
<p className="text-sm font-medium">Large</p>
|
|
281
|
+
<ScrollShadow size="lg" className="h-48 w-64 border border-border rounded-sm">
|
|
282
|
+
{sampleContent.slice(0, 10)}
|
|
283
|
+
</ScrollShadow>
|
|
284
|
+
</div>
|
|
285
|
+
</div>
|
|
286
|
+
),
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
// Responsive Matrix - Mobile, Tablet, Desktop
|
|
290
|
+
export const ResponsiveMatrix: Story = {
|
|
291
|
+
render: () => (
|
|
292
|
+
<div className="space-y-8">
|
|
293
|
+
{/* Mobile */}
|
|
294
|
+
<div>
|
|
295
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
|
|
296
|
+
<div className="w-[375px] border border-dashed border-border p-4">
|
|
297
|
+
<ScrollShadow className="h-48 border border-border rounded-sm">
|
|
298
|
+
{sampleContent.slice(0, 8)}
|
|
299
|
+
</ScrollShadow>
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
{/* Tablet */}
|
|
303
|
+
<div>
|
|
304
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
|
|
305
|
+
<div className="w-[768px] border border-dashed border-border p-4">
|
|
306
|
+
<ScrollShadow
|
|
307
|
+
orientation="horizontal"
|
|
308
|
+
className="border border-border rounded-sm"
|
|
309
|
+
>
|
|
310
|
+
<div className="flex gap-4 p-4">
|
|
311
|
+
{horizontalItems.slice(0, 8)}
|
|
312
|
+
</div>
|
|
313
|
+
</ScrollShadow>
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
{/* Desktop */}
|
|
317
|
+
<div>
|
|
318
|
+
<h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
|
|
319
|
+
<div className="w-[1280px] border border-dashed border-border p-4">
|
|
320
|
+
<div className="grid grid-cols-3 gap-4">
|
|
321
|
+
<ScrollShadow size="sm" className="h-48 border border-border rounded-sm">
|
|
322
|
+
{sampleContent.slice(0, 8)}
|
|
323
|
+
</ScrollShadow>
|
|
324
|
+
<ScrollShadow size="md" className="h-48 border border-border rounded-sm">
|
|
325
|
+
{sampleContent.slice(0, 8)}
|
|
326
|
+
</ScrollShadow>
|
|
327
|
+
<ScrollShadow size="lg" className="h-48 border border-border rounded-sm">
|
|
328
|
+
{sampleContent.slice(0, 8)}
|
|
329
|
+
</ScrollShadow>
|
|
330
|
+
</div>
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
</div>
|
|
334
|
+
),
|
|
335
|
+
};
|