@pure-ds/core 0.6.10 → 0.6.11

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 (75) hide show
  1. package/custom-elements.json +803 -16
  2. package/dist/types/pds.d.ts +1 -0
  3. package/dist/types/public/assets/js/pds-manager.d.ts +98 -1
  4. package/dist/types/public/assets/js/pds-manager.d.ts.map +1 -1
  5. package/dist/types/public/assets/js/pds.d.ts.map +1 -1
  6. package/dist/types/public/assets/pds/components/pds-live-converter.d.ts +8 -0
  7. package/dist/types/public/assets/pds/components/pds-live-converter.d.ts.map +1 -0
  8. package/dist/types/public/assets/pds/components/pds-live-importer.d.ts +2 -0
  9. package/dist/types/public/assets/pds/components/pds-live-importer.d.ts.map +1 -0
  10. package/dist/types/public/assets/pds/components/pds-live-template-canvas.d.ts +2 -0
  11. package/dist/types/public/assets/pds/components/pds-live-template-canvas.d.ts.map +1 -0
  12. package/dist/types/public/assets/pds/components/pds-omnibox.d.ts.map +1 -1
  13. package/dist/types/public/assets/pds/components/pds-scrollrow.d.ts +20 -0
  14. package/dist/types/public/assets/pds/components/pds-scrollrow.d.ts.map +1 -1
  15. package/dist/types/public/assets/pds/components/pds-toaster.d.ts +1 -1
  16. package/dist/types/public/assets/pds/components/pds-toaster.d.ts.map +1 -1
  17. package/dist/types/public/assets/pds/components/pds-treeview.d.ts +37 -0
  18. package/dist/types/public/assets/pds/components/pds-treeview.d.ts.map +1 -0
  19. package/dist/types/src/js/common/toast.d.ts +8 -0
  20. package/dist/types/src/js/common/toast.d.ts.map +1 -1
  21. package/dist/types/src/js/pds-core/pds-enhancers.d.ts.map +1 -1
  22. package/dist/types/src/js/pds-core/pds-generator.d.ts.map +1 -1
  23. package/dist/types/src/js/pds-core/pds-live.d.ts +2 -1
  24. package/dist/types/src/js/pds-core/pds-live.d.ts.map +1 -1
  25. package/dist/types/src/js/pds-live-manager/conversion-service.d.ts +66 -0
  26. package/dist/types/src/js/pds-live-manager/conversion-service.d.ts.map +1 -0
  27. package/dist/types/src/js/pds-live-manager/import-contract.d.ts +15 -0
  28. package/dist/types/src/js/pds-live-manager/import-contract.d.ts.map +1 -0
  29. package/dist/types/src/js/pds-live-manager/import-history-service.d.ts +32 -0
  30. package/dist/types/src/js/pds-live-manager/import-history-service.d.ts.map +1 -0
  31. package/dist/types/src/js/pds-live-manager/import-service.d.ts +21 -0
  32. package/dist/types/src/js/pds-live-manager/import-service.d.ts.map +1 -0
  33. package/dist/types/src/js/pds-live-manager/template-service.d.ts +17 -0
  34. package/dist/types/src/js/pds-live-manager/template-service.d.ts.map +1 -0
  35. package/dist/types/src/js/pds-manager.d.ts +4 -0
  36. package/dist/types/src/js/pds.d.ts.map +1 -1
  37. package/package.json +6 -2
  38. package/packages/pds-cli/README.md +51 -0
  39. package/packages/pds-cli/bin/pds-import.js +176 -0
  40. package/packages/pds-cli/bin/pds-static.js +15 -0
  41. package/packages/pds-cli/bin/postinstall.mjs +17 -8
  42. package/public/assets/js/app.js +15 -139
  43. package/public/assets/js/pds-manager.js +358 -255
  44. package/public/assets/js/pds.js +7 -7
  45. package/public/assets/pds/components/pds-live-converter.js +47 -0
  46. package/public/assets/pds/components/pds-live-edit.js +760 -43
  47. package/public/assets/pds/components/pds-live-importer.js +772 -0
  48. package/public/assets/pds/components/pds-live-template-canvas.js +171 -0
  49. package/public/assets/pds/components/pds-omnibox.js +136 -2
  50. package/public/assets/pds/components/pds-scrollrow.js +56 -1
  51. package/public/assets/pds/components/pds-toaster.js +50 -5
  52. package/public/assets/pds/components/pds-treeview.js +972 -0
  53. package/public/assets/pds/custom-elements.json +803 -16
  54. package/public/assets/pds/pds-css-complete.json +7 -2
  55. package/public/assets/pds/templates/commerce-scroll-explorer.html +115 -0
  56. package/public/assets/pds/templates/content-brand-showcase.html +110 -0
  57. package/public/assets/pds/templates/feedback-ops-dashboard.html +91 -0
  58. package/public/assets/pds/templates/release-readiness-radar.html +69 -0
  59. package/public/assets/pds/templates/support-command-center.html +92 -0
  60. package/public/assets/pds/templates/templates.json +53 -0
  61. package/public/assets/pds/templates/workspace-settings-lab.html +131 -0
  62. package/public/assets/pds/vscode-custom-data.json +54 -4
  63. package/readme.md +34 -0
  64. package/src/js/pds-core/pds-config.js +9 -9
  65. package/src/js/pds-core/pds-enhancers.js +146 -0
  66. package/src/js/pds-core/pds-generator.js +170 -29
  67. package/src/js/pds-core/pds-live.js +453 -13
  68. package/src/js/pds-live-manager/conversion-service.js +3136 -0
  69. package/src/js/pds-live-manager/import-contract.js +57 -0
  70. package/src/js/pds-live-manager/import-history-service.js +145 -0
  71. package/src/js/pds-live-manager/import-service.js +255 -0
  72. package/src/js/pds-live-manager/tailwind-conversion-rules.json +383 -0
  73. package/src/js/pds-live-manager/template-service.js +170 -0
  74. package/src/js/pds.d.ts +1 -0
  75. package/src/js/pds.js +35 -0
