@pure-ds/storybook 0.7.26 → 0.7.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/js/pds.d.ts CHANGED
@@ -160,6 +160,60 @@ export interface PDSEventMap {
160
160
  'pds:docs:view': CustomEvent<{ file: string }>;
161
161
  }
162
162
 
163
+ export type AskActionKind = 'ok' | 'cancel' | 'dismiss' | 'custom';
164
+
165
+ export interface AskButtonOption {
166
+ name: string;
167
+ primary?: boolean;
168
+ cancel?: boolean;
169
+ formNoValidate?: boolean;
170
+ [key: string]: any;
171
+ }
172
+
173
+ export interface AskBeforeCloseContext {
174
+ actionCode: string;
175
+ actionKind: AskActionKind;
176
+ dialog: HTMLDialogElement;
177
+ form: HTMLFormElement | null;
178
+ formData: FormData | null;
179
+ submitter?: HTMLElement | null;
180
+ originalEvent?: Event;
181
+ options: AskOptions;
182
+ button: AskButtonOption | null;
183
+ defaultResult: any;
184
+ }
185
+
186
+ export interface AskBeforeCloseResult {
187
+ allow?: boolean;
188
+ result?: any;
189
+ value?: any;
190
+ reason?: string;
191
+ }
192
+
193
+ export type AskBeforeCloseHandler = (
194
+ context: AskBeforeCloseContext
195
+ ) => Promise<boolean | AskBeforeCloseResult | void> | boolean | AskBeforeCloseResult | void;
196
+
197
+ export type AskMessage =
198
+ | string
199
+ | Node
200
+ | Array<any>
201
+ | { strings: any[]; values: any[] };
202
+
203
+ export interface AskOptions {
204
+ title?: string;
205
+ type?: string;
206
+ buttons?: Record<string, AskButtonOption>;
207
+ useForm?: boolean;
208
+ size?: 'sm' | 'md' | 'lg' | 'xl' | string;
209
+ class?: string | string[];
210
+ maxHeight?: string;
211
+ liquidGlassEffects?: boolean;
212
+ rendered?: (dialog: HTMLDialogElement) => void;
213
+ beforeClose?: AskBeforeCloseHandler;
214
+ [key: string]: any;
215
+ }
216
+
163
217
  export class PDS extends EventTarget {
164
218
  // Static surface
165
219
  static registry: PDSRegistry;
@@ -273,7 +327,7 @@ export class PDS extends EventTarget {
273
327
  * @example
274
328
  * const name = await PDS.ask('Enter your name:', { type: 'prompt' });
275
329
  */
276
- static ask(message: string, options?: any): Promise<any>;
330
+ static ask(message: AskMessage, options?: AskOptions): Promise<any>;
277
331
 
278
332
  /**
279
333
  * Current configuration after PDS.start() completes - read-only, frozen after initialization.
@@ -1,6 +1,6 @@
1
1
  import { html, lazyProps } from "#pds/lit";
2
2
 
3
- const languageOptions = [
3
+ const languageItems = [
4
4
  { id: "et", text: "Amhaars", description: "ET · Ethiopia", category: "Languages" },
5
5
  { id: "bd", text: "Bengaals", description: "BD · Bangladesh", category: "Languages" },
6
6
  { id: "nl", text: "Nederlands", description: "NL · Netherlands", category: "Languages" },
@@ -13,7 +13,7 @@ const languageOptions = [
13
13
  { id: "en", text: "Engels", description: "US · United Kingdom", category: "Languages" },
14
14
  ];
15
15
 
16
- const groupedOptions = [
16
+ const groupedItems = [
17
17
  { id: "et", text: "Amhaars", description: "ET · Ethiopia", category: "Languages" },
18
18
  { id: "nl", text: "Nederlands", description: "NL · Netherlands", category: "Languages" },
19
19
  { id: "react", text: "React", description: "Frontend", category: "Skills" },
@@ -22,7 +22,7 @@ const groupedOptions = [
22
22
  { id: "storybook", text: "Storybook", description: "Component docs", category: "Tools" },
23
23
  ];
24
24
 
25
- const createSettingsFromItems = (items) => {
25
+ const createOmniboxOptionsFromItems = (items) => {
26
26
  const byCategory = items.reduce((map, item) => {
27
27
  const key = item.category || "Options";
28
28
  if (!map.has(key)) map.set(key, []);
@@ -34,8 +34,8 @@ const createSettingsFromItems = (items) => {
34
34
  for (const [categoryName, entries] of byCategory.entries()) {
35
35
  categories[categoryName] = {
36
36
  trigger: () => true,
37
- getItems: async (options) => {
38
- const query = (options?.search || "").trim().toLowerCase();
37
+ getItems: async ({ search = "" } = {}) => {
38
+ const query = String(search).trim().toLowerCase();
39
39
  return entries.filter((item) => {
40
40
  if (!query) return true;
41
41
  return (
@@ -48,33 +48,221 @@ const createSettingsFromItems = (items) => {
48
48
  };
49
49
  }
50
50
 
51
- return {
52
- hideCategory: false,
53
- categories,
54
- };
51
+ return { categories };
55
52
  };
56
53
 
57
- const languageSettings = createSettingsFromItems(languageOptions);
58
- const groupedSettings = createSettingsFromItems(groupedOptions);
54
+ const languageOptions = createOmniboxOptionsFromItems(languageItems);
55
+ const groupedOptions = createOmniboxOptionsFromItems(groupedItems);
59
56
 
60
- const sourceCode = `<pds-tags
57
+ const languageOptionsSource = `const options = {
58
+ categories: {
59
+ Languages: {
60
+ trigger: () => true,
61
+ getItems: async ({ search = "" } = {}) => {
62
+ const query = search.trim().toLowerCase();
63
+ const items = [
64
+ { id: "et", text: "Amhaars", description: "ET · Ethiopia", category: "Languages" },
65
+ { id: "bd", text: "Bengaals", description: "BD · Bangladesh", category: "Languages" },
66
+ { id: "nl", text: "Nederlands", description: "NL · Netherlands", category: "Languages" },
67
+ { id: "fr", text: "Frans", description: "FR · France", category: "Languages" },
68
+ { id: "de", text: "Duits", description: "DE · Germany", category: "Languages" },
69
+ ];
70
+ return items.filter((item) => !query || item.text.toLowerCase().includes(query));
71
+ },
72
+ },
73
+ },
74
+ };
75
+
76
+ <pds-tags
61
77
  name="languages"
62
78
  placeholder="Zoek taal..."
63
79
  value="et,bd,nl"
64
- ${"${lazyProps({ settings: languageSettings })}"}
80
+ ${"${lazyProps({ options })}"}
65
81
  ></pds-tags>`;
66
82
 
67
- const defaultDocs = {
83
+ const groupedOptionsSource = `const options = {
84
+ categories: {
85
+ Languages: {
86
+ trigger: () => true,
87
+ getItems: async ({ search = "" } = {}) => {
88
+ const query = search.trim().toLowerCase();
89
+ const items = [
90
+ { id: "et", text: "Amhaars", description: "ET · Ethiopia", category: "Languages" },
91
+ { id: "nl", text: "Nederlands", description: "NL · Netherlands", category: "Languages" },
92
+ ];
93
+ return items.filter((item) => !query || item.text.toLowerCase().includes(query));
94
+ },
95
+ },
96
+ Skills: {
97
+ trigger: () => true,
98
+ getItems: async ({ search = "" } = {}) => {
99
+ const query = search.trim().toLowerCase();
100
+ const items = [
101
+ { id: "react", text: "React", description: "Frontend", category: "Skills" },
102
+ { id: "wc", text: "Web Components", description: "Frontend", category: "Skills" },
103
+ ];
104
+ return items.filter((item) => !query || item.text.toLowerCase().includes(query));
105
+ },
106
+ },
107
+ Tools: {
108
+ trigger: () => true,
109
+ getItems: async ({ search = "" } = {}) => {
110
+ const query = search.trim().toLowerCase();
111
+ const items = [
112
+ { id: "figma", text: "Figma", description: "Design tooling", category: "Tools" },
113
+ { id: "storybook", text: "Storybook", description: "Component docs", category: "Tools" },
114
+ ];
115
+ return items.filter((item) => !query || item.text.toLowerCase().includes(query));
116
+ },
117
+ },
118
+ },
119
+ };
120
+
121
+ <pds-tags
122
+ name="selections"
123
+ placeholder="Search languages, skills, or tools..."
124
+ value="nl,wc,figma"
125
+ ${"${lazyProps({ options })}"}
126
+ ></pds-tags>`;
127
+
128
+ const formOptionsSource = `const options = {
129
+ categories: {
130
+ Languages: {
131
+ trigger: () => true,
132
+ getItems: async ({ search = "" } = {}) => {
133
+ const query = search.trim().toLowerCase();
134
+ const items = [
135
+ { id: "et", text: "Amhaars", description: "ET · Ethiopia", category: "Languages" },
136
+ { id: "bd", text: "Bengaals", description: "BD · Bangladesh", category: "Languages" },
137
+ { id: "nl", text: "Nederlands", description: "NL · Netherlands", category: "Languages" },
138
+ ];
139
+ return items.filter((item) => !query || item.text.toLowerCase().includes(query));
140
+ },
141
+ },
142
+ },
143
+ };
144
+
145
+ <form class="stack-md">
146
+ <pds-tags
147
+ name="languages"
148
+ required
149
+ placeholder="Select one or more languages..."
150
+ ${"${lazyProps({ options })}"}
151
+ ></pds-tags>
152
+
153
+ <button class="btn-primary" type="submit">Submit</button>
154
+ </form>`;
155
+
156
+ const tvMazeOptions = {
157
+ categories: {
158
+ "TV Shows": {
159
+ trigger: () => true,
160
+ getItems: async ({ search = "" } = {}) => {
161
+ const query = String(search).trim();
162
+ const fallbackQuery = "star";
163
+ const url = `https://api.tvmaze.com/search/shows?q=${encodeURIComponent(query || fallbackQuery)}`;
164
+
165
+ try {
166
+ const response = await fetch(url);
167
+ if (!response.ok) return [];
168
+
169
+ const payload = await response.json();
170
+ if (!Array.isArray(payload)) return [];
171
+
172
+ return payload
173
+ .map((entry) => entry?.show)
174
+ .filter(Boolean)
175
+ .slice(0, 20)
176
+ .map((show) => {
177
+ const year = show.premiered ? String(show.premiered).slice(0, 4) : "";
178
+ const genres = Array.isArray(show.genres)
179
+ ? show.genres.slice(0, 2).join(", ")
180
+ : "";
181
+ const channel = show.network?.name || show.webChannel?.name || "";
182
+ const description = [year, genres, channel].filter(Boolean).join(" · ");
183
+
184
+ return {
185
+ id: String(show.id),
186
+ text: show.name || `Show ${show.id}`,
187
+ description,
188
+ category: "TV Shows",
189
+ };
190
+ });
191
+ } catch {
192
+ return [];
193
+ }
194
+ },
195
+ },
196
+ },
197
+ };
198
+
199
+ const tvMazeOptionsSource = `const options = {
200
+ categories: {
201
+ "TV Shows": {
202
+ trigger: () => true,
203
+ getItems: async ({ search = "" } = {}) => {
204
+ const query = search.trim();
205
+ const fallbackQuery = "star";
206
+ const url = "https://api.tvmaze.com/search/shows?q=" + encodeURIComponent(query || fallbackQuery);
207
+
208
+ const response = await fetch(url);
209
+ if (!response.ok) return [];
210
+
211
+ const payload = await response.json();
212
+ if (!Array.isArray(payload)) return [];
213
+
214
+ return payload
215
+ .map((entry) => entry?.show)
216
+ .filter(Boolean)
217
+ .slice(0, 20)
218
+ .map((show) => ({
219
+ id: String(show.id),
220
+ text: show.name,
221
+ description: [
222
+ show.premiered?.slice(0, 4),
223
+ Array.isArray(show.genres) ? show.genres.slice(0, 2).join(", ") : "",
224
+ show.network?.name || show.webChannel?.name || "",
225
+ ].filter(Boolean).join(" · "),
226
+ category: "TV Shows",
227
+ }));
228
+ },
229
+ },
230
+ },
231
+ };
232
+
233
+ <pds-tags
234
+ name="favoriteShows"
235
+ placeholder="Search TV shows (e.g. office, star trek, dark)..."
236
+ ${"${lazyProps({ options })}"}
237
+ ></pds-tags>`;
238
+
239
+ const docsParameters = {
68
240
  description: {
69
241
  component: "Compound multi-select built with pds-omnibox. Select/unselect values from suggestions and manage them as tags.",
70
242
  },
71
243
  };
72
244
 
245
+ if (typeof window !== "undefined") {
246
+ import("../reference/reference-docs.js")
247
+ .then(({ createComponentDocsPage }) => {
248
+ docsParameters.page = createComponentDocsPage("pds-tags");
249
+ })
250
+ .catch((error) => {
251
+ console.warn(
252
+ "storybook: docs page failed to load for pds-tags",
253
+ error,
254
+ );
255
+ });
256
+ }
257
+
73
258
  export default {
74
259
  title: "Components/pds-tags",
75
260
  tags: ["autodocs", "forms", "autocomplete", "multiselect", "pds-tags"],
76
261
  parameters: {
77
- docs: defaultDocs,
262
+ pds: {
263
+ tags: ["forms", "autocomplete", "multiselect", "pds-tags"],
264
+ },
265
+ docs: docsParameters,
78
266
  },
79
267
  argTypes: {
80
268
  name: { control: "text" },
@@ -93,7 +281,8 @@ const Template = ({ name, placeholder, value, required, disabled }) => html`
93
281
  value=${value}
94
282
  ?required=${required}
95
283
  ?disabled=${disabled}
96
- ${lazyProps({ settings: languageSettings })}
284
+ data-options-source=${languageOptionsSource}
285
+ ${lazyProps({ options: languageOptions })}
97
286
  ></pds-tags>
98
287
  </div>
99
288
  `;
@@ -131,7 +320,8 @@ const FormTemplate = ({ required }) => {
131
320
  name="languages"
132
321
  placeholder="Select one or more languages..."
133
322
  ?required=${required}
134
- ${lazyProps({ settings: languageSettings })}
323
+ data-options-source=${languageOptionsSource}
324
+ ${lazyProps({ options: languageOptions })}
135
325
  ></pds-tags>
136
326
  <div class="flex gap-sm">
137
327
  <button class="btn-primary" type="submit">Submit</button>
@@ -155,7 +345,8 @@ export const Default = {
155
345
  parameters: {
156
346
  docs: {
157
347
  source: {
158
- code: sourceCode,
348
+ type: "code",
349
+ code: languageOptionsSource,
159
350
  },
160
351
  },
161
352
  },
@@ -170,6 +361,14 @@ export const Empty = {
170
361
  disabled: false,
171
362
  },
172
363
  render: Template,
364
+ parameters: {
365
+ docs: {
366
+ source: {
367
+ type: "code",
368
+ code: languageOptionsSource,
369
+ },
370
+ },
371
+ },
173
372
  };
174
373
 
175
374
  export const Required = {
@@ -181,6 +380,14 @@ export const Required = {
181
380
  disabled: false,
182
381
  },
183
382
  render: Template,
383
+ parameters: {
384
+ docs: {
385
+ source: {
386
+ type: "code",
387
+ code: languageOptionsSource,
388
+ },
389
+ },
390
+ },
184
391
  };
185
392
 
186
393
  export const Disabled = {
@@ -192,6 +399,14 @@ export const Disabled = {
192
399
  disabled: true,
193
400
  },
194
401
  render: Template,
402
+ parameters: {
403
+ docs: {
404
+ source: {
405
+ type: "code",
406
+ code: languageOptionsSource,
407
+ },
408
+ },
409
+ },
195
410
  };
196
411
 
197
412
  export const GroupedOptions = {
@@ -210,10 +425,19 @@ export const GroupedOptions = {
210
425
  value=${value}
211
426
  ?required=${required}
212
427
  ?disabled=${disabled}
213
- ${lazyProps({ settings: groupedSettings })}
428
+ data-options-source=${groupedOptionsSource}
429
+ ${lazyProps({ options: groupedOptions })}
214
430
  ></pds-tags>
215
431
  </div>
216
432
  `,
433
+ parameters: {
434
+ docs: {
435
+ source: {
436
+ type: "code",
437
+ code: groupedOptionsSource,
438
+ },
439
+ },
440
+ },
217
441
  };
218
442
 
219
443
  export const FormSubmission = {
@@ -221,4 +445,44 @@ export const FormSubmission = {
221
445
  required: true,
222
446
  },
223
447
  render: FormTemplate,
448
+ parameters: {
449
+ docs: {
450
+ source: {
451
+ type: "code",
452
+ code: formOptionsSource,
453
+ },
454
+ },
455
+ },
456
+ };
457
+
458
+ export const PublicApiTvShows = {
459
+ args: {
460
+ name: "favoriteShows",
461
+ placeholder: "Search TV shows (e.g. office, star trek, dark)...",
462
+ value: "",
463
+ required: false,
464
+ disabled: false,
465
+ },
466
+ render: ({ name, placeholder, value, required, disabled }) => html`
467
+ <div class="stack-md">
468
+ <p class="text-muted">Results are fetched live from the public TVMaze API (no API key required).</p>
469
+ <pds-tags
470
+ name=${name}
471
+ placeholder=${placeholder}
472
+ value=${value}
473
+ ?required=${required}
474
+ ?disabled=${disabled}
475
+ data-options-source=${tvMazeOptionsSource}
476
+ ${lazyProps({ options: tvMazeOptions })}
477
+ ></pds-tags>
478
+ </div>
479
+ `,
480
+ parameters: {
481
+ docs: {
482
+ source: {
483
+ type: "code",
484
+ code: tvMazeOptionsSource,
485
+ },
486
+ },
487
+ },
224
488
  };
@@ -38,9 +38,10 @@ export const ButtonSizes = () => html`
38
38
  <div class="card">
39
39
  <header>
40
40
  <h3>Button Sizes</h3>
41
- <small class="text-muted">Scale actions with <code>.btn-sm</code> and <code>.btn-lg</code> size utilities.</small>
41
+ <small class="text-muted">Scale actions with <code>.btn-xs</code>, <code>.btn-sm</code>, and <code>.btn-lg</code> size utilities.</small>
42
42
  </header>
43
43
  <div class="flex gap-sm flex-wrap items-center">
44
+ <button class="btn-primary btn-xs">Extra Small</button>
44
45
  <button class="btn-primary btn-sm">Small</button>
45
46
  <button class="btn-primary">Default</button>
46
47
  <button class="btn-primary btn-lg">Large</button>
@@ -126,6 +127,9 @@ export const IconOnlyButtons = () => html`
126
127
  <section class="stack-md">
127
128
  <h3>Icon-Only Sizes</h3>
128
129
  <div class="flex gap-sm flex-wrap items-center">
130
+ <button class="icon-only btn-primary btn-xs">
131
+ <pds-icon icon="plus" size="xs" label="Add"></pds-icon>
132
+ </button>
129
133
  <button class="icon-only btn-primary btn-sm">
130
134
  <pds-icon icon="plus" size="sm" label="Add"></pds-icon>
131
135
  </button>
@@ -185,11 +189,26 @@ export const AllSizesCombinations = () => html`
185
189
  <div class="card gap-lg">
186
190
  <header>
187
191
  <h3>All Sizes & Combinations</h3>
188
- <small class="text-muted">Compare all button variants across small, default, and large sizes.</small>
192
+ <small class="text-muted">Compare all button variants across extra-small, small, default, and large sizes.</small>
189
193
  </header>
194
+ <section class="stack-md">
195
+ <h4>Extra Small Buttons</h4>
196
+ <div class="flex gap-sm flex-wrap items-center">
197
+ <button class="btn-primary btn-xs">Primary</button>
198
+ <button class="btn-secondary btn-xs">Secondary</button>
199
+ <button class="btn-outline btn-xs">Outline</button>
200
+ <button class="btn-primary btn-xs">
201
+ <pds-icon icon="heart" size="xs"></pds-icon>
202
+ With Icon
203
+ </button>
204
+ <button class="icon-only btn-primary btn-xs">
205
+ <pds-icon icon="gear" size="xs" label="Settings"></pds-icon>
206
+ </button>
207
+ </div>
208
+ </section>
190
209
  <section class="stack-md">
191
210
  <h4>Small Buttons</h4>
192
- <div class="flex gap-sm flex-wrap">
211
+ <div class="flex gap-sm flex-wrap items-center">
193
212
  <button class="btn-primary btn-sm">Primary</button>
194
213
  <button class="btn-secondary btn-sm">Secondary</button>
195
214
  <button class="btn-outline btn-sm">Outline</button>
@@ -205,7 +224,7 @@ export const AllSizesCombinations = () => html`
205
224
 
206
225
  <section class="stack-md">
207
226
  <h4>Default Buttons</h4>
208
- <div class="flex gap-sm flex-wrap">
227
+ <div class="flex gap-sm flex-wrap items-center">
209
228
  <button class="btn-primary">Primary</button>
210
229
  <button class="btn-secondary">Secondary</button>
211
230
  <button class="btn-outline">Outline</button>
@@ -221,7 +240,7 @@ export const AllSizesCombinations = () => html`
221
240
 
222
241
  <section class="stack-md">
223
242
  <h4>Large Buttons</h4>
224
- <div class="flex gap-sm flex-wrap">
243
+ <div class="flex gap-sm flex-wrap items-center">
225
244
  <button class="btn-primary btn-lg">Primary</button>
226
245
  <button class="btn-secondary btn-lg">Secondary</button>
227
246
  <button class="btn-outline btn-lg">Outline</button>