mypgs 1.6.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/AGENTS.md +2 -2
  2. package/assets/javascript/components/_accordion.js +2 -2
  3. package/assets/javascript/components/_modals.js +159 -136
  4. package/assets/javascript/components/_notifications.js +19 -9
  5. package/assets/javascript/components/_search.js +2 -2
  6. package/assets/javascript/components/_slides.js +66 -32
  7. package/assets/javascript/components/_stepTabs.js +4 -4
  8. package/assets/javascript/components/_steps.js +2 -2
  9. package/assets/javascript/components/_summary.js +2 -2
  10. package/assets/javascript/functions/_formValidate.js +97 -53
  11. package/assets/javascript/functions/_scrollY.js +10 -10
  12. package/assets/javascript/layout/_header.js +3 -0
  13. package/assets/scss/base/_html.scss +2 -2
  14. package/assets/scss/base/_variables.scss +2 -2
  15. package/assets/scss/components/_form.scss +2 -2
  16. package/assets/scss/components/_notification.scss +23 -30
  17. package/assets/scss/components/_search.scss +27 -21
  18. package/assets/scss/layout/_pageShell.scss +18 -15
  19. package/assets/scss/mixin/_mx-form.scss +7 -7
  20. package/{templates/html → demo}/demo.html +1 -1
  21. package/demo/demo.js +240 -0
  22. package/dist/css/index.css +86 -92
  23. package/dist/css/index.css.map +1 -1
  24. package/dist/css/index.min.css +1 -1
  25. package/dist/javascript/index.js +356 -255
  26. package/dist/javascript/index.js.map +1 -1
  27. package/dist/javascript/index.min.js +1 -1
  28. package/docs/componenti-e-markup.md +26 -27
  29. package/docs/components/accordion.md +61 -0
  30. package/docs/components/badges.md +67 -0
  31. package/docs/components/breadcumbs.md +43 -0
  32. package/docs/components/button.md +59 -0
  33. package/docs/components/card.md +50 -0
  34. package/docs/components/dropdown.md +135 -0
  35. package/docs/components/form.md +113 -0
  36. package/docs/components/logo.md +36 -0
  37. package/docs/components/menu.md +90 -0
  38. package/docs/components/modal.md +131 -0
  39. package/docs/components/notification.md +81 -0
  40. package/docs/components/search.md +117 -0
  41. package/docs/components/slides.md +85 -0
  42. package/docs/components/stepTabs.md +93 -0
  43. package/docs/components/steps.md +56 -0
  44. package/docs/components/summary.md +49 -0
  45. package/docs/components/table.md +42 -0
  46. package/docs/components/tooltip.md +53 -0
  47. package/docs/helper-javascript.md +68 -3
  48. package/docs/layout/body.md +41 -0
  49. package/docs/layout/flex.md +116 -0
  50. package/docs/layout/footer.md +71 -0
  51. package/docs/layout/grid.md +116 -0
  52. package/docs/layout/header.md +116 -0
  53. package/docs/layout/pageShell.md +94 -0
  54. package/docs/layout/section.md +69 -0
  55. package/docs/patterns/cookieConsent.md +89 -0
  56. package/package.json +2 -1
  57. package/scripts/generate-component-docs.js +557 -0
  58. package/templates/html/components/accordion.html +33 -4
  59. package/templates/html/components/badges.html +23 -0
  60. package/templates/html/components/breadcumbs.html +14 -0
  61. package/templates/html/components/button.html +22 -0
  62. package/templates/html/components/card.html +20 -0
  63. package/templates/html/components/dropdown.html +34 -0
  64. package/templates/html/components/form.html +82 -4
  65. package/templates/html/components/logo.html +21 -0
  66. package/templates/html/components/menu.html +33 -6
  67. package/templates/html/components/modal.html +44 -0
  68. package/templates/html/components/notification.html +68 -32
  69. package/templates/html/components/search.html +76 -0
  70. package/templates/html/components/slides.html +43 -0
  71. package/templates/html/components/stepTabs.html +47 -0
  72. package/templates/html/components/steps.html +24 -0
  73. package/templates/html/components/summary.html +27 -0
  74. package/templates/html/components/table.html +12 -0
  75. package/templates/html/components/tooltip.html +35 -0
  76. package/templates/html/layout/body.html +20 -5
  77. package/templates/html/layout/flex.html +20 -0
  78. package/templates/html/layout/footer.html +29 -1
  79. package/templates/html/layout/grid.html +20 -0
  80. package/templates/html/layout/header.html +41 -0
  81. package/templates/html/layout/pageShell.html +19 -0
  82. package/templates/html/layout/section.html +32 -10
  83. package/templates/html/patterns/cookieConsent.html +31 -0
  84. package/templates/react/components/form.jsx +3 -3
  85. package/templates/react/components/notification.jsx +4 -4
  86. package/templates/react/patterns/footer.jsx +1 -1
  87. package/templates/html/demo.js +0 -130
  88. /package/{templates/html → demo}/demo.css +0 -0
package/AGENTS.md CHANGED
@@ -71,7 +71,7 @@ Patterns found:
71
71
 
72
72
  - `pgs` is the main attribute: tokens are separated by spaces, for example `pgs="button modal-button"`.
73
73
  - `assets/scss/index.scss` imports base, layout, components, and patterns; layouts/components/patterns are almost all scoped under `[pgs~=initP]`.
74
- - The main markup must enable the library with `htmlBase initP`, and the body with base body tokens. Use `templates/demo.html` and `templates/html/layout/body.html` as the canonical references.
74
+ - The main markup must enable the library with `htmlBase initP`, and the body with base body tokens. Use `demo/demo.html` and `templates/html/layout/body.html` as the canonical references.
75
75
  - Components use a root token and child tokens with a consistent prefix. Use `templates/html/components/` as the canonical markup source.
76
76
 
77
77
  - Runtime states use `pgs-state`, for example `open`, `is-active`, `is-completed`, `is-locked`, `success`, `error`, `warning`, and `info`.
@@ -229,7 +229,7 @@ Canonical template references:
229
229
  - Layouts: `templates/html/layout/body.html`, `templates/html/layout/flex.html`, `templates/html/layout/grid.html`, `templates/html/layout/pageShell.html`, `templates/html/layout/section.html`
230
230
  - Components: `templates/html/components/accordion.html`, `templates/html/components/breadcumbs.html`, `templates/html/components/button.html`, `templates/html/components/card.html`, `templates/html/components/dropdown.html`, `templates/html/components/form.html`, `templates/html/components/logo.html`, `templates/html/components/menu.html`, `templates/html/components/modal.html`, `templates/html/components/notification.html`, `templates/html/components/search.html`, `templates/html/components/slides.html`, `templates/html/components/stepTabs.html`, `templates/html/components/steps.html`, `templates/html/components/table.html`, `templates/html/components/tooltip.html`
231
231
  - Patterns: `templates/html/patterns/cookieConsent.html`, `templates/html/patterns/footer.html`, `templates/html/patterns/header.html`
232
- - Complete demo assembly. DO NOT use this for inspiration. It is only used to show all the modules: `templates/demo.html`
232
+ - Complete demo assembly. DO NOT use this for inspiration. It is only used to show all the modules: `demo/demo.html`
233
233
 
234
234
  Before creating markup, open the relevant template and reuse its current structure, tokens, ARIA attributes, and `pgs-option` syntax.
235
235
 
@@ -11,7 +11,7 @@ function directPgsChild(element, token) {
11
11
  return Array.from(element.children).find(child => pgs(child).contains(token));
12
12
  }
13
13
 