@@ -0,0 +1,171 @@
1
+ const COMPONENT_TAG = "pds-live-template-canvas";
2
+ const PDS = globalThis.PDS;
3
+
4
+ let managerPromise = null;
5
+
6
+ async function getManagerModule() {
7
+ if (managerPromise) return managerPromise;
8
+ managerPromise = (async () => {
9
+ const candidates = [
10
+ PDS?.currentConfig?.managerURL,
11
+ "../core/pds-manager.js",
12
+ "/assets/pds/core/pds-manager.js",
13
+ ].filter(Boolean);
14
+
15
+ const attempted = new Set();
16
+ for (const candidate of candidates) {
17
+ try {
18
+ const resolved = new URL(candidate, import.meta.url).href;
19
+ if (attempted.has(resolved)) continue;
20
+ attempted.add(resolved);
21
+ const mod = await import(resolved);
22
+ if (mod && typeof mod === "object") return mod;
23
+ } catch (e) {}
24
+ }
25
+
26
+ return null;
27
+ })();
28
+
29
+ return managerPromise;
30
+ }
31
+
32
+ class PdsLiveTemplateCanvas extends HTMLElement {
33
+ constructor() {
34
+ super();
35
+ this._templates = [];
36
+ this._selectedTemplateId = "";
37
+ }
38
+
39
+ connectedCallback() {
40
+ this._renderShell();
41
+ this._loadTemplates();
42
+ }
43
+
44
+ _renderShell() {
45
+ this.className = "stack-sm";
46
+ this.innerHTML = `
47
+ <label class="stack-xs">
48
+ <span>Template</span>
49
+ <div class="pds-live-template-omnibox"></div>
50
+ </label>
51
+ <p class="text-muted pds-live-template-description"></p>
52
+ `;
53
+
54
+ }
55
+
56
+ async _loadTemplates() {
57
+ const manager = await getManagerModule();
58
+ if (typeof manager?.listLiveTemplates !== "function") return;
59
+
60
+ this._templates = (await manager.listLiveTemplates()) || [];
61
+ this._selectedTemplateId = "";
62
+
63
+ const host = this.querySelector(".pds-live-template-omnibox");
64
+ if (!host) return;
65
+
66
+ host.replaceChildren();
67
+ try {
68
+ const omnibox = document.createElement("pds-omnibox");
69
+ omnibox.setAttribute("item-grid", "45px 1fr 0");
70
+ omnibox.setAttribute("placeholder", "Search canvas templates...");
71
+ omnibox.value = "";
72
+
73
+ const templates = this._templates;
74
+ omnibox.settings = {
75
+ hideCategory: true,
76
+ itemGrid: "45px 1fr 0",
77
+ categories: {
78
+ Templates: {
79
+ trigger: () => true,
80
+ getItems: (context = {}) => {
81
+ const query = String(context?.search || "").toLowerCase().trim();
82
+ return templates
83
+ .filter((item) => {
84
+ if (!query) return true;
85
+ const haystack = [item.name, item.description, ...(item.tags || [])]
86
+ .join(" ")
87
+ .toLowerCase();
88
+ return haystack.includes(query);
89
+ })
90
+ .map((item) => ({
91
+ id: item.id,
92
+ text: item.name,
93
+ description: item.description,
94
+ icon: item.icon || "grid-four",
95
+ }));
96
+ },
97
+ action: (selection) => {
98
+ this._selectedTemplateId = selection?.id || "";
99
+ this._updateDescription();
100
+ this._applyTemplate();
101
+ return selection?.id || "";
102
+ },
103
+ },
104
+ },
105
+ };
106
+
107
+ omnibox.addEventListener("result-selected", (event) => {
108
+ const selectedId =
109
+ event?.detail?.id ||
110
+ this._templates.find((item) => item.name === event?.detail?.text)?.id ||
111
+ this._selectedTemplateId;
112
+ if (selectedId) {
113
+ this._selectedTemplateId = selectedId;
114
+ }
115
+ const selected = this._templates.find((item) => item.id === this._selectedTemplateId);
116
+ if (selected?.name) {
117
+ omnibox.value = selected.name;
118
+ }
119
+ this._updateDescription();
120
+ });
121
+
122
+ host.appendChild(omnibox);
123
+ } catch (error) {
124
+ const fallback = document.createElement("select");
125
+ fallback.className = "pds-live-template-select";
126
+ this._templates.forEach((template) => {
127
+ const option = document.createElement("option");
128
+ option.value = template.id;
129
+ option.textContent = template.name;
130
+ fallback.appendChild(option);
131
+ });
132
+ fallback.addEventListener("change", () => {
133
+ this._selectedTemplateId = fallback.value;
134
+ this._updateDescription();
135
+ this._applyTemplate();
136
+ });
137
+ host.appendChild(fallback);
138
+ }
139
+
140
+ this._updateDescription();
141
+ }
142
+
143
+ _updateDescription() {
144
+ const description = this.querySelector(".pds-live-template-description");
145
+ if (!description) return;
146
+ const selected = this._templates.find((item) => item.id === this._selectedTemplateId);
147
+ description.textContent = selected?.description || "";
148
+ }
149
+
150
+ async _applyTemplate() {
151
+ if (!this._selectedTemplateId) return;
152
+
153
+ const manager = await getManagerModule();
154
+ if (typeof manager?.runLiveImport !== "function") return;
155
+
156
+ const result = await manager.runLiveImport({
157
+ sourceType: "template",
158
+ templateId: this._selectedTemplateId,
159
+ });
160
+
161
+ this.dispatchEvent(
162
+ new CustomEvent("pds:live-template:inject", {
163
+ bubbles: true,
164
+ composed: true,
165
+ detail: { result, templateId: this._selectedTemplateId },
166
+ })
167
+ );
168
+ }
169
+ }
170
+
171
+ customElements.define(COMPONENT_TAG, PdsLiveTemplateCanvas);
@@ -648,6 +648,8 @@ export class PdsOmnibox extends HTMLElement {
648
648
  if (!suggestion) return;
649
649
  this.#suggestionsObserver = new MutationObserver(() => {
650
650
  if (!suggestion.classList.contains("ac-active")) {
651
+ this.removeAttribute("data-suggestions-open");
652
+ this.#clearSuggestionOverlayStyles(suggestion);
651
653
  this.#resetIconToDefault();
652
654
  }
653
655
  });
