@pure-ds/storybook 0.4.23 → 0.4.25

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.
@@ -11,7 +11,7 @@ function getStep(value) {
11
11
  // Default options for pds-form
12
12
  const DEFAULT_OPTIONS = {
13
13
  widgets: {
14
- booleans: "toggle", // 'toggle' | 'checkbox'
14
+ booleans: "toggle", // 'toggle' | 'toggle-with-icons' | 'checkbox'
15
15
  numbers: "input", // 'input' | 'range'
16
16
  selects: "standard", // 'standard' | 'dropdown'
17
17
  },
@@ -669,10 +669,9 @@ export class SchemaForm extends LitElement {
669
669
  return useRange ? "input-range" : "input-number";
670
670
  }
671
671
  if (schema.type === "boolean") {
672
- // Check if toggle should be used
673
- const useToggle =
674
- this.#getOption("widgets.booleans", "toggle") === "toggle";
675
- return useToggle ? "toggle" : "checkbox";
672
+ // Return the actual boolean widget type
673
+ const booleanWidget = this.#getOption("widgets.booleans", "toggle");
674
+ return booleanWidget === "checkbox" ? "checkbox" : booleanWidget;
676
675
  }
677
676
  return "input-text";
678
677
  }
@@ -1489,14 +1488,20 @@ export class SchemaForm extends LitElement {
1489
1488
  this.#emit("pw:after-render-field", { path, schema: node.schema })
1490
1489
  );
1491
1490
 
1492
- // Add data-toggle for toggle switches
1493
- const isToggle = node.widgetKey === "toggle";
1491
+ // Add data-toggle for toggle switches (both toggle and toggle-with-icons)
1492
+ const isToggle = node.widgetKey === "toggle" || node.widgetKey === "toggle-with-icons";
1493
+ const useIconToggle = node.widgetKey === "toggle-with-icons";
1494
1494
 
1495
1495
  // Add range-output class for range inputs if enabled
1496
1496
  const isRange = node.widgetKey === "input-range";
1497
1497
  const useRangeOutput =
1498
1498
  isRange && this.#getOption("enhancements.rangeOutput", true);
1499
- const labelClass = useRangeOutput ? "range-output" : undefined;
1499
+
1500
+ // Build class list for label
1501
+ const labelClasses = [];
1502
+ if (useRangeOutput) labelClasses.push("range-output");
1503
+ if (useIconToggle) labelClasses.push("with-icons");
1504
+ const labelClass = labelClasses.length > 0 ? labelClasses.join(" ") : undefined;
1500
1505
 
1501
1506
  const renderControlAndLabel = (isToggle) => {
1502
1507
  if (isToggle) return html`${controlTpl} <span data-label>${label}</span>`;
@@ -1855,6 +1860,22 @@ export class SchemaForm extends LitElement {
1855
1860
  `
1856
1861
  );
1857
1862
 
1863
+ // Toggle switch with icons (same as toggle, styling comes from .with-icons class on label)
1864
+ this.defineRenderer(
1865
+ "toggle-with-icons",
1866
+ ({ id, path, value, attrs, set }) => html`
1867
+ <input
1868
+ id=${id}
1869
+ name=${path}
1870
+ type="checkbox"
1871
+ .checked=${!!value}
1872
+ ?disabled=${!!attrs.disabled}
1873
+ ?required=${!!attrs.required}
1874
+ @change=${(e) => set(!!e.target.checked)}
1875
+ />
1876
+ `
1877
+ );
1878
+
1858
1879
  this.defineRenderer(
1859
1880
  "select",
1860
1881
  ({ id, path, value, attrs, set, schema, ui, host }) => {
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Toast notification utilities for PDS
3
+ * Ensures pds-toaster component is properly initialized before use
4
+ */
5
+
6
+ /**
7
+ * Ensures pds-toaster exists in DOM and is fully loaded
8
+ * @returns {Promise<HTMLElement>} The pds-toaster element
9
+ * @private
10
+ */
11
+ async function ensureToaster() {
12
+ let toaster = document.querySelector('pds-toaster');
13
+ if (!toaster) {
14
+ toaster = document.createElement('pds-toaster');
15
+ document.body.appendChild(toaster);
16
+ await customElements.whenDefined('pds-toaster');
17
+ }
18
+ return toaster;
19
+ }
20
+
21
+ /**
22
+ * Display a toast notification
23
+ *
24
+ * This method automatically ensures the pds-toaster component exists and is loaded
25
+ * before displaying the notification. The toaster element is appended to document.body
26
+ * if not already present.
27
+ *
28
+ * @param {string} message - The message to display
29
+ * @param {Object} [options={}] - Toast configuration
30
+ * @param {"information"|"success"|"warning"|"error"} [options.type="information"] - Toast type/severity
31
+ * @param {number} [options.duration] - Duration in milliseconds (auto-calculated if not provided based on message length)
32
+ * @param {boolean} [options.closable=true] - Whether the toast can be manually closed
33
+ * @param {boolean} [options.persistent=false] - If true, toast won't auto-dismiss (requires manual close)
34
+ * @returns {Promise<string>} Toast ID (can be used to dismiss programmatically)
35
+ *
36
+ * @example
37
+ * // Simple success toast
38
+ * await PDS.toast('Changes saved successfully!', { type: 'success' });
39
+ *
40
+ * @example
41
+ * // Error with custom duration
42
+ * await PDS.toast('Failed to save changes', {
43
+ * type: 'error',
44
+ * duration: 8000
45
+ * });
46
+ *
47
+ * @example
48
+ * // Persistent warning (must be manually closed)
49
+ * await PDS.toast('This action cannot be undone', {
50
+ * type: 'warning',
51
+ * persistent: true
52
+ * });
53
+ *
54
+ * @example
55
+ * // Get toast ID to dismiss later
56
+ * const toastId = await PDS.toast('Processing...', { persistent: true });
57
+ * // ... later
58
+ * const toaster = document.querySelector('pds-toaster');
59
+ * toaster.dismissToast(toastId);
60
+ */
61
+ export async function toast(message, options = {}) {
62
+ const toaster = await ensureToaster();
63
+ return toaster.toast(message, options);
64
+ }
65
+
66
+ /**
67
+ * Display a success toast (convenience method)
68
+ *
69
+ * @param {string} message - The success message
70
+ * @param {Object} [options={}] - Additional toast options (type is preset to 'success')
71
+ * @returns {Promise<string>} Toast ID
72
+ *
73
+ * @example
74
+ * await PDS.toast.success('Profile updated!');
75
+ */
76
+ toast.success = async function(message, options = {}) {
77
+ return toast(message, { ...options, type: 'success' });
78
+ };
79
+
80
+ /**
81
+ * Display an error toast (convenience method)
82
+ *
83
+ * @param {string} message - The error message
84
+ * @param {Object} [options={}] - Additional toast options (type is preset to 'error')
85
+ * @returns {Promise<string>} Toast ID
86
+ *
87
+ * @example
88
+ * await PDS.toast.error('Failed to connect to server');
89
+ */
90
+ toast.error = async function(message, options = {}) {
91
+ return toast(message, { ...options, type: 'error' });
92
+ };
93
+
94
+ /**
95
+ * Display a warning toast (convenience method)
96
+ *
97
+ * @param {string} message - The warning message
98
+ * @param {Object} [options={}] - Additional toast options (type is preset to 'warning')
99
+ * @returns {Promise<string>} Toast ID
100
+ *
101
+ * @example
102
+ * await PDS.toast.warning('Session will expire in 5 minutes');
103
+ */
104
+ toast.warning = async function(message, options = {}) {
105
+ return toast(message, { ...options, type: 'warning' });
106
+ };
107
+
108
+ /**
109
+ * Display an information toast (convenience method)
110
+ *
111
+ * @param {string} message - The information message
112
+ * @param {Object} [options={}] - Additional toast options (type is preset to 'information')
113
+ * @returns {Promise<string>} Toast ID
114
+ *
115
+ * @example
116
+ * await PDS.toast.info('New features available!');
117
+ */
118
+ toast.info = async function(message, options = {}) {
119
+ return toast(message, { ...options, type: 'information' });
120
+ };
@@ -9,20 +9,6 @@ import { AutoComplete } from "pure-web/ac";
9
9
  import { figmafyTokens } from "./figma-export.js";
10
10
  const STORAGE_KEY = "pure-ds-config";
11
11
 
12
-
13
-
14
- async function toast(message, options = {}) {
15
- let toaster = document.querySelector("#global-toaster");
16
- if (!toaster) {
17
- toaster = document.createElement("pds-toaster");
18
- toaster.id = "global-toaster";
19
- document.body.appendChild(toaster);
20
- await customElements.whenDefined("pds-toaster");
21
- }
22
-
23
- return toaster.toast(message, options);
24
- }
25
-
26
12
  customElements.define(
27
13
  "pds-config-form",
28
14
  class extends LitElement {
@@ -325,7 +311,7 @@ customElements.define(
325
311
  .slice(0, 3)
326
312
  .map((i) => `• ${i.message}`)
327
313
  .join("\n");
328
- this._validationToastId = toast(
314
+ this._validationToastId = await PDS.toast(
329
315
  `Design has accessibility issues. Fix before applying.\n${summary}`,
330
316
  { type: "error", persistent: true }
331
317
  );
@@ -408,11 +394,11 @@ customElements.define(
408
394
  }
409
395
  }
410
396
 
411
- toggleInspectorMode() {
397
+ async toggleInspectorMode() {
412
398
  this.inspectorMode = !this.inspectorMode;
413
399
  this.dispatchInspectorModeChange();
414
400
 
415
- toast(
401
+ await PDS.toast(
416
402
  this.inspectorMode
417
403
  ? "Code Inspector active - click any element in the showcase to view its code"
418
404
  : "Code Inspector deactivated",
@@ -485,7 +471,7 @@ customElements.define(
485
471
  return filtered;
486
472
  }
487
473
 
488
- handleFormChange = (event) => {
474
+ handleFormChange = async (event) => {
489
475
  // Get values from the pds-form's serialize method or from event detail
490
476
  let values;
491
477
  let changedField = null;
@@ -564,7 +550,7 @@ customElements.define(
564
550
  .slice(0, 3)
565
551
  .map((i) => `• ${i.message}`)
566
552
  .join("\n");
567
- this._validationToastId = toast(
553
+ this._validationToastId = await PDS.toast(
568
554
  `Design has accessibility issues. Fix before saving.\n${summary}`,
569
555
  { type: "error" }
570
556
  );
@@ -635,7 +621,7 @@ customElements.define(
635
621
  this.saveConfig();
636
622
  this.applyStyles(true);
637
623
 
638
- toast("Configuration reset to defaults", {
624
+ await PDS.toast("Configuration reset to defaults", {
639
625
  type: "info",
640
626
  duration: 2000,
641
627
  });
@@ -658,7 +644,7 @@ customElements.define(
658
644
  .slice(0, 3)
659
645
  .map((i) => `• ${i.message}`)
660
646
  .join("\n");
661
- this._validationToastId = toast(
647
+ this._validationToastId = await PDS.toast(
662
648
  `Preset "${preset.name}" has accessibility issues — not applied.\n${summary}`,
663
649
  { type: "error", persistent: true }
664
650
  );
@@ -685,14 +671,14 @@ customElements.define(
685
671
  this.saveConfig();
686
672
  this.applyStyles(true);
687
673
 
688
- toast(`"${preset.name}" preset loaded successfully!`, {
674
+ await PDS.toast(`"${preset.name}" preset loaded successfully!`, {
689
675
  type: "success",
690
676
  duration: 3000,
691
677
  });
692
678
  }
693
679
  };
694
680
 
695
- handleThemeChange(e) {
681
+ async handleThemeChange(e) {
696
682
  try {
697
683
  const value = e.target.value;
698
684
  // Update centralized theme via PDS (this persists + applies + sets up listeners)
@@ -701,7 +687,7 @@ customElements.define(
701
687
  // Apply immediately and emit styles using the user config (keep config separate)
702
688
  this.applyStyles(true);
703
689
 
704
- toast(`Theme set to ${value}`, { type: "info", duration: 1200 });
690
+ await PDS.toast(`Theme set to ${value}`, { type: "info", duration: 1200 });
705
691
  } catch (ex) {
706
692
  console.warn("Failed to change theme:", ex);
707
693
  }
@@ -750,9 +736,9 @@ export const pdsConfig = ${JSON.stringify(this.config, null, 2)};
750
736
  if (!this.schema) {
751
737
  if (!this._loadingToastShown) {
752
738
  this._loadingToastShown = true;
753
- setTimeout(() => {
739
+ setTimeout(async () => {
754
740
  try {
755
- toast("Loading schema...", { duration: 1000 });
741
+ await PDS.toast("Loading schema...", { duration: 1000 });
756
742
  } catch {}
757
743
  }, 250);
758
744
  }
@@ -3,11 +3,6 @@ import { PDS } from "../../../src/js/pds.js";
3
3
 
4
4
  import { AutoComplete } from "pure-web/ac";
5
5
 
6
- const toast = (message, options) => {
7
- const toaster = document.getElementById("global-toaster");
8
- toaster.toast(message, options);
9
- };
10
-
11
6
  customElements.define(
12
7
  "pds-demo",
13
8
  class extends LitElement {
@@ -3414,12 +3409,12 @@ customElements.define(
3414
3409
  } catch (err) {
3415
3410
  console.error("Error fetching README:", err);
3416
3411
  const toaster = document.getElementById("global-toaster");
3417
- toaster.toast("Error loading docs. See console.", { type: "danger" });
3412
+ await PDS.toast("Error loading docs. See console.", { type: "error" });
3418
3413
  }
3419
3414
  }
3420
3415
 
3421
3416
  handleTabChange(event) {
3422
- toast(`Switched to tab: ${event.detail.newTab}`, { type: "info" });
3417
+ await PDS.toast(`Switched to tab: ${event.detail.newTab}`, { type: "info" });
3423
3418
  }
3424
3419
 
3425
3420
  openDrawer() {
@@ -3502,42 +3497,42 @@ customElements.define(
3502
3497
  }
3503
3498
 
3504
3499
  // Toast handler methods
3505
- showSuccessToast() {
3506
- toast("Your changes have been saved successfully!", {
3500
+ async showSuccessToast() {
3501
+ await PDS.toast("Your changes have been saved successfully!", {
3507
3502
  type: "success",
3508
3503
  });
3509
3504
  }
3510
3505
 
3511
- showInfoToast() {
3512
- toast("This is an informational message with helpful context.", {
3513
- type: "info",
3506
+ async showInfoToast() {
3507
+ await PDS.toast("This is an informational message with helpful context.", {
3508
+ type: "information",
3514
3509
  });
3515
3510
  }
3516
3511
 
3517
- showWarningToast() {
3518
- toast("Warning: This action cannot be undone!", {
3512
+ async showWarningToast() {
3513
+ await PDS.toast("Warning: This action cannot be undone!", {
3519
3514
  type: "warning",
3520
3515
  });
3521
3516
  }
3522
3517
 
3523
- showErrorToast() {
3524
- toast("Error: Something went wrong. Please try again.", {
3518
+ async showErrorToast() {
3519
+ await PDS.toast("Error: Something went wrong. Please try again.", {
3525
3520
  type: "error",
3526
3521
  });
3527
3522
  }
3528
3523
 
3529
- showLongToast() {
3530
- toast(
3524
+ async showLongToast() {
3525
+ await PDS.toast(
3531
3526
  "This is a longer toast notification message that demonstrates how the duration is automatically calculated based on the message length. The toast will stay visible longer to give you enough time to read the entire message.",
3532
- { type: "info" }
3527
+ { type: "information" }
3533
3528
  );
3534
3529
  }
3535
3530
 
3536
- showPersistentToast() {
3537
- toast(
3531
+ async showPersistentToast() {
3532
+ await PDS.toast(
3538
3533
  "This is a persistent toast that won't auto-dismiss. Click the × to close it.",
3539
3534
  {
3540
- type: "info",
3535
+ type: "information",
3541
3536
  persistent: true,
3542
3537
  }
3543
3538
  );
@@ -737,7 +737,6 @@ export const presets = {
737
737
  "Data-dense business intelligence app interface with organized hierarchy and professional polish",
738
738
  options: {
739
739
  liquidGlassEffects: false,
740
- backgroundMesh: 2,
741
740
  },
742
741
  colors: {
743
742
  primary: "#0066cc", // corporate blue for primary actions
@@ -815,7 +814,7 @@ presets.default = {
815
814
  form: {
816
815
  options: {
817
816
  widgets: {
818
- booleans: "toggle", // 'toggle' | 'checkbox'
817
+ booleans: "toggle", // 'toggle' | 'toggle-with-icons' | 'checkbox'
819
818
  numbers: "input", // 'input' | 'range'
820
819
  selects: "standard", // 'standard' | 'dropdown'
821
820
  },
package/src/js/pds.d.ts CHANGED
@@ -154,6 +154,68 @@ export class PDS extends EventTarget {
154
154
  static validateDesign: (designConfig: any, options?: { minContrast?: number }) => { ok: boolean; issues: Array<{ path: string; message: string; ratio: number; min: number; context?: string }> };
155
155
  static validateDesigns: (designs: Array<any> | Record<string, any>, options?: { minContrast?: number }) => { ok: boolean; results: Array<{ name?: string; ok: boolean; issues: Array<{ path: string; message: string; ratio: number; min: number; context?: string }> }> };
156
156
 
157
+ /**
158
+ * Display a toast notification.
159
+ *
160
+ * Automatically ensures the pds-toaster component exists and is loaded before displaying.
161
+ *
162
+ * @param message - The message to display
163
+ * @param options - Toast configuration
164
+ * @param options.type - Toast type/severity ("information" | "success" | "warning" | "error")
165
+ * @param options.duration - Duration in milliseconds (auto-calculated if not provided)
166
+ * @param options.closable - Whether toast can be manually closed (default: true)
167
+ * @param options.persistent - If true, toast won't auto-dismiss (default: false)
168
+ * @returns Toast ID (can be used to dismiss programmatically)
169
+ *
170
+ * @example
171
+ * await PDS.toast('Changes saved!', { type: 'success' });
172
+ *
173
+ * @example
174
+ * await PDS.toast('Error occurred', { type: 'error', persistent: true });
175
+ */
176
+ static toast(
177
+ message: string,
178
+ options?: {
179
+ type?: 'information' | 'success' | 'warning' | 'error';
180
+ duration?: number;
181
+ closable?: boolean;
182
+ persistent?: boolean;
183
+ }
184
+ ): Promise<string>;
185
+
186
+ /**
187
+ * Display a success toast (convenience method).
188
+ *
189
+ * @param message - The success message
190
+ * @param options - Additional toast options (type is preset to 'success')
191
+ * @returns Toast ID
192
+ *
193
+ * @example
194
+ * await PDS.toast.success('Profile updated!');
195
+ */
196
+ static toast: {
197
+ (message: string, options?: { type?: 'information' | 'success' | 'warning' | 'error'; duration?: number; closable?: boolean; persistent?: boolean }): Promise<string>;
198
+ success(message: string, options?: { duration?: number; closable?: boolean; persistent?: boolean }): Promise<string>;
199
+ error(message: string, options?: { duration?: number; closable?: boolean; persistent?: boolean }): Promise<string>;
200
+ warning(message: string, options?: { duration?: number; closable?: boolean; persistent?: boolean }): Promise<string>;
201
+ info(message: string, options?: { duration?: number; closable?: boolean; persistent?: boolean }): Promise<string>;
202
+ };
203
+
204
+ /**
205
+ * Display a modal dialog for user input or confirmation.
206
+ *
207
+ * @param message - The message or prompt to display
208
+ * @param options - Dialog configuration
209
+ * @returns User's response (string for prompt, boolean for confirm, true for alert)
210
+ *
211
+ * @example
212
+ * const confirmed = await PDS.ask('Delete this item?', { type: 'confirm' });
213
+ *
214
+ * @example
215
+ * const name = await PDS.ask('Enter your name:', { type: 'prompt' });
216
+ */
217
+ static ask(message: string, options?: any): Promise<any>;
218
+
157
219
  /**
158
220
  * Current configuration after PDS.start() completes - read-only, frozen after initialization.
159
221
  * Contains the complete configuration used to initialize PDS, including mode, design, preset, and theme.
package/src/js/pds.js CHANGED
@@ -56,6 +56,7 @@ import { findComponentForElement } from "./pds-core/pds-ontology.js";
56
56
  import { presets, defaultLog } from "./pds-core/pds-config.js";
57
57
  import { enums } from "./pds-core/pds-enums.js";
58
58
  import { ask } from "./common/ask.js";
59
+ import { toast } from "./common/toast.js";
59
60
  import { PDSQuery } from "./pds-core/pds-query.js";
60
61
  import * as common from "./common/common.js";
61
62
  import { defaultPDSEnhancers } from "./pds-core/pds-enhancers.js";
@@ -87,6 +88,7 @@ PDS.isLiveMode = () => registry.isLive;
87
88
  PDS.enums = enums;
88
89
 
89
90
  PDS.ask = ask;
91
+ PDS.toast = toast;
90
92
 
91
93
  // Expose common utilities (deepMerge, isObject, etc.)
92
94
  PDS.common = common;
@@ -961,10 +963,27 @@ async function __setupAutoDefinerAndEnhancers(options) {
961
963
  enhancers: mergedEnhancers,
962
964
  onError: (tag, err) => {
963
965
  if (typeof tag === "string" && tag.startsWith("pds-")) {
964
- // No config available in this context, using console
965
- console.warn(
966
- `⚠️ PDS component <${tag}> not found. Assets may not be installed.`
967
- );
966
+ // Check if this is a Lit-dependent component with missing #pds/lit import map
967
+ const litDependentComponents = ['pds-form', 'pds-drawer'];
968
+ const isLitComponent = litDependentComponents.includes(tag);
969
+ const isMissingLitError = err?.message?.includes('#pds/lit') ||
970
+ err?.message?.includes('Failed to resolve module specifier');
971
+
972
+ if (isLitComponent && isMissingLitError) {
973
+ console.error(
974
+ `❌ PDS component <${tag}> requires Lit but #pds/lit is not in import map.\n` +
975
+ `Add this to your HTML <head>:\n` +
976
+ `<script type="importmap">\n` +
977
+ ` { "imports": { "#pds/lit": "./path/to/lit.js" } }\n` +
978
+ `</script>\n` +
979
+ `See: https://github.com/pure-ds/core#lit-components`
980
+ );
981
+ } else {
982
+ // Generic component not found warning
983
+ console.warn(
984
+ `⚠️ PDS component <${tag}> not found. Assets may not be installed.`
985
+ );
986
+ }
968
987
  } else {
969
988
  console.error(`❌ Auto-define error for <${tag}>:`, err);
970
989
  }
