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
@@ -6,6 +6,23 @@ import { Calendar as CalendarIcon } from "lucide-react";
6
6
  import { cn } from "@/lib/utils";
7
7
  import { Calendar } from "./calendar";
8
8
 
9
+ export type DatePickerState = "default" | "hover" | "focus" | "open" | "error" | "disabled";
10
+
11
+ // State styles for Storybook/Figma visualization
12
+ const getStateStyles = (state?: DatePickerState) => {
13
+ if (!state || state === "default") return "";
14
+
15
+ const stateMap: Record<string, string> = {
16
+ hover: "border-border-hover",
17
+ focus: "border-input-focus",
18
+ open: "border-input-focus",
19
+ error: "border-error",
20
+ disabled: "opacity-50 cursor-not-allowed",
21
+ };
22
+
23
+ return stateMap[state] || "";
24
+ };
25
+
9
26
  interface DatePickerProps {
10
27
  value?: Date;
11
28
  defaultValue?: Date;
@@ -18,6 +35,8 @@ interface DatePickerProps {
18
35
  minDate?: Date;
19
36
  maxDate?: Date;
20
37
  className?: string;
38
+ /** Visual state for Storybook/Figma documentation */
39
+ state?: DatePickerState;
21
40
  }
22
41
 
23
42
  export function DatePicker({
@@ -32,7 +51,11 @@ export function DatePicker({
32
51
  minDate,
33
52
  maxDate,
34
53
  className,
54
+ state,
35
55
  }: DatePickerProps) {
56
+ const isDisabled = disabled || state === "disabled";
57
+ const hasError = error || state === "error";
58
+ const isOpenState = state === "open";
36
59
  const [internalValue, setInternalValue] = useState<Date | undefined>(defaultValue);
37
60
  const [isOpen, setIsOpen] = useState(false);
38
61
  const containerRef = useRef<HTMLDivElement>(null);
@@ -78,15 +101,16 @@ export function DatePicker({
78
101
  )}
79
102
  <button
80
103
  type="button"
81
- onClick={() => !disabled && setIsOpen(!isOpen)}
82
- disabled={disabled}
104
+ onClick={() => !isDisabled && setIsOpen(!isOpen)}
105
+ disabled={isDisabled}
83
106
  className={cn(
84
107
  "w-full flex items-center justify-between border border-input-border bg-input px-4 py-3",
85
108
  "text-left transition-colors duration-200",
86
109
  "focus:border-input-focus focus:outline-none",
87
110
  "disabled:cursor-not-allowed disabled:opacity-50",
88
- error && "border-error",
89
- isOpen && "border-input-focus"
111
+ hasError && "border-error",
112
+ (isOpen || isOpenState) && "border-input-focus",
113
+ getStateStyles(state)
90
114
  )}
91
115
  >
92
116
  <span className={value ? "text-foreground" : "text-input-placeholder"}>
@@ -0,0 +1,340 @@
1
+ import type { Meta, StoryObj } from '@storybook/nextjs-vite';
2
+ import { useState } from 'react';
3
+ import { addDays, addMonths } from 'date-fns';
4
+ import { DateRangePicker } from './date-range-picker';
5
+
6
+ interface DateRange {
7
+ start: Date | undefined;
8
+ end: Date | undefined;
9
+ }
10
+
11
+ const meta: Meta<typeof DateRangePicker> = {
12
+ title: 'Components/Forms/DateRangePicker',
13
+ component: DateRangePicker,
14
+ tags: ['autodocs'],
15
+ parameters: {
16
+ docs: {
17
+ description: {
18
+ component: 'A date range picker for selecting start and end dates. Opens a dual-month calendar for easy range selection. Supports min/max constraints.',
19
+ },
20
+ },
21
+ },
22
+ argTypes: {
23
+ label: {
24
+ control: 'text',
25
+ description: 'Input label',
26
+ },
27
+ placeholder: {
28
+ control: 'text',
29
+ description: 'Placeholder text',
30
+ table: {
31
+ defaultValue: { summary: 'Select date range' },
32
+ },
33
+ },
34
+ dateFormat: {
35
+ control: 'text',
36
+ description: 'Display format for dates',
37
+ table: {
38
+ defaultValue: { summary: 'MMM d, yyyy' },
39
+ },
40
+ },
41
+ error: {
42
+ control: 'text',
43
+ description: 'Error message',
44
+ },
45
+ disabled: {
46
+ control: 'boolean',
47
+ description: 'Disabled state',
48
+ },
49
+ },
50
+ };
51
+
52
+ export default meta;
53
+ type Story = StoryObj<typeof meta>;
54
+
55
+ // Default
56
+ export const Default: Story = {
57
+ render: () => {
58
+ const [range, setRange] = useState<DateRange>({ start: undefined, end: undefined });
59
+ return (
60
+ <div className="w-80">
61
+ <DateRangePicker
62
+ value={range}
63
+ onValueChange={setRange}
64
+ />
65
+ </div>
66
+ );
67
+ },
68
+ };
69
+
70
+ // With Label
71
+ export const WithLabel: Story = {
72
+ render: () => {
73
+ const [range, setRange] = useState<DateRange>({ start: undefined, end: undefined });
74
+ return (
75
+ <div className="w-80">
76
+ <DateRangePicker
77
+ label="Travel Dates"
78
+ value={range}
79
+ onValueChange={setRange}
80
+ />
81
+ </div>
82
+ );
83
+ },
84
+ };
85
+
86
+ // With Selected Range
87
+ export const WithSelectedRange: Story = {
88
+ render: () => {
89
+ const today = new Date();
90
+ const [range, setRange] = useState<DateRange>({
91
+ start: today,
92
+ end: addDays(today, 7),
93
+ });
94
+ return (
95
+ <div className="w-80">
96
+ <DateRangePicker
97
+ label="Booking Period"
98
+ value={range}
99
+ onValueChange={setRange}
100
+ />
101
+ </div>
102
+ );
103
+ },
104
+ };
105
+
106
+ // With Error
107
+ export const WithError: Story = {
108
+ render: () => {
109
+ const [range, setRange] = useState<DateRange>({ start: undefined, end: undefined });
110
+ return (
111
+ <div className="w-80">
112
+ <DateRangePicker
113
+ label="Required Range"
114
+ error="Please select a date range"
115
+ value={range}
116
+ onValueChange={setRange}
117
+ />
118
+ </div>
119
+ );
120
+ },
121
+ };
122
+
123
+ // With Min/Max Dates
124
+ export const WithMinMaxDates: Story = {
125
+ render: () => {
126
+ const [range, setRange] = useState<DateRange>({ start: undefined, end: undefined });
127
+ const today = new Date();
128
+ return (
129
+ <div className="w-80 space-y-2">
130
+ <DateRangePicker
131
+ label="Event Period"
132
+ placeholder="Select dates within next 3 months"
133
+ value={range}
134
+ onValueChange={setRange}
135
+ minDate={today}
136
+ maxDate={addMonths(today, 3)}
137
+ />
138
+ <p className="text-xs text-foreground-muted">
139
+ Available dates: Today through {addMonths(today, 3).toLocaleDateString()}
140
+ </p>
141
+ </div>
142
+ );
143
+ },
144
+ };
145
+
146
+ // Disabled
147
+ export const Disabled: Story = {
148
+ render: () => {
149
+ const today = new Date();
150
+ return (
151
+ <div className="w-80">
152
+ <DateRangePicker
153
+ label="Locked Period"
154
+ defaultValue={{ start: today, end: addDays(today, 5) }}
155
+ disabled
156
+ />
157
+ </div>
158
+ );
159
+ },
160
+ };
161
+
162
+ // Custom Format
163
+ export const CustomFormat: Story = {
164
+ render: () => {
165
+ const today = new Date();
166
+ const [range, setRange] = useState<DateRange>({
167
+ start: today,
168
+ end: addDays(today, 7),
169
+ });
170
+ return (
171
+ <div className="w-80 space-y-4">
172
+ <DateRangePicker
173
+ label="Default Format"
174
+ dateFormat="MMM d, yyyy"
175
+ value={range}
176
+ onValueChange={setRange}
177
+ />
178
+ <DateRangePicker
179
+ label="Short Format"
180
+ dateFormat="MM/dd/yy"
181
+ value={range}
182
+ onValueChange={setRange}
183
+ />
184
+ <DateRangePicker
185
+ label="Verbose Format"
186
+ dateFormat="EEEE, MMMM d"
187
+ value={range}
188
+ onValueChange={setRange}
189
+ />
190
+ </div>
191
+ );
192
+ },
193
+ };
194
+
195
+ // Hotel Booking Example
196
+ export const HotelBookingExample: Story = {
197
+ render: () => {
198
+ const [range, setRange] = useState<DateRange>({ start: undefined, end: undefined });
199
+ const today = new Date();
200
+
201
+ const nights = range.start && range.end
202
+ ? Math.ceil((range.end.getTime() - range.start.getTime()) / (1000 * 60 * 60 * 24))
203
+ : 0;
204
+
205
+ return (
206
+ <div className="w-80 space-y-4 p-6 border border-border rounded-sm">
207
+ <h3 className="font-medium">Reserve Your Room</h3>
208
+ <DateRangePicker
209
+ label="Stay Duration"
210
+ placeholder="Select check-in and check-out"
211
+ value={range}
212
+ onValueChange={setRange}
213
+ minDate={today}
214
+ maxDate={addMonths(today, 6)}
215
+ />
216
+ {nights > 0 && (
217
+ <div className="space-y-2 pt-2 border-t border-border">
218
+ <div className="flex justify-between text-sm">
219
+ <span className="text-foreground-muted">Duration</span>
220
+ <span>{nights} night{nights > 1 ? 's' : ''}</span>
221
+ </div>
222
+ <div className="flex justify-between text-sm">
223
+ <span className="text-foreground-muted">Rate per night</span>
224
+ <span>$299</span>
225
+ </div>
226
+ <div className="flex justify-between font-medium pt-2 border-t border-border">
227
+ <span>Total</span>
228
+ <span>${(nights * 299).toLocaleString()}</span>
229
+ </div>
230
+ </div>
231
+ )}
232
+ </div>
233
+ );
234
+ },
235
+ };
236
+
237
+ // Report Filter Example
238
+ export const ReportFilterExample: Story = {
239
+ render: () => {
240
+ const [range, setRange] = useState<DateRange>({ start: undefined, end: undefined });
241
+ const today = new Date();
242
+
243
+ const presets = [
244
+ { label: 'Last 7 days', start: addDays(today, -7), end: today },
245
+ { label: 'Last 30 days', start: addDays(today, -30), end: today },
246
+ { label: 'Last 90 days', start: addDays(today, -90), end: today },
247
+ { label: 'This month', start: new Date(today.getFullYear(), today.getMonth(), 1), end: today },
248
+ ];
249
+
250
+ return (
251
+ <div className="w-80 space-y-4">
252
+ <DateRangePicker
253
+ label="Report Period"
254
+ value={range}
255
+ onValueChange={setRange}
256
+ maxDate={today}
257
+ />
258
+ <div className="flex flex-wrap gap-2">
259
+ {presets.map((preset) => (
260
+ <button
261
+ key={preset.label}
262
+ onClick={() => setRange({ start: preset.start, end: preset.end })}
263
+ className="text-xs px-2 py-1 border border-border rounded-sm hover:bg-secondary-hover transition-colors"
264
+ >
265
+ {preset.label}
266
+ </button>
267
+ ))}
268
+ </div>
269
+ </div>
270
+ );
271
+ },
272
+ };
273
+
274
+ // All States
275
+ export const AllStates: Story = {
276
+ render: () => {
277
+ const today = new Date();
278
+ return (
279
+ <div className="space-y-6 w-80">
280
+ <DateRangePicker label="Default" placeholder="Select date range" />
281
+ <DateRangePicker
282
+ label="With Value"
283
+ defaultValue={{ start: today, end: addDays(today, 7) }}
284
+ />
285
+ <DateRangePicker label="With Error" error="This field is required" />
286
+ <DateRangePicker
287
+ label="Disabled"
288
+ disabled
289
+ defaultValue={{ start: today, end: addDays(today, 3) }}
290
+ />
291
+ </div>
292
+ );
293
+ },
294
+ };
295
+
296
+ // Responsive Matrix - Mobile, Tablet, Desktop
297
+ export const ResponsiveMatrix: Story = {
298
+ render: () => {
299
+ const today = new Date();
300
+ return (
301
+ <div className="space-y-8">
302
+ {/* Mobile */}
303
+ <div>
304
+ <h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
305
+ <div className="w-[375px] border border-dashed border-border p-4">
306
+ <DateRangePicker label="Travel Dates" placeholder="Select dates" />
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
+ <div className="grid grid-cols-2 gap-4">
314
+ <DateRangePicker label="Booking Period" placeholder="Select range" />
315
+ <DateRangePicker
316
+ label="With Value"
317
+ defaultValue={{ start: today, end: addDays(today, 7) }}
318
+ />
319
+ </div>
320
+ </div>
321
+ </div>
322
+ {/* Desktop */}
323
+ <div>
324
+ <h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
325
+ <div className="w-[1280px] border border-dashed border-border p-4">
326
+ <div className="grid grid-cols-3 gap-4">
327
+ <DateRangePicker label="Default" placeholder="Select range" />
328
+ <DateRangePicker label="With Error" error="Required field" />
329
+ <DateRangePicker
330
+ label="Disabled"
331
+ disabled
332
+ defaultValue={{ start: today, end: addDays(today, 5) }}
333
+ />
334
+ </div>
335
+ </div>
336
+ </div>
337
+ </div>
338
+ );
339
+ },
340
+ };
@@ -0,0 +1,285 @@
1
+ import type { Meta, StoryObj } from '@storybook/nextjs-vite';
2
+ import { useState } from 'react';
3
+ import {
4
+ Dialog,
5
+ DialogContent,
6
+ DialogHeader,
7
+ DialogTitle,
8
+ DialogDescription,
9
+ DialogBody,
10
+ DialogFooter,
11
+ } from './dialog';
12
+ import { Button } from './button';
13
+ import { Input } from './input';
14
+
15
+ const meta: Meta<typeof Dialog> = {
16
+ title: 'Components/Overlays/Dialog',
17
+ component: Dialog,
18
+ tags: ['autodocs'],
19
+ parameters: {
20
+ docs: {
21
+ description: {
22
+ component: 'A modal dialog component for focused interactions. Includes backdrop, keyboard support (Escape to close), and scroll lock.',
23
+ },
24
+ },
25
+ layout: 'centered',
26
+ },
27
+ };
28
+
29
+ export default meta;
30
+ type Story = StoryObj<typeof meta>;
31
+
32
+ // Default Dialog
33
+ export const Default: Story = {
34
+ render: () => {
35
+ const [open, setOpen] = useState(false);
36
+ return (
37
+ <>
38
+ <Button onClick={() => setOpen(true)}>Open Dialog</Button>
39
+ <Dialog open={open} onClose={() => setOpen(false)}>
40
+ <DialogContent>
41
+ <DialogHeader>
42
+ <DialogTitle>Dialog Title</DialogTitle>
43
+ <DialogDescription>
44
+ This is a description of the dialog content.
45
+ </DialogDescription>
46
+ </DialogHeader>
47
+ <DialogBody>
48
+ <p className="text-foreground-secondary">
49
+ Dialog body content goes here. You can add any content you need.
50
+ </p>
51
+ </DialogBody>
52
+ <DialogFooter>
53
+ <Button variant="secondary" onClick={() => setOpen(false)}>
54
+ Cancel
55
+ </Button>
56
+ <Button onClick={() => setOpen(false)}>Confirm</Button>
57
+ </DialogFooter>
58
+ </DialogContent>
59
+ </Dialog>
60
+ </>
61
+ );
62
+ },
63
+ };
64
+
65
+ // Confirmation Dialog
66
+ export const Confirmation: Story = {
67
+ render: () => {
68
+ const [open, setOpen] = useState(false);
69
+ return (
70
+ <>
71
+ <Button variant="secondary" onClick={() => setOpen(true)}>
72
+ Delete Item
73
+ </Button>
74
+ <Dialog open={open} onClose={() => setOpen(false)}>
75
+ <DialogContent className="max-w-md">
76
+ <DialogHeader>
77
+ <DialogTitle>Delete Item?</DialogTitle>
78
+ <DialogDescription>
79
+ This action cannot be undone. This will permanently delete the item
80
+ from your account.
81
+ </DialogDescription>
82
+ </DialogHeader>
83
+ <DialogFooter>
84
+ <Button variant="secondary" onClick={() => setOpen(false)}>
85
+ Cancel
86
+ </Button>
87
+ <Button onClick={() => setOpen(false)}>Delete</Button>
88
+ </DialogFooter>
89
+ </DialogContent>
90
+ </Dialog>
91
+ </>
92
+ );
93
+ },
94
+ };
95
+
96
+ // Form Dialog
97
+ export const FormDialog: Story = {
98
+ render: () => {
99
+ const [open, setOpen] = useState(false);
100
+ return (
101
+ <>
102
+ <Button onClick={() => setOpen(true)}>Add New Item</Button>
103
+ <Dialog open={open} onClose={() => setOpen(false)}>
104
+ <DialogContent>
105
+ <DialogHeader>
106
+ <DialogTitle>Add New Item</DialogTitle>
107
+ <DialogDescription>
108
+ Fill out the form below to add a new item to your inventory.
109
+ </DialogDescription>
110
+ </DialogHeader>
111
+ <DialogBody>
112
+ <form className="space-y-4">
113
+ <Input label="Item Name" placeholder="Enter item name" />
114
+ <Input label="SKU" placeholder="Enter SKU" />
115
+ <Input label="Price" type="number" placeholder="0.00" />
116
+ </form>
117
+ </DialogBody>
118
+ <DialogFooter>
119
+ <Button variant="secondary" onClick={() => setOpen(false)}>
120
+ Cancel
121
+ </Button>
122
+ <Button onClick={() => setOpen(false)}>Add Item</Button>
123
+ </DialogFooter>
124
+ </DialogContent>
125
+ </Dialog>
126
+ </>
127
+ );
128
+ },
129
+ };
130
+
131
+ // Without Close Button
132
+ export const WithoutCloseButton: Story = {
133
+ render: () => {
134
+ const [open, setOpen] = useState(false);
135
+ return (
136
+ <>
137
+ <Button onClick={() => setOpen(true)}>Open Dialog</Button>
138
+ <Dialog open={open} onClose={() => setOpen(false)}>
139
+ <DialogContent showClose={false}>
140
+ <DialogHeader>
141
+ <DialogTitle>Required Action</DialogTitle>
142
+ <DialogDescription>
143
+ Please complete the required action before closing.
144
+ </DialogDescription>
145
+ </DialogHeader>
146
+ <DialogBody>
147
+ <p className="text-foreground-secondary">
148
+ This dialog has no close button, requiring user to use the action buttons.
149
+ </p>
150
+ </DialogBody>
151
+ <DialogFooter>
152
+ <Button onClick={() => setOpen(false)}>Complete</Button>
153
+ </DialogFooter>
154
+ </DialogContent>
155
+ </Dialog>
156
+ </>
157
+ );
158
+ },
159
+ };
160
+
161
+ // Long Content Dialog
162
+ export const LongContent: Story = {
163
+ render: () => {
164
+ const [open, setOpen] = useState(false);
165
+ return (
166
+ <>
167
+ <Button onClick={() => setOpen(true)}>Terms & Conditions</Button>
168
+ <Dialog open={open} onClose={() => setOpen(false)}>
169
+ <DialogContent className="max-w-2xl">
170
+ <DialogHeader>
171
+ <DialogTitle>Terms and Conditions</DialogTitle>
172
+ </DialogHeader>
173
+ <DialogBody className="max-h-[60vh] overflow-y-auto">
174
+ <div className="space-y-4 text-foreground-secondary text-sm">
175
+ {Array.from({ length: 10 }, (_, i) => (
176
+ <p key={i}>
177
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
178
+ eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
179
+ enim ad minim veniam, quis nostrud exercitation ullamco laboris
180
+ nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
181
+ in reprehenderit in voluptate velit esse cillum dolore eu fugiat
182
+ nulla pariatur.
183
+ </p>
184
+ ))}
185
+ </div>
186
+ </DialogBody>
187
+ <DialogFooter>
188
+ <Button variant="secondary" onClick={() => setOpen(false)}>
189
+ Decline
190
+ </Button>
191
+ <Button onClick={() => setOpen(false)}>Accept</Button>
192
+ </DialogFooter>
193
+ </DialogContent>
194
+ </Dialog>
195
+ </>
196
+ );
197
+ },
198
+ };
199
+
200
+ // Simple Alert Dialog
201
+ export const AlertDialog: Story = {
202
+ render: () => {
203
+ const [open, setOpen] = useState(false);
204
+ return (
205
+ <>
206
+ <Button onClick={() => setOpen(true)}>Show Alert</Button>
207
+ <Dialog open={open} onClose={() => setOpen(false)}>
208
+ <DialogContent className="max-w-sm">
209
+ <DialogHeader>
210
+ <DialogTitle>Alert</DialogTitle>
211
+ </DialogHeader>
212
+ <DialogBody>
213
+ <p className="text-foreground-secondary">
214
+ Your session will expire in 5 minutes.
215
+ </p>
216
+ </DialogBody>
217
+ <DialogFooter>
218
+ <Button onClick={() => setOpen(false)}>Okay</Button>
219
+ </DialogFooter>
220
+ </DialogContent>
221
+ </Dialog>
222
+ </>
223
+ );
224
+ },
225
+ };
226
+
227
+ // Responsive Matrix - Mobile, Tablet, Desktop (static previews)
228
+ export const ResponsiveMatrix: Story = {
229
+ render: () => (
230
+ <div className="space-y-8">
231
+ {/* Mobile */}
232
+ <div>
233
+ <h4 className="text-xs uppercase text-foreground-muted mb-2">Mobile (375px)</h4>
234
+ <div className="w-[375px] border border-dashed border-border p-4 bg-black/50">
235
+ <div className="bg-background rounded-lg p-4 shadow-lg">
236
+ <div className="space-y-2">
237
+ <h3 className="text-lg font-semibold">Confirm Action</h3>
238
+ <p className="text-sm text-foreground-secondary">Are you sure you want to proceed?</p>
239
+ </div>
240
+ <div className="flex flex-col gap-2 mt-4">
241
+ <Button className="w-full">Confirm</Button>
242
+ <Button variant="secondary" className="w-full">Cancel</Button>
243
+ </div>
244
+ </div>
245
+ </div>
246
+ </div>
247
+ {/* Tablet */}
248
+ <div>
249
+ <h4 className="text-xs uppercase text-foreground-muted mb-2">Tablet (768px)</h4>
250
+ <div className="w-[768px] border border-dashed border-border p-4 bg-black/50 flex items-center justify-center">
251
+ <div className="bg-background rounded-lg p-6 shadow-lg w-[400px]">
252
+ <div className="space-y-2">
253
+ <h3 className="text-lg font-semibold">Add New Item</h3>
254
+ <p className="text-sm text-foreground-secondary">Fill out the form below.</p>
255
+ </div>
256
+ <div className="space-y-4 mt-4">
257
+ <Input label="Item Name" placeholder="Enter name" />
258
+ <Input label="Price" type="number" placeholder="0.00" />
259
+ </div>
260
+ <div className="flex justify-end gap-2 mt-6">
261
+ <Button variant="secondary">Cancel</Button>
262
+ <Button>Add Item</Button>
263
+ </div>
264
+ </div>
265
+ </div>
266
+ </div>
267
+ {/* Desktop */}
268
+ <div>
269
+ <h4 className="text-xs uppercase text-foreground-muted mb-2">Desktop (1280px)</h4>
270
+ <div className="w-[1280px] border border-dashed border-border p-4 bg-black/50 flex items-center justify-center">
271
+ <div className="bg-background rounded-lg p-6 shadow-lg w-[500px]">
272
+ <div className="space-y-2">
273
+ <h3 className="text-lg font-semibold">Delete Item?</h3>
274
+ <p className="text-sm text-foreground-secondary">This action cannot be undone. This will permanently delete the item from your account.</p>
275
+ </div>
276
+ <div className="flex justify-end gap-2 mt-6">
277
+ <Button variant="secondary">Cancel</Button>
278
+ <Button>Delete</Button>
279
+ </div>
280
+ </div>
281
+ </div>
282
+ </div>
283
+ </div>
284
+ ),
285
+ };