@@ -682,6 +684,106 @@ export class PdsOmnibox extends HTMLElement {
682
684
  this.#autoCompleteViewportHandler = null;
683
685
  }
684
686
 
687
+ #clearSuggestionOverlayStyles(suggestion) {
688
+ if (!suggestion || !suggestion.style) return;
689
+ suggestion.style.removeProperty("position");
690
+ suggestion.style.removeProperty("left");
691
+ suggestion.style.removeProperty("right");
692
+ suggestion.style.removeProperty("top");
693
+ suggestion.style.removeProperty("bottom");
694
+ suggestion.style.removeProperty("width");
695
+ suggestion.style.removeProperty("max-width");
696
+ }
697
+
698
+ #getComposedParent(node) {
699
+ if (!node) return null;
700
+ if (node.parentElement) return node.parentElement;
701
+ const root = node.getRootNode?.();
702
+ if (root && root.host) return root.host;
703
+ return null;
704
+ }
705
+
706
+ #hasTransformedAncestor(startNode) {
707
+ let current = startNode;
708
+ let safety = 0;
709
+ while (current && safety < 80) {
710
+ if (current instanceof Element) {
711
+ const style = getComputedStyle(current);
712
+ if (
713
+ style.transform !== "none" ||
714
+ style.perspective !== "none" ||
715
+ style.filter !== "none" ||
716
+ style.backdropFilter !== "none"
717
+ ) {
718
+ return true;
719
+ }
720
+ }
721
+ current = this.#getComposedParent(current);
722
+ safety += 1;
723
+ }
724
+ return false;
725
+ }
726
+
727
+ #shouldUseFixedSuggestionOverlay(container) {
728
+ if (!container) return false;
729
+ if (this.closest("pds-drawer, dialog")) return false;
730
+ return !this.#hasTransformedAncestor(this);
731
+ }
732
+
733
+ #positionSuggestionInline({ container, suggestion, rect, direction, offset, maxHeight }) {
734
+ if (!container || !suggestion) return;
735
+ this.#clearSuggestionOverlayStyles(suggestion);
736
+ if (direction === "up") {
737
+ suggestion.style.top = "auto";
738
+ suggestion.style.bottom = `${Math.round(rect.height + offset)}px`;
739
+ } else {
740
+ suggestion.style.bottom = "auto";
741
+ suggestion.style.top = `${Math.round(rect.height + offset)}px`;
742
+ }
743
+ container.setAttribute("data-direction", direction);
744
+ suggestion.setAttribute("data-direction", direction);
745
+ container.style.setProperty("--ac-max-height", `${maxHeight}px`);
746
+ }
747
+
748
+ #positionSuggestionOverlay({
749
+ container,
750
+ suggestion,
751
+ rect,
752
+ viewportHeight,
753
+ viewportWidth,
754
+ direction,
755
+ maxHeight,
756
+ gap,
757
+ offset,
758
+ }) {
759
+ if (!container || !suggestion || !suggestion.style) return;
760
+ if (suggestion.classList.contains("full-mobile")) {
761
+ this.#clearSuggestionOverlayStyles(suggestion);
762
+ return;
763
+ }
764
+
765
+ const clampedLeft = Math.max(gap, Math.min(rect.left, viewportWidth - rect.width - gap));
766
+ const clampedWidth = Math.max(0, Math.min(rect.width, viewportWidth - gap * 2));
767
+
768
+ suggestion.style.position = "fixed";
769
+ suggestion.style.left = `${Math.round(clampedLeft)}px`;
770
+ suggestion.style.width = `${Math.round(clampedWidth)}px`;
771
+ suggestion.style.maxWidth = `${Math.round(Math.max(0, viewportWidth - gap * 2))}px`;
772
+ suggestion.style.right = "auto";
773
+
774
+ if (direction === "up") {
775
+ suggestion.style.top = "auto";
776
+ suggestion.style.bottom = `${Math.round(viewportHeight - rect.top + offset)}px`;
777
+ } else {
778
+ suggestion.style.bottom = "auto";
779
+ suggestion.style.top = `${Math.round(rect.bottom + offset)}px`;
780
+ }
781
+
782
+ container.setAttribute("data-direction", direction);
783
+ suggestion.setAttribute("data-direction", direction);
784
+ container.style.setProperty("--ac-max-height", `${maxHeight}px`);
785
+ }
786
+
685
787
  #updateSuggestionMaxHeight() {