14
- export function PGS_accordion_init(root = document) {
14
+ function PGS_accordion_init(root = document) {
15
15
  pgs(root).querySelectorAll("accordion").forEach((accordion) => {
16
16
  if (API.has(accordion)) return;
17
17
 
@@ -115,7 +115,7 @@ export function PGS_accordion_init(root = document) {
115
115
  PGS_accordion_init();
116
116
 
117
117
  //# API
118
- export function PGS_accordion_api(selector) {
118
+ function PGS_accordion_api(selector) {
119
119
  return API.get(selector);
120
120
  }
121
121
 
@@ -1,161 +1,184 @@
1
1
  //# MODAL
2
2
  const API = new WeakMap();
3
3
 
4
- export function PGS_modal_init(selector = "modal") {
5
- pgs(document).querySelectorAll(selector).forEach(MODAL => {
6
- if (API.has(MODAL)) return;
7
-
8
- const BUTTON_OPEN = pgs(MODAL).querySelector("modal-button");
9
- const DIALOG = MODAL.querySelector("dialog");
10
- if (!BUTTON_OPEN || !DIALOG) return;
11
-
12
- //== SELECTOR
13
- const DOMButtonClose = '<button pgs="buttonClose modal-close" type="button" tabindex="0" aria-label="Chiudi"><i class="fa-solid fa-close"></i></button>';
14
- const modalContentHeader = pgs(MODAL)?.querySelector("modal-dialog-content-header")
15
-
16
-
17
- //== OPTION ATTRIBUTES MODAL
18
- const disableBackdropClose = pgs(MODAL).option.contains("disableBackdropClose")
19
- const data_history = pgs(MODAL).option.contains("history");
20
- const data_container = pgs(MODAL).option.getValueBrackets("containerID");
21
- const data_containerPGS = pgs(MODAL).option.getValueBrackets("containerPGS");
22
-
23
- //== OPTION ATTRIBUTES DIALOG
24
- const topLevel = pgs(DIALOG).option.contains("topLevel")
25
-
26
-
27
- //== BUTTON CLOSE
28
- if (pgs(MODAL).querySelector("modal-close")) null
29
- else if (modalContentHeader) modalContentHeader.insertAdjacentHTML("beforeend", DOMButtonClose)
30
- else DIALOG.insertAdjacentHTML("beforeend", DOMButtonClose)
31
- const BUTTON_CLOSE = pgs(MODAL).querySelector("modal-close")
32
-
33
-
34
- //== SET
35
- pgs(DIALOG).add("dialog modal-dialog");
36
-
37
- //== BUTTON OPEN
38
- BUTTON_OPEN.setAttribute("role", "button");
39
- BUTTON_OPEN.setAttribute("aria-label", "apri modale");
40
-
41
-
42
- //== POSITION
43
- if(!topLevel){
44
- if (data_container) document.querySelector("#" + data_container)?.append(DIALOG);
45
- else if (data_containerPGS) pgs(document).querySelector(data_containerPGS)?.append(DIALOG);
46
- else document.body.append(DIALOG);
47
- }
4
+ function getModals(root) {
5
+ const modals = root instanceof Element && pgs(root).contains("modal") ? [root] : [];
6
+ modals.push(...pgs(root).querySelectorAll("modal"));
7
+ return modals;
8
+ }
48
9
 
10
+ function initializeModal(MODAL, existingDialog = null) {
11
+ if (API.has(MODAL)) return;
49
12
 
50
- //+ FN STATUS
51
- function statusModal(status = true) {
52
- BUTTON_OPEN?.setAttribute("aria-expanded", status);
53
- DIALOG?.setAttribute("aria-expanded", status);
54
- }
13
+ const BUTTON_OPEN = pgs(MODAL).querySelector("modal-button");
14
+ const DIALOG = existingDialog || MODAL.querySelector("dialog");
15
+ if (!BUTTON_OPEN || !DIALOG) return;
16
+ const eventController = new AbortController();
17
+ const { signal } = eventController;
18
+ let historyObserver = null;
19
+ let historyTimeout = null;
55
20
 
56
- //+ FN OPEN
57
- function openModal(e) {
58
- e?.stopImmediatePropagation();
59
- if (DIALOG.open) {
60
- closeModal(e);
61
- return;
62
- }
63
-
64
- if (!DIALOG.open) document.querySelectorAll("dialog[open]").forEach((dlg) => dlg.close());
65
- statusModal(true);
66
- topLevel ? DIALOG.showModal() : DIALOG.show();
67
- // modalCustomEvents('modal:open', { event: e });
68
- MODAL.dispatchEvent(new CustomEvent('modal:open'));
69
- DIALOG.dispatchEvent(new CustomEvent('modal:open'));
70
- }
21
+ //== SELECTOR
22
+ const DOMButtonClose = '<button pgs="buttonClose modal-close" type="button" tabindex="0" aria-label="Chiudi"><i class="fa-solid fa-close"></i></button>';
23
+ const modalContentHeader = pgs(DIALOG).querySelector("modal-dialog-content-header");
71
24
 
72
- //+ FN CLOSE
73
- function closeModal(e) {
74
- e?.stopImmediatePropagation()
75
- statusModal(false);
76
- DIALOG.close();
77
- // modalCustomEvents('modal:close', { event: e });
78
- MODAL.dispatchEvent(new CustomEvent('modal:close'));
79
- DIALOG.dispatchEvent(new CustomEvent('modal:close'));
80
- }
81
25
 
82
- function forceOpen(e) {
83
- if (!DIALOG.open) openModal(e);
84
- }
26
+ //== OPTION ATTRIBUTES MODAL
27
+ const disableBackdropClose = pgs(MODAL).option.contains("disableBackdropClose");
28
+ const data_history = pgs(MODAL).option.contains("history");
29
+ const data_container = pgs(MODAL).option.getValueBrackets("containerID");
30
+ const data_containerPGS = pgs(MODAL).option.getValueBrackets("containerPGS");
85
31
 
86
- function forceClose(e) {
87
- if (DIALOG.open) closeModal(e);
88
- }
32
+ //== OPTION ATTRIBUTES DIALOG
33
+ const topLevel = pgs(DIALOG).option.contains("topLevel");
89
34
 
90
- //+ fn OPEN ON HISTORY
91
- function openModalOnHistory() {
92
- const params = new URLSearchParams(window.location.search);
93
- if (params.get('modal') !== BUTTON_OPEN.id) return;
94
- document.getElementById(BUTTON_OPEN.id)?.scrollIntoView({ behavior: 'smooth' });
95
- openModal();
96
- }
35
+
36
+ //== BUTTON CLOSE
37
+ if (!pgs(DIALOG).querySelector("modal-close") && !pgs(MODAL).querySelector("modal-close")) {
38
+ if (modalContentHeader) modalContentHeader.insertAdjacentHTML("beforeend", DOMButtonClose);
39
+ else DIALOG.insertAdjacentHTML("beforeend", DOMButtonClose);
40
+ }
41
+ const BUTTON_CLOSE = pgs(DIALOG).querySelector("modal-close") || pgs(MODAL).querySelector("modal-close");
97
42
 
98
43
 
99
- //= OPEN
100
- BUTTON_OPEN.addEventListener("click", (e) => openModal(e));
101
- BUTTON_OPEN.addEventListener("keypress", (e) => !DIALOG.open && (e.key === "Enter" || e.key === " ") && openModal(e));
102
-
103
- //= CLOSE
104
- DIALOG.addEventListener("close", () => statusModal(false));
105
- DIALOG.addEventListener("click", e => { if (e.target == DIALOG && !disableBackdropClose) closeModal(e) });
106
- BUTTON_CLOSE?.addEventListener("click", e => closeModal(e));
107
-
108
- //= UPDATE HISTORY
109
- if (data_history && BUTTON_OPEN.id) {
110
- setTimeout(openModalOnHistory, 1);
111
-
112
- //== Aggiorna URL quando cambia l'attributo "open" del dialog
113
- const obs = new MutationObserver(() => {
114
- let isOpen = DIALOG.hasAttribute("open");
115
- try {
116
- const url = new URL(window.location.href);
117
- const params = new URLSearchParams(url.search);
118
- isOpen ? params.set('modal', BUTTON_OPEN.id) : params.delete('modal');
119
- url.search = params.toString() ? `?${params.toString()}` : "";
120
- window.history.pushState({ modal: BUTTON_OPEN.id, open: isOpen }, "", url);
121
- } catch (_) { }
122
- });
123
- obs.observe(DIALOG, { attributes: true, attributeFilter: ["open"] });
124
-
125
- //== Gestisce back/forward del browser per aprire/chiudere il dialog coerentemente
126
- window.addEventListener("popstate", () => {
127
- try {
128
- const params = new URLSearchParams(window.location.search);
129
- const shouldOpen = params.get('modal') === BUTTON_OPEN.id;
130
- if (shouldOpen && !DIALOG.open) DIALOG.showModal();
131
- if (!shouldOpen && DIALOG.open) closeModal()
132
- } catch (_) { }
133
- });
44
+ //== SET
45
+ pgs(DIALOG).add("dialog modal-dialog");
46
+
47
+ //== BUTTON OPEN
48
+ BUTTON_OPEN.setAttribute("role", "button");
49
+ BUTTON_OPEN.setAttribute("aria-label", "apri modale");
50
+
51
+
52
+ //== POSITION
53
+ if (topLevel && !MODAL.contains(DIALOG)) MODAL.append(DIALOG);
54
+ else if (!topLevel) {
55
+ if (data_container) document.querySelector("#" + data_container)?.append(DIALOG);
56
+ else if (data_containerPGS) pgs(document).querySelector(data_containerPGS)?.append(DIALOG);
57
+ else document.body.append(DIALOG);
58
+ }
59
+
60
+
61
+ //+ FN STATUS
62
+ function statusModal(status = true) {
63
+ BUTTON_OPEN?.setAttribute("aria-expanded", status);
64
+ DIALOG?.setAttribute("aria-expanded", status);
65
+ }
66
+
67
+ //+ FN OPEN
68
+ function openModal(e) {
69
+ e?.stopImmediatePropagation();
70
+ if (DIALOG.open) {
71
+ closeModal(e);
72
+ return;
134
73
  }
135
74
 
136
- API.set(MODAL, {
137
- element: MODAL,
138
- button: BUTTON_OPEN,
139
- dialog: DIALOG,
140
- closeButton: BUTTON_CLOSE,
141
- open: forceOpen,
142
- close: forceClose,
143
- toggle: openModal,
144
- refresh: () => {
145
- PGS_modal_init(selector);
146
- return API.get(MODAL);
147
- },
148
- isOpen: () => DIALOG.open,
75
+ if (!DIALOG.open) document.querySelectorAll("dialog[open]").forEach((dlg) => dlg.close());
76
+ statusModal(true);
77
+ topLevel ? DIALOG.showModal() : DIALOG.show();
78
+ // modalCustomEvents('modal:open', { event: e });
79
+ MODAL.dispatchEvent(new CustomEvent('modal:open'));
80
+ DIALOG.dispatchEvent(new CustomEvent('modal:open'));
81
+ }
82
+
83
+ //+ FN CLOSE
84
+ function closeModal(e) {
85
+ e?.stopImmediatePropagation()
86
+ statusModal(false);
87
+ DIALOG.close();
88
+ // modalCustomEvents('modal:close', { event: e });
89
+ MODAL.dispatchEvent(new CustomEvent('modal:close'));
90
+ DIALOG.dispatchEvent(new CustomEvent('modal:close'));
91
+ }
92
+
93
+ function forceOpen(e) {
94
+ if (!DIALOG.open) openModal(e);
95
+ }
96
+
97
+ function forceClose(e) {
98
+ if (DIALOG.open) closeModal(e);
99
+ }
100
+
101
+ //+ fn OPEN ON HISTORY
102
+ function openModalOnHistory() {
103
+ const params = new URLSearchParams(window.location.search);
104
+ if (params.get('modal') !== BUTTON_OPEN.id) return;
105
+ document.getElementById(BUTTON_OPEN.id)?.scrollIntoView({ behavior: 'smooth' });
106
+ openModal();
107
+ }
108
+
109
+
110
+ //= OPEN
111
+ BUTTON_OPEN.addEventListener("click", (e) => openModal(e), { signal });
112
+ BUTTON_OPEN.addEventListener("keypress", (e) => !DIALOG.open && (e.key === "Enter" || e.key === " ") && openModal(e), { signal });
113
+
114
+ //= CLOSE
115
+ DIALOG.addEventListener("close", () => statusModal(false), { signal });
116
+ DIALOG.addEventListener("click", e => { if (e.target == DIALOG && !disableBackdropClose) closeModal(e) }, { signal });
117
+ BUTTON_CLOSE?.addEventListener("click", e => closeModal(e), { signal });
118
+
119
+ //= UPDATE HISTORY
120
+ if (data_history && BUTTON_OPEN.id) {
121
+ historyTimeout = window.setTimeout(openModalOnHistory, 1);
122
+
123
+ //== Aggiorna URL quando cambia l'attributo "open" del dialog
124
+ historyObserver = new MutationObserver(() => {
125
+ let isOpen = DIALOG.hasAttribute("open");
126
+ try {
127
+ const url = new URL(window.location.href);
128
+ const params = new URLSearchParams(url.search);
129
+ isOpen ? params.set('modal', BUTTON_OPEN.id) : params.delete('modal');
130
+ url.search = params.toString() ? `?${params.toString()}` : "";
131
+ window.history.pushState({ modal: BUTTON_OPEN.id, open: isOpen }, "", url);
132
+ } catch (_) { }
149
133
  });
134
+ historyObserver.observe(DIALOG, { attributes: true, attributeFilter: ["open"] });
135
+
136
+ //== Gestisce back/forward del browser per aprire/chiudere il dialog coerentemente
137
+ window.addEventListener("popstate", () => {
138
+ try {
139
+ const params = new URLSearchParams(window.location.search);
140
+ const shouldOpen = params.get('modal') === BUTTON_OPEN.id;
141
+ if (shouldOpen && !DIALOG.open) DIALOG.showModal();
142
+ if (!shouldOpen && DIALOG.open) closeModal()
143
+ } catch (_) { }
144
+ }, { signal });
145
+ }
146
+
147
+ function destroy() {
148
+ eventController.abort();
149
+ historyObserver?.disconnect();
150
+ if (historyTimeout !== null) window.clearTimeout(historyTimeout);
151
+ API.delete(MODAL);
152
+ }
153
+
154
+ API.set(MODAL, {
155
+ element: MODAL,
156
+ button: BUTTON_OPEN,
157
+ dialog: DIALOG,
158
+ closeButton: BUTTON_CLOSE,
159
+ open: forceOpen,
160
+ close: forceClose,
161
+ toggle: openModal,
162
+ refresh: () => {
163
+ const nextDialog = MODAL.querySelector("dialog") || DIALOG;
164
+ destroy();
165
+ initializeModal(MODAL, nextDialog);
166
+ return API.get(MODAL);
167
+ },
168
+ isOpen: () => DIALOG.open,
150
169
  });
151
170
  }
152
171
 
172
+ function PGS_modal_init(root = document) {
173
+ getModals(root).forEach(MODAL => initializeModal(MODAL));
174
+ }
175
+
153
176
  //# INIT PGS_modal
154
177
  PGS_modal_init()
155
178
 
156
179
  //# API
157
- export function PGS_modal_api(selector) {
158
- return API.get(selector);
180
+ function PGS_modal_api(element) {
181
+ return API.get(element);
159
182
  }
160
183
 
161
184
  export const PGS_modal = {
@@ -24,10 +24,12 @@ const fn_notification = {
24
24
  },
25
25
 
26
26
  _getData(root) {
27
+ const rawNotification = pgs(root).option.getValueBrackets("notification") || "{}";
28
+
27
29
  try {
28
- return JSON.parse(root.dataset.notification || "{}");
30
+ return JSON.parse(rawNotification);
29
31
  } catch (error) {
30
- console.warn("PGS notification: dati non validi", error);
32
+ console.warn("PGS notification: configurazione JSON non valida", error);
31
33
  return {};
32
34
  }
33
35
  },
@@ -46,13 +48,21 @@ const fn_notification = {
46
48
  `;
47
49
  },
48
50
 
49
- initNotification(type, containerToken, icon, text, timeout, methodDelete = "replace", link = null) {
50
- let containerNotification = pgs(document).querySelector(containerToken);
51
+ _getContainer(containerType) {
52
+ return Array.from(pgs(document).querySelectorAll("notification")).find(container => {
53
+ const isToast = pgs(container).option.contains("toast");
54
+ return containerType === "toast" ? isToast : !isToast;
55
+ });
56
+ },
57
+
58
+ initNotification(type, containerType, icon, text, timeout, methodDelete = "replace", link = null) {
59
+ let containerNotification = this._getContainer(containerType);
51
60
 
52
61
  //== Create Container
53
62
  if (!containerNotification) {
54
63
  const newContainer = document.createElement("div");
55
- pgs(newContainer).add(containerToken);
64
+ pgs(newContainer).add("notification");
65
+ if (containerType === "toast") pgs(newContainer).option.add("toast");
56
66
  newContainer.setAttribute("aria-live", "polite");
57
67
  newContainer.setAttribute("aria-relevant", "additions");
58
68
  document.body.appendChild(newContainer);
@@ -98,8 +108,8 @@ const fn_notification = {
98
108
  });
99
109
  },
100
110
 
101
- deleteAll(containerToken) {
102
- let containerNotification = pgs(document).querySelector(containerToken);
111
+ deleteAll(containerType) {
112
+ let containerNotification = this._getContainer(containerType);
103
113
  if (containerNotification) containerNotification.innerHTML = "";
104
114
  },
105
115
 
@@ -133,8 +143,8 @@ const fn_notification = {
133
143
  }
134
144
  };
135
145
 
136
- //# EXPORT
137
- export function PGS_notificationTrigger_init(root = document) {
146
+ //# TRIGGER
147
+ function PGS_notificationTrigger_init(root = document) {
138
148
  return fn_notification.trigger(root);
139
149
  }
140
150
 
@@ -5,7 +5,7 @@ let searchId = 0;
5
5
  const DEFAULT_OPTIONS = {
6
6
  minLength: 2,
7
7
  debounce: 200,
8
- limit: 8,
8
+ limit: 6,
9
9
  submitOnSelect: false,
10
10
  searchOnFocus: true,
11
11
  source: null,
@@ -381,7 +381,7 @@ document.addEventListener("pointerdown", event => {
381
381
 
382
382
  PGS_search_init();
383
383
 
384
- export function PGS_search_api(selector) {
384
+ function PGS_search_api(selector) {
385
385
  return API.get(selector);
386
386
  }
387
387
 
@@ -1,10 +1,16 @@
1
- import { PGS_scrollHorizontal } from "../functions/_scrollY"
1
+ import { PGS_scrollHorizontal } from "../functions/_scrollY.js";
2
2
  const API = new WeakMap();
3
3
 
4
+ function getSlides(root) {
5
+ const slides = root instanceof Element && pgs(root).contains("slides") ? [root] : [];
6
+ slides.push(...pgs(root).querySelectorAll("slides"));
7
+ return slides;
8
+ }
9
+
4
10
  class PGS_Slides {
5
11
  //- CONSTRUCTOR
6
- constructor({ selector, viewRatio = 0.97, optionIntersectionObserver = {}, scrollOptions = {} } = {}) {
7
- this.selector = selector;
12
+ constructor({ element, viewRatio = 0.97, optionIntersectionObserver = {}, scrollOptions = {} } = {}) {
13
+ this.element = element;
8
14
  this.viewRatio = viewRatio;
9
15
 
10
16
  this.optionIntersectionObserver = {
@@ -21,28 +27,36 @@ class PGS_Slides {
21
27
  ...scrollOptions,
22
28
  };
23
29
 
24
- this.container = this.selector ? pgs(this.selector).querySelector("slides-container") : null;
30
+ this.container = this.element ? pgs(this.element).querySelector("slides-container") : null;
25
31
  }
26
32
 
27
33
  //+ CREATE BUTTON
28
34
  #createButtonsAndDots() {
29
- const EL = this.selector
35
+ const EL = this.element;
30
36
 
31
37
  //== PULSANTI
32
- if (!pgs(EL).querySelector('slides-prec') && !pgs(EL).querySelector('slides-next')) {
38
+ if (!pgs(EL).querySelector('slides-prec')) {
33
39
  EL.insertAdjacentHTML("afterbegin", `<button pgs="slides-prec buttonIcon" type="button" class="precButton" aria-label="slide precedente"> <span> <i class="fa-solid fa-arrow-left"></i></span></button>`);
40
+ }
41
+ if (!pgs(EL).querySelector('slides-next')) {
34
42
  EL.insertAdjacentHTML("beforeend", `<button pgs="slides-next buttonIcon" type="button" class="nextButton" aria-label="prossima slide"> <span> <i class="fa-solid fa-arrow-right"></i></span></button>`);
35
43
  }
36
44
 
37
45
  //== DOTS
38
46
  if (!pgs(EL).querySelector('slides-dots')) {
39
47
  EL.insertAdjacentHTML("beforeend", `<div pgs="slides-dots" class="slides-dots"></div>`);
48
+ }
40
49
 
41
- Array.from(this.container.children).forEach(() => {
42
- pgs(EL).querySelector('slides-dots')
43
- .insertAdjacentHTML("beforeend", `<button type="button" class="slide-dot" aria-label="vai alla prossima slide"></button>`);
44
- });
50
+ const dotsContainer = pgs(EL).querySelector('slides-dots');
51
+ while (dotsContainer.children.length < this.container.children.length) {
52
+ dotsContainer.insertAdjacentHTML("beforeend", `<button type="button" class="slide-dot"></button>`);
53
+ }
54
+ while (dotsContainer.children.length > this.container.children.length) {
55
+ dotsContainer.lastElementChild.remove();
45
56
  }
57
+ Array.from(dotsContainer.children).forEach((dot, index) => {
58
+ dot.setAttribute("aria-label", `vai alla slide ${index + 1}`);
59
+ });
46
60
  }
47
61
 
48
62
  //+ PREV
@@ -51,7 +65,7 @@ class PGS_Slides {
51
65
  let current;
52
66
 
53
67
 
54
- if (pgs(this.selector).option.contains('singleScroll')) current = currents[currents.length - 1];
68
+ if (pgs(this.element).option.contains('singleScroll')) current = currents[currents.length - 1];
55
69
  else current = currents[0];
56
70
 
57
71
  const prev = current?.previousElementSibling;
@@ -67,19 +81,17 @@ class PGS_Slides {
67
81
 
68
82
 
69
83
 
70
- if (pgs(this.selector).option.contains('singleScroll')) current = currents[0];
84
+ if (pgs(this.element).option.contains('singleScroll')) current = currents[0];
71
85
  else current = currents[currents.length - 1];
72
86
 
73
87
  const next = current?.nextElementSibling;
74
- console.log(current, next);
75
-
76
88
  next?.scrollIntoView(this.scrollOptions);
77
89
  next?.focus({ preventScroll: true });
78
90
  }
79
91
 
80
92
  //+ GO TO NUMBER SLIDE
81
93
  #goToNumberSlide(index) {
82
- this.container.children[index].scrollIntoView(this.scrollOptions)
94
+ this.container.children[index]?.scrollIntoView(this.scrollOptions);
83
95
  }
84
96
 
85
97
  //+ CALLBACK
@@ -118,8 +130,10 @@ class PGS_Slides {
118
130
 
119
131
  //= EXECUTE
120
132
  execute() {
121
- const slides = this.selector;
122
- if (!this.container) return
133
+ const slides = this.element;
134
+ if (!this.container) return;
135
+ const eventController = new AbortController();
136
+ const { signal } = eventController;
123
137
 
124
138
  //== elements
125
139
  this.#createButtonsAndDots();
@@ -131,12 +145,14 @@ class PGS_Slides {
131
145
  const notScrollWithMouse = pgs(slides).option.contains('notScrollWithMouse');
132
146
 
133
147
  //== scroll
134
- if (!notScrollWithMouse) PGS_scrollHorizontal(this.container, 5);
148
+ const removeHorizontalScroll = notScrollWithMouse
149
+ ? null
150
+ : PGS_scrollHorizontal(this.container, 5);
135
151
 
136
152
  //==Listener: DOT, PREC, NEXT
137
- dots.forEach((dot, index) => dot.addEventListener("click", e => this.#goToNumberSlide(index)));
138
- precButton.addEventListener("click", e => this.#previousSlide(), { passive: true });
139
- nextButton.addEventListener("click", e => this.#nextSlide(), { passive: true });
153
+ dots.forEach((dot, index) => dot.addEventListener("click", () => this.#goToNumberSlide(index), { signal }));
154
+ precButton.addEventListener("click", () => this.#previousSlide(), { passive: true, signal });
155
+ nextButton.addEventListener("click", () => this.#nextSlide(), { passive: true, signal });
140
156
 
141
157
  //== observer
142
158
  const observer = new IntersectionObserver(
@@ -146,9 +162,18 @@ class PGS_Slides {
146
162
  Array.from(this.container.children).forEach(allLi => observer.observe(allLi));
147
163
 
148
164
 
165
+ let api;
166
+ const destroy = () => {
167
+ if (API.get(this.element) !== api) return;
168
+ eventController.abort();
169
+ observer.disconnect();
170
+ removeHorizontalScroll?.();
171
+ API.delete(this.element);
172
+ };
173
+
149
174
  //- API
150
- API.set(this.selector, {
151
- element: this.selector,
175
+ api = {
176
+ element: this.element,
152
177
  container: this.container,
153
178
  previous: () => this.#previousSlide(),
154
179
  next: () => this.#nextSlide(),
@@ -163,19 +188,28 @@ class PGS_Slides {
163
188
  return last?.classList.contains("view") || false;
164
189
  },
165
190
  refresh: () => {
166
- PGS_slides_init(this.selector.parentNode || document);
167
- return API.get(this.selector);
191
+ if (API.get(this.element) !== api) return API.get(this.element);
192
+ destroy();
193
+ const instance = new PGS_Slides({
194
+ element: this.element,
195
+ viewRatio: this.viewRatio,
196
+ optionIntersectionObserver: this.optionIntersectionObserver,
197
+ scrollOptions: this.scrollOptions,
198
+ });
199
+ instance.execute();
200
+ return API.get(this.element);
168
201
  },
169
- });
202
+ };
203
+ API.set(this.element, api);
170
204
  }
171
205
  }
172
206
 
173
207
  //# INIT
174
- export function PGS_slides_init(root = document) {
175
- pgs(root).querySelectorAll("slides").forEach(el => {
176
- if (API.has(el)) return;
208
+ function PGS_slides_init(root = document) {
209
+ getSlides(root).forEach(element => {
210
+ if (API.has(element)) return;
177
211
 
178
- const instance = new PGS_Slides({ selector: el });
212
+ const instance = new PGS_Slides({ element });
179
213
  instance.execute();
180
214
  });
181
215
  }
@@ -183,8 +217,8 @@ export function PGS_slides_init(root = document) {
183
217
  PGS_slides_init();
184
218
 
185
219
  //# API
186
- export function PGS_slides_api(selector) {
187
- return API.get(selector);
220
+ function PGS_slides_api(element) {
221
+ return API.get(element);
188
222
  }
189
223
 
190
224
  export const PGS_slides = {