@pure-ds/storybook 0.3.19 → 0.4.0

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 (39) hide show
  1. package/.storybook/addons/pds-configurator/SearchTool.js +44 -44
  2. package/dist/pds-reference.json +65 -13
  3. package/package.json +50 -50
  4. package/stories/components/PdsCalendar.stories.js +266 -263
  5. package/stories/components/PdsDrawer.stories.js +2 -2
  6. package/stories/components/PdsIcon.stories.js +2 -2
  7. package/stories/components/PdsJsonform.stories.js +2 -2
  8. package/stories/components/PdsRichtext.stories.js +367 -367
  9. package/stories/components/PdsScrollrow.stories.js +140 -140
  10. package/stories/components/PdsSplitpanel.stories.js +502 -502
  11. package/stories/components/PdsTabstrip.stories.js +2 -2
  12. package/stories/components/PdsToaster.stories.js +186 -186
  13. package/stories/components/PdsUpload.stories.js +66 -66
  14. package/stories/enhancements/Dropdowns.stories.js +185 -185
  15. package/stories/enhancements/InteractiveStates.stories.js +625 -625
  16. package/stories/enhancements/MeshGradients.stories.js +321 -320
  17. package/stories/enhancements/OpenGroups.stories.js +227 -227
  18. package/stories/enhancements/RangeSliders.stories.js +232 -232
  19. package/stories/enhancements/RequiredFields.stories.js +189 -189
  20. package/stories/enhancements/Toggles.stories.js +167 -167
  21. package/stories/foundations/Colors.stories.js +2 -1
  22. package/stories/foundations/Icons.stories.js +4 -0
  23. package/stories/foundations/SmartSurfaces.stories.js +485 -367
  24. package/stories/foundations/Spacing.stories.js +5 -1
  25. package/stories/foundations/Typography.stories.js +4 -0
  26. package/stories/foundations/ZIndex.stories.js +329 -325
  27. package/stories/layout/LayoutOverview.stories.js +247 -0
  28. package/stories/layout/LayoutSystem.stories.js +852 -0
  29. package/stories/patterns/BorderEffects.stories.js +74 -72
  30. package/stories/primitives/Accordion.stories.js +5 -3
  31. package/stories/primitives/Alerts.stories.js +261 -46
  32. package/stories/primitives/Badges.stories.js +4 -0
  33. package/stories/primitives/Buttons.stories.js +2 -2
  34. package/stories/primitives/Cards.stories.js +98 -170
  35. package/stories/primitives/Media.stories.js +442 -203
  36. package/stories/primitives/Tables.stories.js +358 -232
  37. package/stories/utilities/Backdrop.stories.js +197 -0
  38. package/stories/patterns/Layout.stories.js +0 -99
  39. package/stories/utilities/GridSystem.stories.js +0 -208