686
788
  if (!this.#input) return;
687
789
  const container = this.#input.parentElement;
@@ -696,6 +798,7 @@ export class PdsOmnibox extends HTMLElement {
696
798
  suggestion?.getAttribute("data-direction") ||
697
799
  container.getAttribute("data-direction") ||
698
800
  "down";
801
+ const offset = this.#readSpacingToken(container, "--ac-suggest-offset") || 0;
699
802
 
700
803
  const availableDown = viewportHeight - rect.bottom - gap;
701
804
  const availableUp = rect.top - gap;
@@ -705,8 +808,6 @@ export class PdsOmnibox extends HTMLElement {
705
808
  container.setAttribute("data-direction", direction);
706
809
  if (suggestion) suggestion.setAttribute("data-direction", direction);
707
810
  if (suggestion) {
708
- const offset =
709
- this.#readSpacingToken(container, "--ac-suggest-offset") || 0;
710
811
  if (direction === "up") {
711
812
  suggestion.style.top = "auto";
712
813
  suggestion.style.bottom = `${rect.height + offset}px`;
@@ -726,6 +827,39 @@ export class PdsOmnibox extends HTMLElement {
726
827
  Math.floor(Math.min(available, configuredMaxHeight)),
727
828
  );
728
829
  container.style.setProperty("--ac-max-height", `${maxHeight}px`);
830
+
831
+ const isSuggestionActive = suggestion?.classList?.contains("ac-active");
832
+ this.toggleAttribute("data-suggestions-open", Boolean(isSuggestionActive));
833
+
834
+ if (!suggestion || !isSuggestionActive || suggestion.classList.contains("full-mobile")) {
835
+ this.#clearSuggestionOverlayStyles(suggestion);
836
+ return;
837
+ }
838
+
839
+ if (!this.#shouldUseFixedSuggestionOverlay(container)) {
840
+ this.#positionSuggestionInline({
841
+ container,
842
+ suggestion,
843
+ rect,
844
+ direction,
845
+ offset,
846
+ maxHeight,
847
+ });
848
+ return;
849
+ }
850
+
851
+ const viewportWidth = window.visualViewport?.width || window.innerWidth;
852
+ this.#positionSuggestionOverlay({
853
+ container,
854
+ suggestion,
855
+ rect,
856
+ viewportHeight,
857
+ viewportWidth,
858
+ direction,
859
+ maxHeight,
860
+ gap,
861
+ offset,
862
+ });
729
863
  }
