@pure-ds/core 0.6.10 → 0.7.1

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