@@ -1,625 +1,625 @@
1
- import { html } from 'lit';
2
-
3
- const interactiveSkeletonStoryStyles = html`
4
- <style>
5
- .interactive-skeleton-card {
6
- display: grid;
7
- gap: var(--spacing-3);
8
- }
9
-
10
- .interactive-skeleton-block {
11
- display: block;
12
- }
13
-
14
- .interactive-skeleton-height-24 {
15
- height: 1.5rem;
16
- }
17
-
18
- .interactive-skeleton-height-20 {
19
- height: 1.25rem;
20
- }
21
-
22
- .interactive-skeleton-height-16 {
23
- height: 1rem;
24
- }
25
-
26
- .interactive-skeleton-height-14 {
27
- height: 0.875rem;
28
- }
29
-
30
- .interactive-skeleton-width-60 {
31
- width: 60%;
32
- }
33
-
34
- .interactive-skeleton-width-80 {
35
- width: 80%;
36
- }
37
-
38
- .interactive-skeleton-width-70 {
39
- width: 70%;
40
- }
41
-
42
- .interactive-skeleton-width-50 {
43
- width: 50%;
44
- }
45
-
46
- .interactive-skeleton-width-40 {
47
- width: 40%;
48
- }
49
-
50
- .interactive-skeleton-width-85 {
51
- width: 85%;
52
- }
53
-
54
- .interactive-skeleton-margin-lg {
55
- margin-bottom: var(--spacing-3);
56
- }
57
-
58
- .interactive-skeleton-margin-md {
59
- margin-bottom: var(--spacing-2);
60
- }
61
-
62
- .interactive-skeleton-avatar {
63
- width: 2.5rem;
64
- height: 2.5rem;
65
- border-radius: 50%;
66
- }
67
-
68
- .interactive-skeleton-list-item {
69
- padding: var(--spacing-3);
70
- }
71
-
72
- .interactive-skeleton-details {
73
- flex: 1;
74
- }
75
- </style>
76
- `;
77
-
78
- export default {
79
- title: 'Enhancements/Interactive States',
80
- tags: ['buttons', 'interaction'],
81
- parameters: {
82
- pds: {
83
- tags: ['interaction', 'accessibility', 'feedback']
84
- },
85
- docs: {
86
- description: {
87
- component: 'Interactive states including focus rings, hover effects, active states, disabled states, and working/loading states. All animations respect user preferences and accessibility settings.'
88
- }
89
- }
90
- }
91
- };
92
-
93
- export const FocusStates = () => html`
94
- <div class="card">
95
- <h2>Focus States</h2>
96
- <p>Press <kbd>Tab</kbd> to navigate and see focus rings on interactive elements</p>
97
-
98
- <div class="flex flex-wrap gap-sm align-center">
99
- <button class="btn-primary">Button 1</button>
100
- <button class="btn-secondary">Button 2</button>
101
- <button class="btn-outline">Button 3</button>
102
- <input type="text" placeholder="Focus me" />
103
- <select>
104
- <option>Option 1</option>
105
- <option>Option 2</option>
106
- <option>Option 3</option>
107
- </select>
108
- <a href="#" onclick="event.preventDefault();">Link Example</a>
109
- </div>
110
- </div>
111
-
112
- <div class="card">
113
- <h3>Form Controls</h3>
114
- <div class="max-w-md">
115
- <label>
116
- <span>Text Input</span>
117
- <input type="text" placeholder="Tab to focus" />
118
- </label>
119
-
120
- <label>
121
- <span>Textarea</span>
122
- <textarea rows="3" placeholder="Tab to focus"></textarea>
123
- </label>
124
-
125
- <fieldset role="group">
126
- <legend>Options</legend>
127
- <label>
128
- <input type="checkbox" />
129
- <span>Checkbox option 1</span>
130
- </label>
131
- <label>
132
- <input type="checkbox" checked />
133
- <span>Checkbox option 2</span>
134
- </label>
135
- </fieldset>
136
-
137
- <fieldset role="radiogroup">
138
- <legend>Choice</legend>
139
- <label>
140
- <input type="radio" name="choice" value="a" checked />
141
- <span>Radio option A</span>
142
- </label>
143
- <label>
144
- <input type="radio" name="choice" value="b" />
145
- <span>Radio option B</span>
146
- </label>
147
- </fieldset>
148
- </div>
149
- </div>
150
- `;
151
-
152
- FocusStates.storyName = 'Focus States';
153
-
154
- export const HoverStates = () => html`
155
- <div class="card">
156
- <h2>Hover States</h2>
157
- <p>Hover over elements to see smooth transitions and state changes</p>
158
-
159
- <h3>Buttons</h3>
160
- <div class="flex flex-wrap gap-sm">
161
- <button class="btn-primary">Hover Me</button>
162
- <button class="btn-secondary">Hover Me</button>
163
- <button class="btn-outline">Hover Me</button>
164
- <button class="btn-primary btn-sm">Small</button>
165
- <button class="btn-primary btn-lg">Large</button>
166
- </div>
167
- </div>
168
-
169
- <div class="card">
170
- <h3>Icon Buttons</h3>
171
- <div class="flex flex-wrap gap-sm">
172
- <button class="btn-primary icon-only" aria-label="Settings">
173
- <pds-icon icon="gear"></pds-icon>
174
- </button>
175
- <button class="btn-secondary icon-only" aria-label="Search">
176
- <pds-icon icon="magnifying-glass"></pds-icon>
177
- </button>
178
- <button class="btn-outline icon-only" aria-label="Heart">
179
- <pds-icon icon="heart"></pds-icon>
180
- </button>
181
- </div>
182
- </div>
183
-
184
- <div class="card">
185
- <h3>Buttons with Icons</h3>
186
- <div class="flex flex-wrap gap-sm">
187
- <button class="btn-primary">
188
- <pds-icon icon="plus" size="sm"></pds-icon>
189
- Add Item
190
- </button>
191
- <button class="btn-secondary">
192
- <pds-icon icon="download" size="sm"></pds-icon>
193
- Download
194
- </button>
195
- <button class="btn-outline">
196
- <pds-icon icon="share" size="sm"></pds-icon>
197
- Share
198
- </button>
199
- </div>
200
- </div>
201
-
202
- <div class="card">
203
- <h3>Links</h3>
204
- <div class="flex flex-wrap gap-md">
205
- <a href="#" onclick="event.preventDefault();">Text Link</a>
206
- <a href="#" onclick="event.preventDefault();">
207
- <pds-icon icon="arrow-right" size="sm"></pds-icon>
208
- Link with Icon
209
- </a>
210
- </div>
211
- </div>
212
- `;
213
-
214
- HoverStates.storyName = 'Hover States';
215
-
216
- export const ActiveStates = () => html`
217
- <div class="card">
218
- <h2>Active States</h2>
219
- <p>Click and hold to see active/pressed states</p>
220
-
221
- <h3>Buttons</h3>
222
- <div class="flex flex-wrap gap-sm">
223
- <button class="btn-primary">Click and Hold</button>
224
- <button class="btn-secondary">Click and Hold</button>
225
- <button class="btn-outline">Click and Hold</button>
226
- </div>
227
- </div>
228
-
229
- <div class="card">
230
- <h3>Icon Buttons</h3>
231
- <div class="flex flex-wrap gap-sm">
232
- <button class="btn-primary icon-only" aria-label="Like">
233
- <pds-icon icon="heart"></pds-icon>
234
- </button>
235
- <button class="btn-secondary icon-only" aria-label="Bookmark">
236
- <pds-icon icon="bookmark"></pds-icon>
237
- </button>
238
- <button class="btn-outline icon-only" aria-label="Star">
239
- <pds-icon icon="star"></pds-icon>
240
- </button>
241
- </div>
242
- </div>
243
- `;
244
-
245
- ActiveStates.storyName = 'Active States';
246
-
247
- export const TransitionSpeeds = () => {
248
- const triggerAnimation = (speed) => {
249
- const ball = document.getElementById(`ball-${speed}`);
250
- if (!ball || ball.classList.contains('animating')) return;
251
-
252
- ball.classList.add('animating');
253
- ball.style.transition = `transform var(--transition-${speed})`;
254
- ball.style.transform = 'translateX(250px)';
255
-
256
- const duration = speed === 'fast' ? 150 : speed === 'slow' ? 500 : 250;
257
-
258
- setTimeout(() => {
259
- ball.style.transform = 'translateX(0)';
260
- setTimeout(() => {
261
- ball.classList.remove('animating');
262
- }, duration);
263
- }, duration);
264
- };
265
-
266
- return html`
267
- <div class="card">
268
- <h2>Transition Speeds</h2>
269
- <p>The design system provides three transition speed tokens that can be configured globally.</p>
270
-
271
- <div class="grid gap-lg">
272
- ${['fast', 'normal', 'slow'].map(speed => html`
273
- <div>
274
- <h3>--transition-${speed}</h3>
275
- <button class="btn-primary btn-sm" @click=${() => triggerAnimation(speed)}>
276
- <pds-icon icon="play" size="sm"></pds-icon>
277
- Animate ${speed}
278
- </button>
279
- <div class="card surface-subtle">
280
- <div id="ball-${speed}" class="badge badge-primary radius-full shadow-md">
281
- <pds-icon icon="cursor-click" size="sm"></pds-icon>
282
- </div>
283
- </div>
284
- </div>
285
- `)}
286
- </div>
287
- </div>
288
-
289
- <div class="card">
290
- <h3>Code Example</h3>
291
- <pre class="surface-subtle radius-md overflow-auto"><code>/* Use transition tokens in your CSS */
292
- .button {
293
- transition: background-color var(--transition-fast);
294
- }
295
-
296
- .card {
297
- transition:
298
- transform var(--transition-normal),
299
- box-shadow var(--transition-normal);
300
- }
301
-
302
- .modal {
303
- transition: opacity var(--transition-slow);
304
- }</code></pre>
305
- </div>
306
- `;
307
- };
308
-
309
- TransitionSpeeds.storyName = 'Transition Speeds';
310
-
311
- export const WorkingStates = () => {
312
- const toggleWorking = (btn) => {
313
- btn.classList.add('btn-working');
314
- setTimeout(() => {
315
- btn.classList.remove('btn-working');
316
- }, 2000);
317
- };
318
-
319
- return html`
320
- <div class="card">
321
- <h2>Working/Loading States</h2>
322
- <p>
323
- Click buttons to see the <code>.btn-working</code> state with automatic spinner animation.
324
- The PDS enhancer automatically swaps existing icons to spinners or adds a spinner if none exists.
325
- </p>
326
-
327
- <h3>Buttons Without Icons</h3>
328
- <p class="text-muted text-sm">Enhancer automatically adds spinner icon</p>
329
- <div class="flex flex-wrap gap-sm">
330
- <button class="btn-primary" @click=${(e) => toggleWorking(e.target)}>
331
- Save
332
- </button>
333
- <button class="btn-secondary" @click=${(e) => toggleWorking(e.target)}>
334
- Upload
335
- </button>
336
- <button class="btn-outline" @click=${(e) => toggleWorking(e.target)}>
337
- Download
338
- </button>
339
- </div>
340
- </div>
341
-
342
- <div class="card">
343
- <h3>Buttons With Existing Icons</h3>
344
- <p class="text-muted text-sm">Enhancer swaps icon to spinner, restores original when complete</p>
345
- <div class="flex flex-wrap gap-sm">
346
- <button class="btn-primary" @click=${(e) => toggleWorking(e.target)}>
347
- <pds-icon icon="floppy-disk" size="sm"></pds-icon>
348
- Save
349
- </button>
350
- <button class="btn-secondary" @click=${(e) => toggleWorking(e.target)}>
351
- <pds-icon icon="upload" size="sm"></pds-icon>
352
- Upload
353
- </button>
354
- <button class="btn-outline" @click=${(e) => toggleWorking(e.target)}>
355
- <pds-icon icon="download" size="sm"></pds-icon>
356
- Download
357
- </button>
358
- </div>
359
- </div>
360
-
361
- <div class="card">
362
- <h3>Icon Buttons</h3>
363
- <p class="text-muted text-sm">Icon-only buttons with automatic spinner swap</p>
364
- <div class="flex flex-wrap gap-sm">
365
- <button class="btn-primary icon-only" @click=${(e) => toggleWorking(e.target)} aria-label="Refresh">
366
- <pds-icon icon="arrow-counter-clockwise"></pds-icon>
367
- </button>
368
- <button class="btn-secondary icon-only" @click=${(e) => toggleWorking(e.target)} aria-label="Sync">
369
- <pds-icon icon="arrow-counter-clockwise"></pds-icon>
370
- </button>
371
- <button class="btn-outline icon-only" @click=${(e) => toggleWorking(e.target)} aria-label="Process">
372
- <pds-icon icon="gear"></pds-icon>
373
- </button>
374
- </div>
375
- </div>
376
-
377
- <div class="card">
378
- <h3>Different Sizes</h3>
379
- <p class="text-muted text-sm">Spinner size adapts to button size</p>
380
- <div class="flex flex-wrap gap-sm align-center">
381
- <button class="btn-primary btn-sm" @click=${(e) => toggleWorking(e.target)}>
382
- <pds-icon icon="paper-plane-tilt" size="sm"></pds-icon>
383
- Small
384
- </button>
385
- <button class="btn-primary" @click=${(e) => toggleWorking(e.target)}>
386
- <pds-icon icon="paper-plane-tilt" size="sm"></pds-icon>
387
- Default
388
- </button>
389
- <button class="btn-primary btn-lg" @click=${(e) => toggleWorking(e.target)}>
390
- <pds-icon icon="paper-plane-tilt"></pds-icon>
391
- Large
392
- </button>
393
- </div>
394
- </div>
395
-
396
- <div class="card">
397
- <h3>Permanent Working State</h3>
398
- <p class="text-muted">Buttons in continuous working state</p>
399
- <div class="flex flex-wrap gap-sm">
400
- <button class="btn-primary btn-working">
401
- <pds-icon icon="circle-notch" size="sm"></pds-icon>
402
- Loading...
403
- </button>
404
- <button class="btn-secondary btn-working">
405
- <pds-icon icon="circle-notch" size="sm"></pds-icon>
406
- Processing...
407
- </button>
408
- <button class="btn-outline icon-only btn-working" aria-label="Loading">
409
- <pds-icon icon="circle-notch"></pds-icon>
410
- </button>
411
- </div>
412
- </div>
413
-
414
- <div class="card">
415
- <h3>Usage</h3>
416
- <pre class="bg-surface-subtle radius-md overflow-auto"><code>// Simply toggle the class - PDS handles the rest
417
- button.classList.add('btn-working');
418
-
419
- // After async operation completes
420
- button.classList.remove('btn-working');</code></pre>
421
- </div>
422
- `;
423
- };
424
-
425
- WorkingStates.storyName = 'Working States';
426
-
427
- export const SkeletonLoading = () => html`
428
- ${interactiveSkeletonStoryStyles}
429
- <div class="card">
430
- <h2>Skeleton Loading</h2>
431
- <p>Use the <code>.skeleton</code> class for content placeholders while loading</p>
432
-
433
- <h3>Card Skeleton</h3>
434
- <div class="max-w-xl">
435
- <div class="card interactive-skeleton-card">
436
- <div class="skeleton interactive-skeleton-block interactive-skeleton-height-24 interactive-skeleton-width-60 interactive-skeleton-margin-lg"></div>
437
- <div class="skeleton interactive-skeleton-block interactive-skeleton-height-16 interactive-skeleton-margin-md"></div>
438
- <div class="skeleton interactive-skeleton-block interactive-skeleton-height-16 interactive-skeleton-margin-md"></div>
439
- <div class="skeleton interactive-skeleton-block interactive-skeleton-height-16 interactive-skeleton-width-80"></div>
440
- </div>
441
- </div>
442
- </div>
443
-
444
- <div class="card">
445
- <h3>List Skeleton</h3>
446
- <div class="max-w-md">
447
- ${Array.from({ length: 4 }, () => html`
448
- <div class="flex gap-sm align-center border-bottom interactive-skeleton-list-item">
449
- <div class="skeleton interactive-skeleton-avatar"></div>
450
- <div class="interactive-skeleton-details">
451
- <div class="skeleton interactive-skeleton-block interactive-skeleton-height-16 interactive-skeleton-width-70 interactive-skeleton-margin-md"></div>
452
- <div class="skeleton interactive-skeleton-block interactive-skeleton-height-14 interactive-skeleton-width-50"></div>
453
- </div>
454
- </div>
455
- `)}
456
- </div>
457
- </div>
458
-
459
- <div class="card">
460
- <h3>Text Skeleton</h3>
461
- <div class="max-w-lg">
462
- <div class="skeleton interactive-skeleton-block interactive-skeleton-height-20 interactive-skeleton-width-40 interactive-skeleton-margin-lg"></div>
463
- <div class="skeleton interactive-skeleton-block interactive-skeleton-height-16 interactive-skeleton-margin-md"></div>
464
- <div class="skeleton interactive-skeleton-block interactive-skeleton-height-16 interactive-skeleton-margin-md"></div>
465
- <div class="skeleton interactive-skeleton-block interactive-skeleton-height-16 interactive-skeleton-margin-md"></div>
466
- <div class="skeleton interactive-skeleton-block interactive-skeleton-height-16 interactive-skeleton-width-85"></div>
467
- </div>
468
- </div>
469
- `;
470
-
471
- SkeletonLoading.storyName = 'Skeleton Loading';
472
-
473
- export const DisabledStates = () => html`
474
- <div class="card">
475
- <h2>Disabled States</h2>
476
- <p>Disabled elements have reduced opacity and no pointer events</p>
477
-
478
- <h3>Buttons</h3>
479
- <div class="flex flex-wrap gap-sm">
480
- <button class="btn-primary" disabled>Primary Disabled</button>
481
- <button class="btn-secondary" disabled>Secondary Disabled</button>
482
- <button class="btn-outline" disabled>Outline Disabled</button>
483
- </div>
484
- </div>
485
-
486
- <div class="card">
487
- <h3>Icon Buttons</h3>
488
- <div class="flex flex-wrap gap-sm">
489
- <button class="btn-primary icon-only" disabled aria-label="Settings">
490
- <pds-icon icon="gear"></pds-icon>
491
- </button>
492
- <button class="btn-secondary icon-only" disabled aria-label="Search">
493
- <pds-icon icon="magnifying-glass"></pds-icon>
494
- </button>
495
- <button class="btn-outline icon-only" disabled aria-label="Heart">
496
- <pds-icon icon="heart"></pds-icon>
497
- </button>
498
- </div>
499
- </div>
500
-
501
- <div class="card">
502
- <h3>Buttons with Icons</h3>
503
- <div class="flex flex-wrap gap-sm">
504
- <button class="btn-primary" disabled>
505
- <pds-icon icon="plus" size="sm"></pds-icon>
506
- Add Item
507
- </button>
508
- <button class="btn-secondary" disabled>
509
- <pds-icon icon="download" size="sm"></pds-icon>
510
- Download
511
- </button>
512
- </div>
513
- </div>
514
-
515
- <div class="card">
516
- <h3>Form Controls</h3>
517
- <div class="max-w-md">
518
- <label>
519
- <span>Disabled Input</span>
520
- <input type="text" disabled value="Cannot edit" />
521
- </label>
522
-
523
- <label>
524
- <span>Disabled Select</span>
525
- <select disabled>
526
- <option>Cannot select</option>
527
- </select>
528
- </label>
529
-
530
- <label>
531
- <span>Disabled Textarea</span>
532
- <textarea disabled rows="3">Cannot edit this text</textarea>
533
- </label>
534
-
535
- <fieldset role="group">
536
- <legend>Disabled Checkboxes</legend>
537
- <label>
538
- <input type="checkbox" disabled />
539
- <span>Disabled unchecked</span>
540
- </label>
541
- <label>
542
- <input type="checkbox" disabled checked />
543
- <span>Disabled checked</span>
544
- </label>
545
- </fieldset>
546
- </div>
547
- </div>
548
- `;
549
-
550
- DisabledStates.storyName = 'Disabled States';
551
-
552
- export const CombinedStates = () => html`
553
- <div class="card">
554
- <h2>Combined State Examples</h2>
555
- <p>Comprehensive examples showing all interactive states together</p>
556
- </div>
557
-
558
- <div class="card">
559
- <h3>Idle State</h3>
560
- <div class="flex flex-wrap gap-sm">
561
- <button class="btn-primary">Primary</button>
562
- <button class="btn-secondary">Secondary</button>
563
- <button class="btn-outline">Outline</button>
564
- </div>
565
- </div>
566
-
567
- <div class="card">
568
- <h3>Hover State</h3>
569
- <p class="text-muted text-sm">Hover over buttons to see effect</p>
570
- <div class="flex flex-wrap gap-sm">
571
- <button class="btn-primary">Primary</button>
572
- <button class="btn-secondary">Secondary</button>
573
- <button class="btn-outline">Outline</button>
574
- </div>
575
- </div>
576
-
577
- <div class="card">
578
- <h3>Active State</h3>
579
- <p class="text-muted text-sm">Click and hold to see effect</p>
580
- <div class="flex flex-wrap gap-sm">
581
- <button class="btn-primary">Primary</button>
582
- <button class="btn-secondary">Secondary</button>
583
- <button class="btn-outline">Outline</button>
584
- </div>
585
- </div>
586
-
587
- <div class="card">
588
- <h3>Focus State</h3>
589
- <p class="text-muted text-sm">Tab to focus on buttons</p>
590
- <div class="flex flex-wrap gap-sm">
591
- <button class="btn-primary">Primary</button>
592
- <button class="btn-secondary">Secondary</button>
593
- <button class="btn-outline">Outline</button>
594
- </div>
595
- </div>
596
-
597
- <div class="card">
598
- <h3>Disabled State</h3>
599
- <div class="flex flex-wrap gap-sm">
600
- <button class="btn-primary" disabled>Primary</button>
601
- <button class="btn-secondary" disabled>Secondary</button>
602
- <button class="btn-outline" disabled>Outline</button>
603
- </div>
604
- </div>
605
-
606
- <div class="card">
607
- <h3>Working State</h3>
608
- <div class="flex flex-wrap gap-sm">
609
- <button class="btn-primary btn-working">
610
- <pds-icon icon="circle-notch" size="sm"></pds-icon>
611
- Primary
612
- </button>
613
- <button class="btn-secondary btn-working">
614
- <pds-icon icon="circle-notch" size="sm"></pds-icon>
615
- Secondary
616
- </button>
617
- <button class="btn-outline btn-working">
618
- <pds-icon icon="circle-notch" size="sm"></pds-icon>
619
- Outline
620
- </button>
621
- </div>
622
- </div>
623
- `;
624
-
625
- CombinedStates.storyName = 'All States';
1
+ import { html } from 'lit';
2
+
3
+ const interactiveSkeletonStoryStyles = html`
4
+ <style>
5
+ .interactive-skeleton-card {
6
+ display: grid;
7
+ gap: var(--spacing-3);
8
+ }
9
+
10
+ .interactive-skeleton-block {
11
+ display: block;
12
+ }
13
+
14
+ .interactive-skeleton-height-24 {
15
+ height: 1.5rem;
16
+ }
17
+
18
+ .interactive-skeleton-height-20 {
19
+ height: 1.25rem;
20
+ }
21
+
22
+ .interactive-skeleton-height-16 {
23
+ height: 1rem;
24
+ }
25
+
26
+ .interactive-skeleton-height-14 {
27
+ height: 0.875rem;
28
+ }
29
+
30
+ .interactive-skeleton-width-60 {
31
+ width: 60%;
32
+ }
33
+
34
+ .interactive-skeleton-width-80 {
35
+ width: 80%;
36
+ }
37
+
38
+ .interactive-skeleton-width-70 {
39
+ width: 70%;
40
+ }
41
+
42
+ .interactive-skeleton-width-50 {
43
+ width: 50%;
44
+ }
45
+
46
+ .interactive-skeleton-width-40 {
47
+ width: 40%;
48
+ }
49
+
50
+ .interactive-skeleton-width-85 {
51
+ width: 85%;
52
+ }
53
+
54
+ .interactive-skeleton-margin-lg {
55
+ margin-bottom: var(--spacing-3);
56
+ }
57
+
58
+ .interactive-skeleton-margin-md {
59
+ margin-bottom: var(--spacing-2);
60
+ }
61
+
62
+ .interactive-skeleton-avatar {
63
+ width: 2.5rem;
64
+ height: 2.5rem;
65
+ border-radius: 50%;
66
+ }
67
+
68
+ .interactive-skeleton-list-item {
69
+ padding: var(--spacing-3);
70
+ }
71
+
72
+ .interactive-skeleton-details {
73
+ flex: 1;
74
+ }
75
+ </style>
76
+ `;
77
+
78
+ export default {
79
+ title: 'Enhancements/Interactive States',
80
+ tags: ['hover', 'focus', 'active', 'disabled', 'loading', 'interaction'],
81
+ parameters: {
82
+ pds: {
83
+ tags: ['hover', 'focus', 'active', 'disabled', 'loading', 'working', 'interaction', 'accessibility', 'feedback', 'state']
84
+ },
85
+ docs: {
86
+ description: {
87
+ component: 'Interactive states including focus rings, hover effects, active states, disabled states, and working/loading states. All animations respect user preferences and accessibility settings.'
88
+ }
89
+ }
90
+ }
91
+ };
92
+
93
+ export const FocusStates = () => html`
94
+ <div class="card">
95
+ <h2>Focus States</h2>
96
+ <p>Press <kbd>Tab</kbd> to navigate and see focus rings on interactive elements</p>
97
+
98
+ <div class="flex flex-wrap gap-sm align-center">
99
+ <button class="btn-primary">Button 1</button>
100
+ <button class="btn-secondary">Button 2</button>
101
+ <button class="btn-outline">Button 3</button>
102
+ <input type="text" placeholder="Focus me" />
103
+ <select>
104
+ <option>Option 1</option>
105
+ <option>Option 2</option>
106
+ <option>Option 3</option>
107
+ </select>
108
+ <a href="#" onclick="event.preventDefault();">Link Example</a>
109
+ </div>
110
+ </div>
111
+
112
+ <div class="card">
113
+ <h3>Form Controls</h3>
114
+ <div class="max-w-md">
115
+ <label>
116
+ <span>Text Input</span>
117
+ <input type="text" placeholder="Tab to focus" />
118
+ </label>
119
+
120
+ <label>
121
+ <span>Textarea</span>
122
+ <textarea rows="3" placeholder="Tab to focus"></textarea>
123
+ </label>
124
+
125
+ <fieldset role="group">
126
+ <legend>Options</legend>
127
+ <label>
128
+ <input type="checkbox" />
129
+ <span>Checkbox option 1</span>
130
+ </label>
131
+ <label>
132
+ <input type="checkbox" checked />
133
+ <span>Checkbox option 2</span>
134
+ </label>
135
+ </fieldset>
136
+
137
+ <fieldset role="radiogroup">
138
+ <legend>Choice</legend>
139
+ <label>
140
+ <input type="radio" name="choice" value="a" checked />
141
+ <span>Radio option A</span>
142
+ </label>
143
+ <label>
144
+ <input type="radio" name="choice" value="b" />
145
+ <span>Radio option B</span>
146
+ </label>
147
+ </fieldset>
148
+ </div>
149
+ </div>
150
+ `;
151
+
152
+ FocusStates.storyName = 'Focus States';
153
+
154
+ export const HoverStates = () => html`
155
+ <div class="card">
156
+ <h2>Hover States</h2>
157
+ <p>Hover over elements to see smooth transitions and state changes</p>
158
+
159
+ <h3>Buttons</h3>
160
+ <div class="flex flex-wrap gap-sm">
161
+ <button class="btn-primary">Hover Me</button>
162
+ <button class="btn-secondary">Hover Me</button>
163
+ <button class="btn-outline">Hover Me</button>
164
+ <button class="btn-primary btn-sm">Small</button>
165
+ <button class="btn-primary btn-lg">Large</button>
166
+ </div>
167
+ </div>
168
+
169
+ <div class="card">
170
+ <h3>Icon Buttons</h3>
171
+ <div class="flex flex-wrap gap-sm">
172
+ <button class="btn-primary icon-only" aria-label="Settings">
173
+ <pds-icon icon="gear"></pds-icon>
174
+ </button>
175
+ <button class="btn-secondary icon-only" aria-label="Search">
176
+ <pds-icon icon="magnifying-glass"></pds-icon>
177
+ </button>
178
+ <button class="btn-outline icon-only" aria-label="Heart">
179
+ <pds-icon icon="heart"></pds-icon>
180
+ </button>
181
+ </div>
182
+ </div>
183
+
184
+ <div class="card">
185
+ <h3>Buttons with Icons</h3>
186
+ <div class="flex flex-wrap gap-sm">
187
+ <button class="btn-primary">
188
+ <pds-icon icon="plus" size="sm"></pds-icon>
189
+ Add Item
190
+ </button>
191
+ <button class="btn-secondary">
192
+ <pds-icon icon="download" size="sm"></pds-icon>
193
+ Download
194
+ </button>
195
+ <button class="btn-outline">
196
+ <pds-icon icon="share" size="sm"></pds-icon>
197
+ Share
198
+ </button>
199
+ </div>
200
+ </div>
201
+
202
+ <div class="card">
203
+ <h3>Links</h3>
204
+ <div class="flex flex-wrap gap-md">
205
+ <a href="#" onclick="event.preventDefault();">Text Link</a>
206
+ <a href="#" onclick="event.preventDefault();">
207
+ <pds-icon icon="arrow-right" size="sm"></pds-icon>
208
+ Link with Icon
209
+ </a>
210
+ </div>
211
+ </div>
212
+ `;
213
+
214
+ HoverStates.storyName = 'Hover States';
215
+
216
+ export const ActiveStates = () => html`
217
+ <div class="card">
218
+ <h2>Active States</h2>
219
+ <p>Click and hold to see active/pressed states</p>
220
+
221
+ <h3>Buttons</h3>
222
+ <div class="flex flex-wrap gap-sm">
223
+ <button class="btn-primary">Click and Hold</button>
224
+ <button class="btn-secondary">Click and Hold</button>
225
+ <button class="btn-outline">Click and Hold</button>
226
+ </div>
227
+ </div>
228
+
229
+ <div class="card">
230
+ <h3>Icon Buttons</h3>
231
+ <div class="flex flex-wrap gap-sm">
232
+ <button class="btn-primary icon-only" aria-label="Like">
233
+ <pds-icon icon="heart"></pds-icon>
234
+ </button>
235
+ <button class="btn-secondary icon-only" aria-label="Bookmark">
236
+ <pds-icon icon="bookmark"></pds-icon>
237
+ </button>
238
+ <button class="btn-outline icon-only" aria-label="Star">
239
+ <pds-icon icon="star"></pds-icon>
240
+ </button>
241
+ </div>
242
+ </div>
243
+ `;
244
+
245
+ ActiveStates.storyName = 'Active States';
246
+
247
+ export const TransitionSpeeds = () => {
248
+ const triggerAnimation = (speed) => {
249
+ const ball = document.getElementById(`ball-${speed}`);
250
+ if (!ball || ball.classList.contains('animating')) return;
251
+
252
+ ball.classList.add('animating');
253
+ ball.style.transition = `transform var(--transition-${speed})`;
254
+ ball.style.transform = 'translateX(250px)';
255
+
256
+ const duration = speed === 'fast' ? 150 : speed === 'slow' ? 500 : 250;
257
+
258
+ setTimeout(() => {
259
+ ball.style.transform = 'translateX(0)';
260
+ setTimeout(() => {
261
+ ball.classList.remove('animating');
262
+ }, duration);
263
+ }, duration);
264
+ };
265
+
266
+ return html`
267
+ <div class="card">
268
+ <h2>Transition Speeds</h2>
269
+ <p>The design system provides three transition speed tokens that can be configured globally.</p>
270
+
271
+ <div class="grid gap-lg">
272
+ ${['fast', 'normal', 'slow'].map(speed => html`
273
+ <div>
274
+ <h3>--transition-${speed}</h3>
275
+ <button class="btn-primary btn-sm" @click=${() => triggerAnimation(speed)}>
276
+ <pds-icon icon="play" size="sm"></pds-icon>
277
+ Animate ${speed}
278
+ </button>
279
+ <div class="card surface-subtle">
280
+ <div id="ball-${speed}" class="badge badge-primary radius-full shadow-md">
281
+ <pds-icon icon="cursor-click" size="sm"></pds-icon>
282
+ </div>
283
+ </div>
284
+ </div>
285
+ `)}
286
+ </div>
287
+ </div>
288
+
289
+ <div class="card">
290
+ <h3>Code Example</h3>
291
+ <pre class="surface-subtle radius-md overflow-auto"><code>/* Use transition tokens in your CSS */
292
+ .button {
293
+ transition: background-color var(--transition-fast);
294
+ }
295
+
296
+ .card {
297
+ transition:
298
+ transform var(--transition-normal),
299
+ box-shadow var(--transition-normal);
300
+ }
301
+
302
+ .modal {
303
+ transition: opacity var(--transition-slow);
304
+ }</code></pre>
305
+ </div>
306
+ `;
307
+ };
308
+
309
+ TransitionSpeeds.storyName = 'Transition Speeds';
310
+
311
+ export const WorkingStates = () => {
312
+ const toggleWorking = (btn) => {
313
+ btn.classList.add('btn-working');
314
+ setTimeout(() => {
315
+ btn.classList.remove('btn-working');
316
+ }, 2000);
317
+ };
318
+
319
+ return html`
320
+ <div class="card">
321
+ <h2>Working/Loading States</h2>
322
+ <p>
323
+ Click buttons to see the <code>.btn-working</code> state with automatic spinner animation.
324
+ The PDS enhancer automatically swaps existing icons to spinners or adds a spinner if none exists.
325
+ </p>
326
+
327
+ <h3>Buttons Without Icons</h3>
328
+ <p class="text-muted text-sm">Enhancer automatically adds spinner icon</p>
329
+ <div class="flex flex-wrap gap-sm">
330
+ <button class="btn-primary" @click=${(e) => toggleWorking(e.target)}>
331
+ Save
332
+ </button>
333
+ <button class="btn-secondary" @click=${(e) => toggleWorking(e.target)}>
334
+ Upload
335
+ </button>
336
+ <button class="btn-outline" @click=${(e) => toggleWorking(e.target)}>
337
+ Download
338
+ </button>
339
+ </div>
340
+ </div>
341
+
342
+ <div class="card">
343
+ <h3>Buttons With Existing Icons</h3>
344
+ <p class="text-muted text-sm">Enhancer swaps icon to spinner, restores original when complete</p>
345
+ <div class="flex flex-wrap gap-sm">
346
+ <button class="btn-primary" @click=${(e) => toggleWorking(e.target)}>
347
+ <pds-icon icon="floppy-disk" size="sm"></pds-icon>
348
+ Save
349
+ </button>
350
+ <button class="btn-secondary" @click=${(e) => toggleWorking(e.target)}>
351
+ <pds-icon icon="upload" size="sm"></pds-icon>
352
+ Upload
353
+ </button>
354
+ <button class="btn-outline" @click=${(e) => toggleWorking(e.target)}>
355
+ <pds-icon icon="download" size="sm"></pds-icon>
356
+ Download
357
+ </button>
358
+ </div>
359
+ </div>
360
+
361
+ <div class="card">
362
+ <h3>Icon Buttons</h3>
363
+ <p class="text-muted text-sm">Icon-only buttons with automatic spinner swap</p>
364
+ <div class="flex flex-wrap gap-sm">
365
+ <button class="btn-primary icon-only" @click=${(e) => toggleWorking(e.target)} aria-label="Refresh">
366
+ <pds-icon icon="arrow-counter-clockwise"></pds-icon>
367
+ </button>
368
+ <button class="btn-secondary icon-only" @click=${(e) => toggleWorking(e.target)} aria-label="Sync">
369
+ <pds-icon icon="arrow-counter-clockwise"></pds-icon>
370
+ </button>
371
+ <button class="btn-outline icon-only" @click=${(e) => toggleWorking(e.target)} aria-label="Process">
372
+ <pds-icon icon="gear"></pds-icon>
373
+ </button>
374
+ </div>
375
+ </div>
376
+
377
+ <div class="card">
378
+ <h3>Different Sizes</h3>
379
+ <p class="text-muted text-sm">Spinner size adapts to button size</p>
380
+ <div class="flex flex-wrap gap-sm align-center">
381
+ <button class="btn-primary btn-sm" @click=${(e) => toggleWorking(e.target)}>
382
+ <pds-icon icon="paper-plane-tilt" size="sm"></pds-icon>
383
+ Small
384
+ </button>
385
+ <button class="btn-primary" @click=${(e) => toggleWorking(e.target)}>
386
+ <pds-icon icon="paper-plane-tilt" size="sm"></pds-icon>
387
+ Default
388
+ </button>
389
+ <button class="btn-primary btn-lg" @click=${(e) => toggleWorking(e.target)}>
390
+ <pds-icon icon="paper-plane-tilt"></pds-icon>
391
+ Large
392
+ </button>
393
+ </div>
394
+ </div>
395
+
396
+ <div class="card">
397
+ <h3>Permanent Working State</h3>
398
+ <p class="text-muted">Buttons in continuous working state</p>
399
+ <div class="flex flex-wrap gap-sm">
400
+ <button class="btn-primary btn-working">
401
+ <pds-icon icon="circle-notch" size="sm"></pds-icon>
402
+ Loading...
403
+ </button>
404
+ <button class="btn-secondary btn-working">
405
+ <pds-icon icon="circle-notch" size="sm"></pds-icon>
406
+ Processing...
407
+ </button>
408
+ <button class="btn-outline icon-only btn-working" aria-label="Loading">
409
+ <pds-icon icon="circle-notch"></pds-icon>
410
+ </button>
411
+ </div>
412
+ </div>
413
+
414
+ <div class="card">
415
+ <h3>Usage</h3>
416
+ <pre class="bg-surface-subtle radius-md overflow-auto"><code>// Simply toggle the class - PDS handles the rest
417
+ button.classList.add('btn-working');
418
+
419
+ // After async operation completes
420
+ button.classList.remove('btn-working');</code></pre>
421
+ </div>
422
+ `;
423
+ };
424
+
425
+ WorkingStates.storyName = 'Working States';
426
+
427
+ export const SkeletonLoading = () => html`
428
+ ${interactiveSkeletonStoryStyles}
429
+ <div class="card">
430
+ <h2>Skeleton Loading</h2>
431
+ <p>Use the <code>.skeleton</code> class for content placeholders while loading</p>
432
+
433
+ <h3>Card Skeleton</h3>
434
+ <div class="max-w-xl">
435
+ <div class="card interactive-skeleton-card">
436
+ <div class="skeleton interactive-skeleton-block interactive-skeleton-height-24 interactive-skeleton-width-60 interactive-skeleton-margin-lg"></div>
437
+ <div class="skeleton interactive-skeleton-block interactive-skeleton-height-16 interactive-skeleton-margin-md"></div>
438
+ <div class="skeleton interactive-skeleton-block interactive-skeleton-height-16 interactive-skeleton-margin-md"></div>
439
+ <div class="skeleton interactive-skeleton-block interactive-skeleton-height-16 interactive-skeleton-width-80"></div>
440
+ </div>
441
+ </div>
442
+ </div>
443
+
444
+ <div class="card">
445
+ <h3>List Skeleton</h3>
446
+ <div class="max-w-md">
447
+ ${Array.from({ length: 4 }, () => html`
448
+ <div class="flex gap-sm align-center border-bottom interactive-skeleton-list-item">
449
+ <div class="skeleton interactive-skeleton-avatar"></div>
450
+ <div class="interactive-skeleton-details">
451
+ <div class="skeleton interactive-skeleton-block interactive-skeleton-height-16 interactive-skeleton-width-70 interactive-skeleton-margin-md"></div>
452
+ <div class="skeleton interactive-skeleton-block interactive-skeleton-height-14 interactive-skeleton-width-50"></div>
453
+ </div>
454
+ </div>
455
+ `)}
456
+ </div>
457
+ </div>
458
+
459
+ <div class="card">
460
+ <h3>Text Skeleton</h3>
461
+ <div class="max-w-lg">
462
+ <div class="skeleton interactive-skeleton-block interactive-skeleton-height-20 interactive-skeleton-width-40 interactive-skeleton-margin-lg"></div>
463
+ <div class="skeleton interactive-skeleton-block interactive-skeleton-height-16 interactive-skeleton-margin-md"></div>
464
+ <div class="skeleton interactive-skeleton-block interactive-skeleton-height-16 interactive-skeleton-margin-md"></div>
465
+ <div class="skeleton interactive-skeleton-block interactive-skeleton-height-16 interactive-skeleton-margin-md"></div>
466
+ <div class="skeleton interactive-skeleton-block interactive-skeleton-height-16 interactive-skeleton-width-85"></div>
467
+ </div>
468
+ </div>
469
+ `;
470
+
471
+ SkeletonLoading.storyName = 'Skeleton Loading';
472
+
473
+ export const DisabledStates = () => html`
474
+ <div class="card">
475
+ <h2>Disabled States</h2>
476
+ <p>Disabled elements have reduced opacity and no pointer events</p>
477
+
478
+ <h3>Buttons</h3>
479
+ <div class="flex flex-wrap gap-sm">
480
+ <button class="btn-primary" disabled>Primary Disabled</button>
481
+ <button class="btn-secondary" disabled>Secondary Disabled</button>
482
+ <button class="btn-outline" disabled>Outline Disabled</button>
483
+ </div>
484
+ </div>
485
+
486
+ <div class="card">
487
+ <h3>Icon Buttons</h3>
488
+ <div class="flex flex-wrap gap-sm">
489
+ <button class="btn-primary icon-only" disabled aria-label="Settings">
490
+ <pds-icon icon="gear"></pds-icon>
491
+ </button>
492
+ <button class="btn-secondary icon-only" disabled aria-label="Search">
493
+ <pds-icon icon="magnifying-glass"></pds-icon>
494
+ </button>
495
+ <button class="btn-outline icon-only" disabled aria-label="Heart">
496
+ <pds-icon icon="heart"></pds-icon>
497
+ </button>
498
+ </div>
499
+ </div>
500
+
501
+ <div class="card">
502
+ <h3>Buttons with Icons</h3>
503
+ <div class="flex flex-wrap gap-sm">
504
+ <button class="btn-primary" disabled>
505
+ <pds-icon icon="plus" size="sm"></pds-icon>
506
+ Add Item
507
+ </button>
508
+ <button class="btn-secondary" disabled>
509
+ <pds-icon icon="download" size="sm"></pds-icon>
510
+ Download
511
+ </button>
512
+ </div>
513
+ </div>
514
+
515
+ <div class="card">
516
+ <h3>Form Controls</h3>
517
+ <div class="max-w-md">
518
+ <label>
519
+ <span>Disabled Input</span>
520
+ <input type="text" disabled value="Cannot edit" />
521
+ </label>
522
+
523
+ <label>
524
+ <span>Disabled Select</span>
525
+ <select disabled>
526
+ <option>Cannot select</option>
527
+ </select>
528
+ </label>
529
+
530
+ <label>
531
+ <span>Disabled Textarea</span>
532
+ <textarea disabled rows="3">Cannot edit this text</textarea>
533
+ </label>
534
+
535
+ <fieldset role="group">
536
+ <legend>Disabled Checkboxes</legend>
537
+ <label>
538
+ <input type="checkbox" disabled />
539
+ <span>Disabled unchecked</span>
540
+ </label>
541
+ <label>
542
+ <input type="checkbox" disabled checked />
543
+ <span>Disabled checked</span>
544
+ </label>
545
+ </fieldset>
546
+ </div>
547
+ </div>
548
+ `;
549
+
550
+ DisabledStates.storyName = 'Disabled States';
551
+
552
+ export const CombinedStates = () => html`
553
+ <div class="card">
554
+ <h2>Combined State Examples</h2>
555
+ <p>Comprehensive examples showing all interactive states together</p>
556
+ </div>
557
+
558
+ <div class="card">
559
+ <h3>Idle State</h3>
560
+ <div class="flex flex-wrap gap-sm">
561
+ <button class="btn-primary">Primary</button>
562
+ <button class="btn-secondary">Secondary</button>
563
+ <button class="btn-outline">Outline</button>
564
+ </div>
565
+ </div>
566
+
567
+ <div class="card">
568
+ <h3>Hover State</h3>
569
+ <p class="text-muted text-sm">Hover over buttons to see effect</p>
570
+ <div class="flex flex-wrap gap-sm">
571
+ <button class="btn-primary">Primary</button>
572
+ <button class="btn-secondary">Secondary</button>
573
+ <button class="btn-outline">Outline</button>
574
+ </div>
575
+ </div>
576
+
577
+ <div class="card">
578
+ <h3>Active State</h3>
579
+ <p class="text-muted text-sm">Click and hold to see effect</p>
580
+ <div class="flex flex-wrap gap-sm">
581
+ <button class="btn-primary">Primary</button>
582
+ <button class="btn-secondary">Secondary</button>
583
+ <button class="btn-outline">Outline</button>
584
+ </div>
585
+ </div>
586
+
587
+ <div class="card">
588
+ <h3>Focus State</h3>
589
+ <p class="text-muted text-sm">Tab to focus on buttons</p>
590
+ <div class="flex flex-wrap gap-sm">
591
+ <button class="btn-primary">Primary</button>
592
+ <button class="btn-secondary">Secondary</button>
593
+ <button class="btn-outline">Outline</button>
594
+ </div>
595
+ </div>
596
+
597
+ <div class="card">
598
+ <h3>Disabled State</h3>
599
+ <div class="flex flex-wrap gap-sm">
600
+ <button class="btn-primary" disabled>Primary</button>
601
+ <button class="btn-secondary" disabled>Secondary</button>
602
+ <button class="btn-outline" disabled>Outline</button>
603
+ </div>
604
+ </div>
605
+
606
+ <div class="card">
607
+ <h3>Working State</h3>
608
+ <div class="flex flex-wrap gap-sm">
609
+ <button class="btn-primary btn-working">
610
+ <pds-icon icon="circle-notch" size="sm"></pds-icon>
611
+ Primary
612
+ </button>
613
+ <button class="btn-secondary btn-working">
614
+ <pds-icon icon="circle-notch" size="sm"></pds-icon>
615
+ Secondary
616
+ </button>
617
+ <button class="btn-outline btn-working">
618
+ <pds-icon icon="circle-notch" size="sm"></pds-icon>
619
+ Outline
620
+ </button>
621
+ </div>
622
+ </div>
623
+ `;
624
+
625
+ CombinedStates.storyName = 'All States';