730
864
 
731
865
  #resolveSuggestionDirection(container) {
@@ -11,6 +11,8 @@
11
11
  *
12
12
  * @attr {string} label - Accessible label for the scroll region; also used as fallback heading copy
13
13
  * @attr {"start"|"center"} snap - Snap alignment for tiles when scrolling (default: start)
14
+ * @attr {string} tile-min - Minimum tile width (CSS length, e.g. "250px")
15
+ * @attr {string} tile-max - Maximum tile width (CSS length, e.g. "360px")
14
16
  */
15
17
  class PdsScrollrow extends HTMLElement {
16
18
  #viewport;
@@ -19,7 +21,7 @@ class PdsScrollrow extends HTMLElement {
19
21
  #adopted = false;
20
22
 
21
23
  static get observedAttributes() {
22
- return ["label", "snap"];
24
+ return ["label", "snap", "tile-min", "tile-max"];
23
25
  }
24
26
 
25
27
  constructor() {
@@ -128,6 +130,36 @@ class PdsScrollrow extends HTMLElement {
128
130
  else this.setAttribute("snap", String(val));
129
131
  }
130
132
 
133
+ /**
134
+ * Minimum tile size applied to slotted content.
135
+ * @returns {string|null}
136
+ */
137
+ get tileMin() {
138
+ return this.getAttribute("tile-min");
139
+ }
140
+ /**
141
+ * @param {string|null} val
142
+ */
143
+ set tileMin(val) {
144
+ if (val == null) this.removeAttribute("tile-min");
145
+ else this.setAttribute("tile-min", String(val));
146
+ }
147
+
148
+ /**
149
+ * Maximum tile size applied to slotted content.
150
+ * @returns {string|null}
151
+ */
152
+ get tileMax() {
153
+ return this.getAttribute("tile-max");
154
+ }
155
+ /**
156
+ * @param {string|null} val
157
+ */
158
+ set tileMax(val) {
159
+ if (val == null) this.removeAttribute("tile-max");
160
+ else this.setAttribute("tile-max", String(val));
161
+ }
162
+
131
163
  /**
132
164
  * Lifecycle hook called when the element is inserted into the document.
133
165
  */
@@ -189,6 +221,11 @@ class PdsScrollrow extends HTMLElement {
189
221
  this.#applySnap();
190
222
  break;
191
223
  }
224
+ case "tile-min":
225
+ case "tile-max": {
226
+ this.#applyTileSizing();
227
+ break;
228
+ }
192
229
  }
193
230
  }
194
231
 
@@ -256,6 +293,7 @@ class PdsScrollrow extends HTMLElement {
256
293
 
257
294
  // Apply initial snap alignment
258
295
  this.#applySnap();
296
+ this.#applyTileSizing();
259
297
 
260
298
  // Observe size changes to refresh controls
261
299
  this.#ro = new ResizeObserver(() => this.#updateControls());
@@ -274,6 +312,23 @@ class PdsScrollrow extends HTMLElement {
274
312
  }
275
313
  }
276
314
 
315
+ #applyTileSizing() {
316
+ const tileMin = this.tileMin;
317
+ const tileMax = this.tileMax;
318
+
319
+ if (tileMin && String(tileMin).trim()) {
320
+ this.style.setProperty("--tile-min", String(tileMin).trim());
321
+ } else {
322
+ this.style.removeProperty("--tile-min");
323
+ }
324
+
325
+ if (tileMax && String(tileMax).trim()) {
326
+ this.style.setProperty("--tile-max", String(tileMax).trim());
327
+ } else {
328
+ this.style.removeProperty("--tile-max");
329
+ }
330
+ }
331
+
277
332
  /**
278
333
  * Scroll the viewport by roughly one page in the indicated direction.
279
334
  * @param {Event} e
@@ -124,6 +124,14 @@ export class AppToaster extends HTMLElement {
124
124
  white-space: pre-line;
125
125
  }
126
126
 
127
+ aside.toast .toast-content {
128
+ width: 100%;
129
+ }
130
+
131
+ aside.toast .toast-action {
132
+ margin-top: var(--spacing-2);
133
+ }
134
+
127
135
  /* Mobile responsive toast positioning */