@@ -10,12 +10,26 @@ From zero to hero with PDS.
10
10
  npm install @pure-ds/core
11
11
  ```
12
12
 
13
- Create `pds.config.js`:
13
+ **What happens during install:**
14
+
15
+ PDS automatically sets up your project:
16
+ - ✅ Creates `pds.config.js` with commented examples (if it doesn't exist)
17
+ - ✅ Exports static assets to your web root (components, icons, styles)
18
+ - ✅ Copies AI/Copilot instructions to `.github/copilot-instructions.md`
19
+ - ✅ Adds helper scripts to your `package.json` (`pds:export`, `pds:build-icons`)
20
+
21
+ The generated `pds.config.js` includes:
14
22
 
15
23
  ```javascript
24
+ // pds.config.js (auto-generated)
16
25
  export const config = {
17
26
  mode: "live",
18
- preset: "default"
27
+ preset: "default",
28
+
29
+ // Uncomment and customize as needed:
30
+ // design: { colors: { primary: '#007acc' } },
31
+ // enhancers: [ /* custom enhancements */ ],
32
+ // autoDefine: { predefine: ['pds-icon'] }
19
33
  }
20
34
  ```
21
35
 
@@ -23,11 +37,21 @@ Then initialize in your app:
23
37
 
24
38
  ```javascript
25
39
  import { PDS } from '@pure-ds/core';
26
- import { config } from "../../pds.config.js"; // change to match location (project root)
40
+ import { config } from "./pds.config.js"; // project root
27
41
 
28
42
  await PDS.start(config); // That's it! Start writing semantic HTML.
29
43
  ```
30
44
 
45
+ **Manual config generation:**
46
+
47
+ ```bash
48
+ # Create or regenerate config with examples
49
+ npx pds-init-config
50
+
51
+ # Force overwrite existing config
52
+ npx pds-init-config --force
53
+ ```
54
+
31
55
  ### Option B: CDN (Zero Install)
32
56
 
33
57
  Perfect for quick prototypes and learning: