@reliability-design/web 1.0.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 (60) hide show
  1. package/dist/components/rel-accordion/rel-accordion.stories.ts +313 -0
  2. package/dist/components/rel-accordion/rel-accordion.ts +114 -0
  3. package/dist/components/rel-alert/rel-alert.stories.ts +412 -0
  4. package/dist/components/rel-alert/rel-alert.ts +194 -0
  5. package/dist/components/rel-avatar/rel-avatar.stories.ts +327 -0
  6. package/dist/components/rel-avatar/rel-avatar.ts +100 -0
  7. package/dist/components/rel-badge/rel-badge.stories.ts +350 -0
  8. package/dist/components/rel-badge/rel-badge.ts +70 -0
  9. package/dist/components/rel-breadcrumb/rel-breadcrumb.stories.ts +306 -0
  10. package/dist/components/rel-breadcrumb/rel-breadcrumb.ts +97 -0
  11. package/dist/components/rel-button/rel-button.stories.ts +356 -0
  12. package/dist/components/rel-button/rel-button.ts +204 -0
  13. package/dist/components/rel-card/rel-card.stories.ts +299 -0
  14. package/dist/components/rel-card/rel-card.ts +33 -0
  15. package/dist/components/rel-cardmedia/rel-cardmedia.stories.ts +368 -0
  16. package/dist/components/rel-cardmedia/rel-cardmedia.ts +80 -0
  17. package/dist/components/rel-checkbox/rel-checkbox.stories.ts +414 -0
  18. package/dist/components/rel-checkbox/rel-checkbox.ts +164 -0
  19. package/dist/components/rel-chip/rel-chip.stories.ts +552 -0
  20. package/dist/components/rel-chip/rel-chip.ts +261 -0
  21. package/dist/components/rel-container/rel-container.stories.ts +239 -0
  22. package/dist/components/rel-container/rel-container.ts +74 -0
  23. package/dist/components/rel-divider/rel-divider.stories.ts +229 -0
  24. package/dist/components/rel-divider/rel-divider.ts +44 -0
  25. package/dist/components/rel-layout/rel-layout.stories.ts +713 -0
  26. package/dist/components/rel-layout/rel-layout.ts +60 -0
  27. package/dist/components/rel-menu/rel-menu.stories.ts +910 -0
  28. package/dist/components/rel-menu/rel-menu.ts +319 -0
  29. package/dist/components/rel-progress/rel-progress.stories.ts +817 -0
  30. package/dist/components/rel-progress/rel-progress.ts +182 -0
  31. package/dist/components/rel-radio/rel-radio.stories.ts +1048 -0
  32. package/dist/components/rel-radio/rel-radio.ts +187 -0
  33. package/dist/components/rel-segmented-button/rel-segmented-button.stories.ts +1079 -0
  34. package/dist/components/rel-segmented-button/rel-segmented-button.ts +150 -0
  35. package/dist/components/rel-select/rel-select.stories.ts +1201 -0
  36. package/dist/components/rel-select/rel-select.ts +261 -0
  37. package/dist/components/rel-slider/rel-slider.stories.ts +730 -0
  38. package/dist/components/rel-slider/rel-slider.ts +440 -0
  39. package/dist/components/rel-snackbar/rel-snackbar.stories.ts +808 -0
  40. package/dist/components/rel-snackbar/rel-snackbar.ts +212 -0
  41. package/dist/components/rel-stepper/rel-stepper.stories.ts +752 -0
  42. package/dist/components/rel-stepper/rel-stepper.ts +425 -0
  43. package/dist/components/rel-switch/rel-switch.stories.ts +441 -0
  44. package/dist/components/rel-switch/rel-switch.ts +161 -0
  45. package/dist/components/rel-tab/rel-tab.stories.ts +492 -0
  46. package/dist/components/rel-tab/rel-tab.ts +145 -0
  47. package/dist/components/rel-textfield/rel-textfield.stories.ts +710 -0
  48. package/dist/components/rel-textfield/rel-textfield.ts +224 -0
  49. package/dist/components/rel-toast/rel-toast.stories.ts +394 -0
  50. package/dist/components/rel-toast/rel-toast.ts +154 -0
  51. package/dist/components/rel-tooltip/rel-tooltip.stories.ts +364 -0
  52. package/dist/components/rel-tooltip/rel-tooltip.ts +80 -0
  53. package/dist/index.d.mts +372 -0
  54. package/dist/index.d.ts +372 -0
  55. package/dist/index.js +4593 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/index.mjs +4544 -0
  58. package/dist/index.mjs.map +1 -0
  59. package/dist/styles/vars.css +184 -0
  60. package/package.json +32 -0
@@ -0,0 +1,808 @@
1
+ import type { Meta, StoryObj } from '@storybook/web-components-vite';
2
+ import { html } from 'lit';
3
+ import './rel-snackbar';
4
+
5
+ /**
6
+ * The Snackbar component provides brief messages about app processes at the bottom of the screen.
7
+ * Built with Lit web components for framework-agnostic usage with full token support.
8
+ *
9
+ * ## Features
10
+ * - **Auto-dismiss**: Automatically disappears after specified duration
11
+ * - **Action Button**: Optional action button with custom text
12
+ * - **Close Icon**: Optional close button
13
+ * - **Layout Modes**: Single or double line layouts
14
+ * - **Longer Action**: Doubles the duration for actions requiring more time
15
+ * - **Token-Based Styling**: Uses design system tokens
16
+ * - **Animated Entry**: Slides up with fade-in animation
17
+ */
18
+ const meta: Meta = {
19
+ title: 'Components/Snackbar',
20
+ tags: ['autodocs'],
21
+ component: 'rel-snackbar',
22
+ argTypes: {
23
+ lines: {
24
+ control: { type: 'select' },
25
+ options: ['single', 'double'],
26
+ description: 'Layout style',
27
+ table: {
28
+ defaultValue: { summary: 'single' },
29
+ type: { summary: 'single | double' },
30
+ },
31
+ },
32
+ duration: {
33
+ control: { type: 'number' },
34
+ description: 'Auto-dismiss duration in milliseconds (0 to disable)',
35
+ table: {
36
+ defaultValue: { summary: '4000' },
37
+ },
38
+ },
39
+ longerAction: {
40
+ control: { type: 'boolean' },
41
+ description: 'Doubles the duration for longer actions',
42
+ table: {
43
+ defaultValue: { summary: 'false' },
44
+ },
45
+ },
46
+ iconClose: {
47
+ control: { type: 'boolean' },
48
+ description: 'Show close button',
49
+ table: {
50
+ defaultValue: { summary: 'true' },
51
+ },
52
+ },
53
+ },
54
+ };
55
+
56
+ export default meta;
57
+ type Story = StoryObj;
58
+
59
+ /**
60
+ * Default snackbar with message and close icon
61
+ */
62
+ export const Default: Story = {
63
+ render: () => {
64
+ const snackbar = document.createElement('rel-snackbar') as any;
65
+ snackbar.message = 'This is a snackbar message';
66
+ snackbar.duration = 0; // Disable auto-dismiss for demo
67
+ snackbar.addEventListener('close', () => {
68
+ console.log('Snackbar closed');
69
+ });
70
+
71
+ return snackbar;
72
+ },
73
+ };
74
+
75
+ /**
76
+ * Snackbar with action button
77
+ */
78
+ export const WithAction: Story = {
79
+ render: () => {
80
+ const snackbar = document.createElement('rel-snackbar') as any;
81
+ snackbar.message = 'Connection lost';
82
+ snackbar.action = 'Retry';
83
+ snackbar.duration = 0;
84
+ snackbar.addEventListener('actionClick', () => {
85
+ console.log('Action clicked');
86
+ });
87
+
88
+ return snackbar;
89
+ },
90
+ };
91
+
92
+ /**
93
+ * Snackbar without close icon
94
+ */
95
+ export const WithoutCloseIcon: Story = {
96
+ render: () => {
97
+ const snackbar = document.createElement('rel-snackbar') as any;
98
+ snackbar.message = 'Message sent successfully';
99
+ snackbar.iconClose = false;
100
+ snackbar.duration = 0;
101
+
102
+ return snackbar;
103
+ },
104
+ };
105
+
106
+ /**
107
+ * Snackbar with action and no close icon
108
+ */
109
+ export const ActionOnly: Story = {
110
+ render: () => {
111
+ const snackbar = document.createElement('rel-snackbar') as any;
112
+ snackbar.message = 'Your changes have been saved';
113
+ snackbar.action = 'Undo';
114
+ snackbar.iconClose = false;
115
+ snackbar.duration = 0;
116
+ snackbar.addEventListener('actionClick', () => {
117
+ console.log('Undo clicked');
118
+ });
119
+
120
+ return snackbar;
121
+ },
122
+ };
123
+
124
+ /**
125
+ * Double line layout
126
+ */
127
+ export const DoubleLine: Story = {
128
+ render: () => {
129
+ const snackbar = document.createElement('rel-snackbar') as any;
130
+ snackbar.message = 'This is a longer snackbar message that needs more space';
131
+ snackbar.action = 'View Details';
132
+ snackbar.lines = 'double';
133
+ snackbar.duration = 0;
134
+
135
+ return snackbar;
136
+ },
137
+ };
138
+
139
+ /**
140
+ * Double line with longer action
141
+ */
142
+ export const DoubleLineWithLongerAction: Story = {
143
+ render: () => {
144
+ const snackbar = document.createElement('rel-snackbar') as any;
145
+ snackbar.message = 'Please review the details before continuing with this action';
146
+ snackbar.action = 'Review';
147
+ snackbar.lines = 'double';
148
+ snackbar.longerAction = true;
149
+ snackbar.duration = 0;
150
+
151
+ return snackbar;
152
+ },
153
+ };
154
+
155
+ /**
156
+ * Success message
157
+ */
158
+ export const SuccessMessage: Story = {
159
+ render: () => {
160
+ const snackbar = document.createElement('rel-snackbar') as any;
161
+ snackbar.message = 'File uploaded successfully';
162
+ snackbar.iconClose = false;
163
+ snackbar.duration = 0;
164
+
165
+ return snackbar;
166
+ },
167
+ };
168
+
169
+ /**
170
+ * Error message with retry
171
+ */
172
+ export const ErrorWithRetry: Story = {
173
+ render: () => {
174
+ const snackbar = document.createElement('rel-snackbar') as any;
175
+ snackbar.message = 'Failed to save changes';
176
+ snackbar.action = 'Retry';
177
+ snackbar.duration = 0;
178
+ snackbar.addEventListener('actionClick', () => {
179
+ console.log('Retrying...');
180
+ });
181
+
182
+ return snackbar;
183
+ },
184
+ };
185
+
186
+ /**
187
+ * Network status
188
+ */
189
+ export const NetworkStatus: Story = {
190
+ render: () => {
191
+ const snackbar = document.createElement('rel-snackbar') as any;
192
+ snackbar.message = 'No internet connection';
193
+ snackbar.action = 'Retry';
194
+ snackbar.duration = 0;
195
+
196
+ return snackbar;
197
+ },
198
+ };
199
+
200
+ /**
201
+ * Download complete
202
+ */
203
+ export const DownloadComplete: Story = {
204
+ render: () => {
205
+ const snackbar = document.createElement('rel-snackbar') as any;
206
+ snackbar.message = 'Download complete';
207
+ snackbar.action = 'Open';
208
+ snackbar.iconClose = false;
209
+ snackbar.duration = 0;
210
+ snackbar.addEventListener('actionClick', () => {
211
+ console.log('Opening file...');
212
+ });
213
+
214
+ return snackbar;
215
+ },
216
+ };
217
+
218
+ /**
219
+ * Undo action
220
+ */
221
+ export const UndoAction: Story = {
222
+ render: () => {
223
+ const snackbar = document.createElement('rel-snackbar') as any;
224
+ snackbar.message = 'Item deleted';
225
+ snackbar.action = 'Undo';
226
+ snackbar.duration = 0;
227
+ snackbar.addEventListener('actionClick', () => {
228
+ console.log('Undo delete');
229
+ });
230
+
231
+ return snackbar;
232
+ },
233
+ };
234
+
235
+ /**
236
+ * Long message
237
+ */
238
+ export const LongMessage: Story = {
239
+ render: () => {
240
+ const snackbar = document.createElement('rel-snackbar') as any;
241
+ snackbar.message = 'Your request is being processed. This may take a few moments to complete.';
242
+ snackbar.action = 'View Status';
243
+ snackbar.lines = 'double';
244
+ snackbar.duration = 0;
245
+
246
+ return snackbar;
247
+ },
248
+ };
249
+
250
+ /**
251
+ * Update available
252
+ */
253
+ export const UpdateAvailable: Story = {
254
+ render: () => {
255
+ const snackbar = document.createElement('rel-snackbar') as any;
256
+ snackbar.message = 'A new version is available';
257
+ snackbar.action = 'Update';
258
+ snackbar.duration = 0;
259
+ snackbar.addEventListener('actionClick', () => {
260
+ console.log('Updating...');
261
+ });
262
+
263
+ return snackbar;
264
+ },
265
+ };
266
+
267
+ /**
268
+ * Multiple snackbars stacked
269
+ */
270
+ export const StackedSnackbars: Story = {
271
+ render: () => html`
272
+ <div style="display: flex; flex-direction: column; align-items: flex-start;">
273
+ ${(() => {
274
+ const snackbar1 = document.createElement('rel-snackbar') as any;
275
+ snackbar1.message = 'First notification';
276
+ snackbar1.action = 'Action';
277
+ snackbar1.duration = 0;
278
+
279
+ return snackbar1;
280
+ })()}
281
+ ${(() => {
282
+ const snackbar2 = document.createElement('rel-snackbar') as any;
283
+ snackbar2.message = 'Second notification';
284
+ snackbar2.duration = 0;
285
+
286
+ return snackbar2;
287
+ })()}
288
+ ${(() => {
289
+ const snackbar3 = document.createElement('rel-snackbar') as any;
290
+ snackbar3.message = 'Third notification';
291
+ snackbar3.action = 'View';
292
+ snackbar3.duration = 0;
293
+
294
+ return snackbar3;
295
+ })()}
296
+ </div>
297
+ `,
298
+ };
299
+
300
+ /**
301
+ * Interactive demo with auto-dismiss
302
+ */
303
+ export const InteractiveDemo: Story = {
304
+ render: () => html`
305
+ <div style="display: flex; flex-direction: column; gap: 16px;">
306
+ <button
307
+ style="padding: 12px 24px; background: var(--rel-colors-black); color: white; border: none; border-radius: 6px; cursor: pointer; font-family: var(--rel-fontFamilies-primary); font-size: var(--rel-fontSizes-sm);"
308
+ @click=${(e: Event) => {
309
+ const container = (e.target as HTMLElement).parentElement!.querySelector('.snackbar-container');
310
+ const snackbar = document.createElement('rel-snackbar') as any;
311
+ snackbar.message = 'This will auto-dismiss in 4 seconds';
312
+ snackbar.action = 'Dismiss';
313
+ snackbar.duration = 4000;
314
+ snackbar.addEventListener('close', () => {
315
+ snackbar.remove();
316
+ });
317
+ snackbar.addEventListener('actionClick', () => {
318
+ console.log('Action clicked');
319
+ });
320
+ container!.appendChild(snackbar);
321
+ }}>
322
+ Show Snackbar
323
+ </button>
324
+ <div class="snackbar-container" style="min-height: 100px;"></div>
325
+ </div>
326
+ `,
327
+ };
328
+
329
+ /**
330
+ * Single line - basic message only
331
+ */
332
+ export const SingleLineBasic: Story = {
333
+ render: () => {
334
+ const snackbar = document.createElement('rel-snackbar') as any;
335
+ snackbar.message = 'Operation completed';
336
+ snackbar.lines = 'single';
337
+ snackbar.iconClose = false;
338
+ snackbar.duration = 0;
339
+
340
+ return snackbar;
341
+ },
342
+ };
343
+
344
+ /**
345
+ * Single line - with action and close
346
+ */
347
+ export const SingleLineComplete: Story = {
348
+ render: () => {
349
+ const snackbar = document.createElement('rel-snackbar') as any;
350
+ snackbar.message = 'Email sent to john@example.com';
351
+ snackbar.action = 'View';
352
+ snackbar.lines = 'single';
353
+ snackbar.duration = 0;
354
+ snackbar.addEventListener('actionClick', () => {
355
+ console.log('View email');
356
+ });
357
+
358
+ return snackbar;
359
+ },
360
+ };
361
+
362
+ /**
363
+ * Double line - no action
364
+ */
365
+ export const DoubleLineNoAction: Story = {
366
+ render: () => {
367
+ const snackbar = document.createElement('rel-snackbar') as any;
368
+ snackbar.message = 'Your data has been synchronized across all devices successfully';
369
+ snackbar.lines = 'double';
370
+ snackbar.duration = 0;
371
+
372
+ return snackbar;
373
+ },
374
+ };
375
+
376
+ /**
377
+ * Double line - action only (no close)
378
+ */
379
+ export const DoubleLineActionOnly: Story = {
380
+ render: () => {
381
+ const snackbar = document.createElement('rel-snackbar') as any;
382
+ snackbar.message = 'New features are available in this release. Check them out!';
383
+ snackbar.action = 'Learn More';
384
+ snackbar.lines = 'double';
385
+ snackbar.iconClose = false;
386
+ snackbar.duration = 0;
387
+ snackbar.addEventListener('actionClick', () => {
388
+ console.log('Navigate to features page');
389
+ });
390
+
391
+ return snackbar;
392
+ },
393
+ };
394
+
395
+ /**
396
+ * Duration - 2 seconds (short)
397
+ */
398
+ export const ShortDuration: Story = {
399
+ render: () => html`
400
+ <div style="display: flex; flex-direction: column; gap: 16px;">
401
+ <button
402
+ style="padding: 12px 24px; background: var(--rel-colors-black); color: white; border: none; border-radius: 6px; cursor: pointer; font-family: var(--rel-fontFamilies-primary); font-size: var(--rel-fontSizes-sm);"
403
+ @click=${(e: Event) => {
404
+ const container = (e.target as HTMLElement).parentElement!.querySelector('.snackbar-container');
405
+ const snackbar = document.createElement('rel-snackbar') as any;
406
+ snackbar.message = 'Quick notification (2s)';
407
+ snackbar.duration = 2000;
408
+ snackbar.addEventListener('close', () => {
409
+ snackbar.remove();
410
+ });
411
+ container!.appendChild(snackbar);
412
+ }}>
413
+ Show 2-Second Snackbar
414
+ </button>
415
+ <div class="snackbar-container" style="min-height: 100px;"></div>
416
+ </div>
417
+ `,
418
+ };
419
+
420
+ /**
421
+ * Duration - 6 seconds (long)
422
+ */
423
+ export const LongDuration: Story = {
424
+ render: () => html`
425
+ <div style="display: flex; flex-direction: column; gap: 16px;">
426
+ <button
427
+ style="padding: 12px 24px; background: var(--rel-colors-black); color: white; border: none; border-radius: 6px; cursor: pointer; font-family: var(--rel-fontFamilies-primary); font-size: var(--rel-fontSizes-sm);"
428
+ @click=${(e: Event) => {
429
+ const container = (e.target as HTMLElement).parentElement!.querySelector('.snackbar-container');
430
+ const snackbar = document.createElement('rel-snackbar') as any;
431
+ snackbar.message = 'Important message (6s)';
432
+ snackbar.action = 'Acknowledge';
433
+ snackbar.duration = 6000;
434
+ snackbar.addEventListener('close', () => {
435
+ snackbar.remove();
436
+ });
437
+ snackbar.addEventListener('actionClick', () => {
438
+ console.log('Acknowledged');
439
+ });
440
+ container!.appendChild(snackbar);
441
+ }}>
442
+ Show 6-Second Snackbar
443
+ </button>
444
+ <div class="snackbar-container" style="min-height: 100px;"></div>
445
+ </div>
446
+ `,
447
+ };
448
+
449
+ /**
450
+ * Longer action demonstration - duration doubles
451
+ */
452
+ export const LongerActionDemo: Story = {
453
+ render: () => html`
454
+ <div style="display: flex; flex-direction: column; gap: 16px;">
455
+ <div style="margin-bottom: 12px;">
456
+ <p style="margin: 0; font-family: var(--rel-fontFamilies-primary); font-size: var(--rel-fontSizes-sm); color: var(--rel-colors-gray-heading);">
457
+ The longerAction property doubles the auto-dismiss duration (4s → 8s)
458
+ </p>
459
+ </div>
460
+ <button
461
+ style="padding: 12px 24px; background: var(--rel-colors-black); color: white; border: none; border-radius: 6px; cursor: pointer; font-family: var(--rel-fontFamilies-primary); font-size: var(--rel-fontSizes-sm);"
462
+ @click=${(e: Event) => {
463
+ const container = (e.target as HTMLElement).parentElement!.querySelector('.snackbar-container');
464
+ const snackbar = document.createElement('rel-snackbar') as any;
465
+ snackbar.message = 'Review this carefully before deciding';
466
+ snackbar.action = 'Review & Approve';
467
+ snackbar.lines = 'double';
468
+ snackbar.longerAction = true;
469
+ snackbar.duration = 4000; // Will become 8000ms due to longerAction
470
+ snackbar.addEventListener('close', () => {
471
+ snackbar.remove();
472
+ });
473
+ snackbar.addEventListener('actionClick', () => {
474
+ console.log('Review action clicked');
475
+ });
476
+ container!.appendChild(snackbar);
477
+ }}>
478
+ Show Longer Action Snackbar (8s)
479
+ </button>
480
+ <div class="snackbar-container" style="min-height: 100px;"></div>
481
+ </div>
482
+ `,
483
+ };
484
+
485
+ /**
486
+ * Interactive - Form submission success
487
+ */
488
+ export const FormSubmissionSuccess: Story = {
489
+ render: () => html`
490
+ <div style="display: flex; flex-direction: column; gap: 16px;">
491
+ <div style="padding: 24px; border: 1px solid var(--rel-colors-gray-border); border-radius: 8px;">
492
+ <h4 style="margin: 0 0 16px; font-family: var(--rel-fontFamilies-primary); color: var(--rel-colors-gray-heading);">Contact Form</h4>
493
+ <input type="text" placeholder="Name" style="width: 100%; padding: 8px; margin-bottom: 12px; border: 1px solid var(--rel-colors-gray-border); border-radius: 4px; font-family: var(--rel-fontFamilies-primary);" />
494
+ <input type="email" placeholder="Email" style="width: 100%; padding: 8px; margin-bottom: 12px; border: 1px solid var(--rel-colors-gray-border); border-radius: 4px; font-family: var(--rel-fontFamilies-primary);" />
495
+ <button
496
+ style="padding: 12px 24px; background: var(--rel-colors-black); color: white; border: none; border-radius: 6px; cursor: pointer; font-family: var(--rel-fontFamilies-primary); font-size: var(--rel-fontSizes-sm);"
497
+ @click=${(e: Event) => {
498
+ const container = (e.target as HTMLElement).closest('div')!.parentElement!.querySelector('.snackbar-container');
499
+ const snackbar = document.createElement('rel-snackbar') as any;
500
+ snackbar.message = 'Form submitted successfully';
501
+ snackbar.action = 'View';
502
+ snackbar.iconClose = false;
503
+ snackbar.duration = 3000;
504
+ snackbar.addEventListener('close', () => {
505
+ snackbar.remove();
506
+ });
507
+ snackbar.addEventListener('actionClick', () => {
508
+ console.log('View submission');
509
+ });
510
+ container!.appendChild(snackbar);
511
+ }}>
512
+ Submit
513
+ </button>
514
+ </div>
515
+ <div class="snackbar-container" style="min-height: 100px;"></div>
516
+ </div>
517
+ `,
518
+ };
519
+
520
+ /**
521
+ * Interactive - Delete with undo
522
+ */
523
+ export const DeleteWithUndo: Story = {
524
+ render: () => html`
525
+ <div style="display: flex; flex-direction: column; gap: 16px;">
526
+ <div style="padding: 24px; border: 1px solid var(--rel-colors-gray-border); border-radius: 8px;">
527
+ <h4 style="margin: 0 0 16px; font-family: var(--rel-fontFamilies-primary); color: var(--rel-colors-gray-heading);">File Manager</h4>
528
+ <div style="padding: 12px; background: var(--rel-colors-gray-bg); border-radius: 4px; margin-bottom: 12px; display: flex; justify-content: space-between; align-items: center;">
529
+ <span style="font-family: var(--rel-fontFamilies-primary); font-size: var(--rel-fontSizes-sm);">document.pdf</span>
530
+ <button
531
+ style="padding: 8px 16px; background: #dc2626; color: white; border: none; border-radius: 4px; cursor: pointer; font-family: var(--rel-fontFamilies-primary); font-size: var(--rel-fontSizes-sm);"
532
+ @click=${(e: Event) => {
533
+ const container = (e.target as HTMLElement).closest('div')!.parentElement!.parentElement!.querySelector('.snackbar-container');
534
+ const snackbar = document.createElement('rel-snackbar') as any;
535
+ snackbar.message = 'document.pdf deleted';
536
+ snackbar.action = 'Undo';
537
+ snackbar.duration = 5000;
538
+ snackbar.addEventListener('close', () => {
539
+ snackbar.remove();
540
+ });
541
+ snackbar.addEventListener('actionClick', () => {
542
+ console.log('File restored');
543
+ alert('File restored!');
544
+ });
545
+ container!.appendChild(snackbar);
546
+ }}>
547
+ Delete
548
+ </button>
549
+ </div>
550
+ </div>
551
+ <div class="snackbar-container" style="min-height: 100px;"></div>
552
+ </div>
553
+ `,
554
+ };
555
+
556
+ /**
557
+ * Interactive - Network error with retry
558
+ */
559
+ export const NetworkErrorRetry: Story = {
560
+ render: () => html`
561
+ <div style="display: flex; flex-direction: column; gap: 16px;">
562
+ <button
563
+ style="padding: 12px 24px; background: var(--rel-colors-black); color: white; border: none; border-radius: 6px; cursor: pointer; font-family: var(--rel-fontFamilies-primary); font-size: var(--rel-fontSizes-sm);"
564
+ @click=${(e: Event) => {
565
+ const container = (e.target as HTMLElement).parentElement!.querySelector('.snackbar-container');
566
+ // Clear any existing snackbars
567
+ container!.innerHTML = '';
568
+ const snackbar = document.createElement('rel-snackbar') as any;
569
+ snackbar.message = 'Failed to connect to server';
570
+ snackbar.action = 'Retry';
571
+ snackbar.lines = 'single';
572
+ snackbar.duration = 0; // Don't auto-dismiss on error
573
+ let retryCount = 0;
574
+ snackbar.addEventListener('actionClick', () => {
575
+ retryCount++;
576
+ console.log(`Retry attempt ${retryCount}`);
577
+
578
+ // Simulate retry
579
+ setTimeout(() => {
580
+ snackbar.remove();
581
+ if (retryCount === 1) {
582
+ // First retry fails
583
+ const retrySnackbar = document.createElement('rel-snackbar') as any;
584
+ retrySnackbar.message = 'Still unable to connect';
585
+ retrySnackbar.action = 'Retry';
586
+ retrySnackbar.duration = 0;
587
+ retrySnackbar.addEventListener('actionClick', () => {
588
+ retrySnackbar.remove();
589
+ // Second retry succeeds
590
+ const successSnackbar = document.createElement('rel-snackbar') as any;
591
+ successSnackbar.message = 'Connected successfully';
592
+ successSnackbar.iconClose = false;
593
+ successSnackbar.duration = 3000;
594
+ successSnackbar.addEventListener('close', () => {
595
+ successSnackbar.remove();
596
+ });
597
+ container!.appendChild(successSnackbar);
598
+ });
599
+ container!.appendChild(retrySnackbar);
600
+ }
601
+ }, 1000);
602
+ });
603
+ container!.appendChild(snackbar);
604
+ }}>
605
+ Simulate Connection Error
606
+ </button>
607
+ <div class="snackbar-container" style="min-height: 100px;"></div>
608
+ </div>
609
+ `,
610
+ };
611
+
612
+ /**
613
+ * Interactive - Multiple notification queue
614
+ */
615
+ export const NotificationQueue: Story = {
616
+ render: () => html`
617
+ <div style="display: flex; flex-direction: column; gap: 16px;">
618
+ <div style="display: flex; gap: 12px;">
619
+ <button
620
+ style="padding: 12px 24px; background: var(--rel-colors-black); color: white; border: none; border-radius: 6px; cursor: pointer; font-family: var(--rel-fontFamilies-primary); font-size: var(--rel-fontSizes-sm);"
621
+ @click=${(e: Event) => {
622
+ const container = (e.target as HTMLElement).parentElement!.parentElement!.querySelector('.snackbar-container');
623
+ const messages = [
624
+ { msg: 'Task 1 completed', action: 'View' },
625
+ { msg: 'Task 2 completed', action: 'View' },
626
+ { msg: 'Task 3 completed', action: 'View' },
627
+ ];
628
+
629
+ messages.forEach((item, index) => {
630
+ setTimeout(() => {
631
+ const snackbar = document.createElement('rel-snackbar') as any;
632
+ snackbar.message = item.msg;
633
+ snackbar.action = item.action;
634
+ snackbar.duration = 3000;
635
+ snackbar.addEventListener('close', () => {
636
+ snackbar.remove();
637
+ });
638
+ snackbar.addEventListener('actionClick', () => {
639
+ console.log(`View task ${index + 1}`);
640
+ });
641
+ container!.appendChild(snackbar);
642
+ }, index * 500);
643
+ });
644
+ }}>
645
+ Queue Multiple Notifications
646
+ </button>
647
+ <button
648
+ style="padding: 12px 24px; background: #dc2626; color: white; border: none; border-radius: 6px; cursor: pointer; font-family: var(--rel-fontFamilies-primary); font-size: var(--rel-fontSizes-sm);"
649
+ @click=${(e: Event) => {
650
+ const container = (e.target as HTMLElement).parentElement!.parentElement!.querySelector('.snackbar-container');
651
+ container!.innerHTML = '';
652
+ }}>
653
+ Clear All
654
+ </button>
655
+ </div>
656
+ <div class="snackbar-container" style="display: flex; flex-direction: column; gap: 12px; min-height: 200px;"></div>
657
+ </div>
658
+ `,
659
+ };
660
+
661
+ /**
662
+ * Use case - File upload progress
663
+ */
664
+ export const FileUploadProgress: Story = {
665
+ render: () => html`
666
+ <div style="display: flex; flex-direction: column; gap: 16px;">
667
+ <button
668
+ style="padding: 12px 24px; background: var(--rel-colors-black); color: white; border: none; border-radius: 6px; cursor: pointer; font-family: var(--rel-fontFamilies-primary); font-size: var(--rel-fontSizes-sm);"
669
+ @click=${(e: Event) => {
670
+ const container = (e.target as HTMLElement).parentElement!.querySelector('.snackbar-container');
671
+ container!.innerHTML = '';
672
+
673
+ // Show uploading
674
+ const uploadingSnackbar = document.createElement('rel-snackbar') as any;
675
+ uploadingSnackbar.message = 'Uploading image.jpg...';
676
+ uploadingSnackbar.iconClose = false;
677
+ uploadingSnackbar.duration = 0;
678
+ container!.appendChild(uploadingSnackbar);
679
+
680
+ // After 2 seconds, show success
681
+ setTimeout(() => {
682
+ uploadingSnackbar.remove();
683
+ const successSnackbar = document.createElement('rel-snackbar') as any;
684
+ successSnackbar.message = 'image.jpg uploaded successfully';
685
+ successSnackbar.action = 'View';
686
+ successSnackbar.duration = 4000;
687
+ successSnackbar.addEventListener('close', () => {
688
+ successSnackbar.remove();
689
+ });
690
+ successSnackbar.addEventListener('actionClick', () => {
691
+ console.log('View uploaded file');
692
+ });
693
+ container!.appendChild(successSnackbar);
694
+ }, 2000);
695
+ }}>
696
+ Upload File
697
+ </button>
698
+ <div class="snackbar-container" style="min-height: 100px;"></div>
699
+ </div>
700
+ `,
701
+ };
702
+
703
+ /**
704
+ * Use case - Settings saved
705
+ */
706
+ export const SettingsSaved: Story = {
707
+ render: () => html`
708
+ <div style="display: flex; flex-direction: column; gap: 16px;">
709
+ <div style="padding: 24px; border: 1px solid var(--rel-colors-gray-border); border-radius: 8px;">
710
+ <h4 style="margin: 0 0 16px; font-family: var(--rel-fontFamilies-primary); color: var(--rel-colors-gray-heading);">User Preferences</h4>
711
+ <label style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-family: var(--rel-fontFamilies-primary); font-size: var(--rel-fontSizes-sm);">
712
+ <input type="checkbox" />
713
+ Enable notifications
714
+ </label>
715
+ <label style="display: flex; align-items: center; gap: 8px; margin-bottom: 16px; font-family: var(--rel-fontFamilies-primary); font-size: var(--rel-fontSizes-sm);">
716
+ <input type="checkbox" />
717
+ Auto-save
718
+ </label>
719
+ <button
720
+ style="padding: 12px 24px; background: var(--rel-colors-black); color: white; border: none; border-radius: 6px; cursor: pointer; font-family: var(--rel-fontFamilies-primary); font-size: var(--rel-fontSizes-sm);"
721
+ @click=${(e: Event) => {
722
+ const container = (e.target as HTMLElement).closest('div')!.parentElement!.parentElement!.querySelector('.snackbar-container');
723
+ const snackbar = document.createElement('rel-snackbar') as any;
724
+ snackbar.message = 'Settings saved';
725
+ snackbar.iconClose = false;
726
+ snackbar.duration = 2000;
727
+ snackbar.addEventListener('close', () => {
728
+ snackbar.remove();
729
+ });
730
+ container!.appendChild(snackbar);
731
+ }}>
732
+ Save Settings
733
+ </button>
734
+ </div>
735
+ <div class="snackbar-container" style="min-height: 100px;"></div>
736
+ </div>
737
+ `,
738
+ };
739
+
740
+ /**
741
+ * All variants showcase
742
+ */
743
+ export const AllVariants: Story = {
744
+ render: () => html`
745
+ <div style="display: flex; flex-direction: column; gap: 24px;">
746
+ <div>
747
+ <h4 style="margin: 0 0 12px; color: var(--rel-colors-gray-heading); font-size: 16px;">Default</h4>
748
+ ${(() => {
749
+ const snackbar = document.createElement('rel-snackbar') as any;
750
+ snackbar.message = 'This is a snackbar message';
751
+ snackbar.duration = 0;
752
+
753
+ return snackbar;
754
+ })()}
755
+ </div>
756
+
757
+ <div>
758
+ <h4 style="margin: 0 0 12px; color: var(--rel-colors-gray-heading); font-size: 16px;">With Action</h4>
759
+ ${(() => {
760
+ const snackbar = document.createElement('rel-snackbar') as any;
761
+ snackbar.message = 'Connection lost';
762
+ snackbar.action = 'Retry';
763
+ snackbar.duration = 0;
764
+
765
+ return snackbar;
766
+ })()}
767
+ </div>
768
+
769
+ <div>
770
+ <h4 style="margin: 0 0 12px; color: var(--rel-colors-gray-heading); font-size: 16px;">Without Close Icon</h4>
771
+ ${(() => {
772
+ const snackbar = document.createElement('rel-snackbar') as any;
773
+ snackbar.message = 'Message sent successfully';
774
+ snackbar.iconClose = false;
775
+ snackbar.duration = 0;
776
+
777
+ return snackbar;
778
+ })()}
779
+ </div>
780
+
781
+ <div>
782
+ <h4 style="margin: 0 0 12px; color: var(--rel-colors-gray-heading); font-size: 16px;">Action Only</h4>
783
+ ${(() => {
784
+ const snackbar = document.createElement('rel-snackbar') as any;
785
+ snackbar.message = 'Your changes have been saved';
786
+ snackbar.action = 'Undo';
787
+ snackbar.iconClose = false;
788
+ snackbar.duration = 0;
789
+
790
+ return snackbar;
791
+ })()}
792
+ </div>
793
+
794
+ <div>
795
+ <h4 style="margin: 0 0 12px; color: var(--rel-colors-gray-heading); font-size: 16px;">Double Line</h4>
796
+ ${(() => {
797
+ const snackbar = document.createElement('rel-snackbar') as any;
798
+ snackbar.message = 'This is a longer snackbar message that needs more space';
799
+ snackbar.action = 'View Details';
800
+ snackbar.lines = 'double';
801
+ snackbar.duration = 0;
802
+
803
+ return snackbar;
804
+ })()}
805
+ </div>
806
+ </div>
807
+ `,
808
+ };