128
136
  @_media (max-width: 640px) {
129
137
  :host {
@@ -182,19 +190,23 @@ export class AppToaster extends HTMLElement {
182
190
  duration: null, // auto-calculated based on reading time
183
191
  closable: true,
184
192
  persistent: false, // if true, doesn't auto-dismiss
193
+ html: false,
194
+ action: null,
185
195
  };
186
196
 
187
197
  const config = { ...defaults, ...options };
188
198
 
189
199
  // Calculate reading time (average 200 words per minute)
190
- const wordCount = message.split(/\s+/).length;
200
+ const messageText = String(message || "");
201
+ const readingText = config.html ? messageText.replace(/<[^>]*>/g, " ") : messageText;
202
+ const wordCount = readingText.split(/\s+/).filter(Boolean).length;
191
203
  const baseReadingTime = Math.max(2000, (wordCount / 200) * 60 * 1000); // minimum 2 seconds
192
204
 
193
205
  // Extend time for errors (people need more time to process error messages)
194
206
  const multiplier = config.type === "error" ? 1.5 : 1;
195
207
  const duration = config.duration || baseReadingTime * multiplier;
196
208
 
197
- return this.#showToast(message, config, duration);
209
+ return this.#showToast(messageText, config, duration);
198
210
  }
199
211
 
200
212
  /*
@@ -213,7 +225,9 @@ export class AppToaster extends HTMLElement {
213
225
  config.type,
214
226
  config.closable,
215
227
  duration,
216
- config.persistent
228
+ config.persistent,
229
+ config.html,
230
+ config.action
217
231
  );
218
232
 
219
233
  // Add to DOM
@@ -247,7 +261,7 @@ export class AppToaster extends HTMLElement {
247
261
  * @param {boolean} persistent
248
262
  * @returns {HTMLElement}
249
263
  */
250
- createToastElement(id, message, type, closable, duration, persistent) {
264
+ createToastElement(id, message, type, closable, duration, persistent, html = false, action = null) {
251
265
  const toast = document.createElement("aside");
252
266
  toast.className = `toast callout ${this.#getAlertClass(type)}`;
253
267
  toast.setAttribute("data-toast-id", id);
@@ -260,6 +274,7 @@ export class AppToaster extends HTMLElement {
260
274
  icon.setAttribute("size", "lg");
261
275
 
262
276
  const content = document.createElement("div");
277
+ content.className = "toast-content";
263
278
 
264
279
  const title = document.createElement("div");
265
280
  title.className = "callout-title";
@@ -267,11 +282,41 @@ export class AppToaster extends HTMLElement {
267
282
 
268
283
  const text = document.createElement("p");
269
284
  text.style.margin = "0";
270
- text.textContent = message;
285
+ if (html) {
286
+ text.innerHTML = String(message || "");
287
+ } else {
288
+ text.textContent = String(message || "");
289
+ }
271
290
 
272
291
  content.appendChild(title);
273
292
  content.appendChild(text);
274
293
 
294
+ if (action && typeof action === "object" && action.label) {
295
+ const actionButton = document.createElement("button");
296
+ actionButton.className = "btn-outline btn-sm toast-action";
297
+ actionButton.type = "button";
298
+ const actionLabel = document.createElement("span");
299
+ actionLabel.textContent = String(action.label);
300
+
301
+ if (action.icon) {
302
+ const icon = document.createElement("pds-icon");
303
+ icon.setAttribute("icon", String(action.icon));
304
+ icon.setAttribute("size", "sm");
305
+ actionButton.appendChild(icon);
306
+ }
307
+
308
+ actionButton.appendChild(actionLabel);
309
+ actionButton.addEventListener("click", () => {
310
+ if (typeof action.onClick === "function") {
311
+ action.onClick();
312
+ }
313
+ if (action.dismissOnClick !== false) {
314
+ this.dismissToast(id);
315
+ }
316
+ });
317
+ content.appendChild(actionButton);
318
+ }
319
+
275
320
  toast.appendChild(icon);
276
321
  toast.appendChild(content);
277
322