mypgs 1.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 (79) hide show
  1. package/README.md +33 -0
  2. package/assets/javascript/_pgs.js +214 -0
  3. package/assets/javascript/base/_darkmode.js +121 -0
  4. package/assets/javascript/base/_object.js +42 -0
  5. package/assets/javascript/components/_accordion.js +77 -0
  6. package/assets/javascript/components/_dropdown.js +107 -0
  7. package/assets/javascript/components/_exeNotifications.js +85 -0
  8. package/assets/javascript/components/_menu.js +92 -0
  9. package/assets/javascript/components/_modals.js +123 -0
  10. package/assets/javascript/components/_slides.js +183 -0
  11. package/assets/javascript/components/_steps.js +27 -0
  12. package/assets/javascript/components/_tabs.js +134 -0
  13. package/assets/javascript/functions/_formValidate.js +217 -0
  14. package/assets/javascript/functions/_notifications.js +74 -0
  15. package/assets/javascript/functions/_scrollY.js +77 -0
  16. package/assets/javascript/functions/_sendForm.js +100 -0
  17. package/assets/javascript/index.js +20 -0
  18. package/assets/javascript/patterns/_cookieConsent.js +209 -0
  19. package/assets/javascript/patterns/_header.js +134 -0
  20. package/assets/javascript/pgs.d.ts +23 -0
  21. package/assets/scss/base/_body.scss +151 -0
  22. package/assets/scss/base/_color.scss +56 -0
  23. package/assets/scss/base/_general.scss +58 -0
  24. package/assets/scss/base/_heading.scss +20 -0
  25. package/assets/scss/base/_html.scss +4 -0
  26. package/assets/scss/base/_reset.scss +15 -0
  27. package/assets/scss/base/_variables.scss +39 -0
  28. package/assets/scss/components/_accordion.scss +91 -0
  29. package/assets/scss/components/_breadcumbs.scss +40 -0
  30. package/assets/scss/components/_button.scss +28 -0
  31. package/assets/scss/components/_dropdown.scss +106 -0
  32. package/assets/scss/components/_form.scss +53 -0
  33. package/assets/scss/components/_logo.scss +51 -0
  34. package/assets/scss/components/_menu.scss +23 -0
  35. package/assets/scss/components/_modals.scss +120 -0
  36. package/assets/scss/components/_notification.scss +193 -0
  37. package/assets/scss/components/_searchbar.scss +68 -0
  38. package/assets/scss/components/_slides.scss +198 -0
  39. package/assets/scss/components/_steps.scss +111 -0
  40. package/assets/scss/components/_table.scss +59 -0
  41. package/assets/scss/components/_tabs.scss +69 -0
  42. package/assets/scss/components/_tooltip.scss +16 -0
  43. package/assets/scss/index.scss +64 -0
  44. package/assets/scss/layout/_flex.scss +47 -0
  45. package/assets/scss/layout/_gap.scss +12 -0
  46. package/assets/scss/layout/_grid.scss +11 -0
  47. package/assets/scss/layout/_layout.scss +41 -0
  48. package/assets/scss/layout/_pageShell.scss +171 -0
  49. package/assets/scss/mixin/_mx-base.scss +126 -0
  50. package/assets/scss/mixin/_mx-button.scss +186 -0
  51. package/assets/scss/mixin/_mx-card.scss +77 -0
  52. package/assets/scss/mixin/_mx-color.scss +34 -0
  53. package/assets/scss/mixin/_mx-form-addon.scss +180 -0
  54. package/assets/scss/mixin/_mx-form.scss +512 -0
  55. package/assets/scss/mixin/_mx-menu.scss +154 -0
  56. package/assets/scss/mixin/_mx-responsive.scss +353 -0
  57. package/assets/scss/mixin/_mx-semantic.scss +147 -0
  58. package/assets/scss/mixin/mixin.scss +36 -0
  59. package/assets/scss/patterns/_cookieConsent.scss +101 -0
  60. package/assets/scss/patterns/_footer.scss +180 -0
  61. package/assets/scss/patterns/_header.scss +143 -0
  62. package/dist/css/index.css +7026 -0
  63. package/dist/css/index.css.map +1 -0
  64. package/dist/css/index.min.css +1 -0
  65. package/dist/index.d.ts +24 -0
  66. package/dist/javascript/index.js +2034 -0
  67. package/dist/javascript/index.js.map +1 -0
  68. package/dist/javascript/index.min.js +1 -0
  69. package/package.json +40 -0
  70. package/templates/components/md-breadcumbs.html +23 -0
  71. package/templates/components/md-menu.html +6 -0
  72. package/templates/components/md-notification.html +10 -0
  73. package/templates/components/md-searchbar.html +24 -0
  74. package/templates/components/md-tooltip.html +5 -0
  75. package/templates/layout/md-body.html +20 -0
  76. package/templates/layout/md-cookieConsent.html +51 -0
  77. package/templates/layout/md-footer.html +16 -0
  78. package/templates/layout/md-header.html +43 -0
  79. package/webpack.config.js +131 -0
@@ -0,0 +1,217 @@
1
+ import { PGS_toast } from "../functions/_notifications.js";
2
+
3
+
4
+ export class PGS_formValidate {
5
+ constructor({ form } = {}) {
6
+ this.container = form;
7
+ this._rules = [];
8
+ // pgs(this.container).add("formError");
9
+ }
10
+
11
+ //+ ADD
12
+ addError(field, i) {
13
+ field.setAttribute("data-form-field-status", "error");
14
+ if (i == 0) field.scrollIntoView();
15
+
16
+ let message = field.getAttribute("data-form-field-message");
17
+
18
+ if (i == 0 && message) PGS_toast.error(message);
19
+ else if (i == 0) PGS_toast.error("Compila tutti i campi!");
20
+ }
21
+
22
+ //+ REMOVE
23
+ removeError(field) {
24
+ field.setAttribute("data-form-field-status", "");
25
+ }
26
+
27
+ #removeErrorOnClick(allFields) {
28
+ allFields.forEach(element => {
29
+ element.addEventListener("click", e => this.removeError(element))
30
+ });
31
+ }
32
+
33
+ // + --------------------------
34
+ // + Helpers
35
+ // + --------------------------
36
+ help = {
37
+ // supporta sia required nativo, sia data-required="true"
38
+ isRequired(field) {
39
+ if (!field) return false;
40
+
41
+ const required = field.required === true || field?.dataset?.required === "true" || field?.getAttribute('aria-required') == "true";
42
+ return required && !field.hidden; // solo attributo/proprietà "hidden"
43
+ },
44
+ // input (non speciali), textarea
45
+ isEmptyTextLike(field) { return !String(field?.value ?? "").trim(); },
46
+ // select: vuoto se value == "" o null
47
+ isEmptySelect(field) { return !String(field?.value ?? "").trim(); },
48
+ // recupera name in modo sicuro
49
+ getGroupName(field) { return field?.name || field?.getAttribute?.("name") || ""; }
50
+ };
51
+
52
+
53
+ // + --------------------------
54
+ // + input + altri elementi.
55
+ // + --------------------------
56
+ #inputValue(container) {
57
+
58
+ //++ add rule
59
+ const ruleInvalidFields = [];
60
+ for (const rule of this._rules) {
61
+ const res = rule(container);
62
+
63
+ // la rule può tornare:
64
+ // • null/undefined => ok
65
+ // • un elemento => invalido
66
+ // • un array di elementi => invalidi
67
+ if (!res) continue;
68
+
69
+ if (Array.isArray(res)) ruleInvalidFields.push(...res);
70
+ else ruleInvalidFields.push(res);
71
+ }
72
+
73
+ //== INPUT
74
+ // "testuali" (esclude hidden/disabled/checkbox/radio/file come nel tuo snippet)
75
+ const textInputs = Array.from(container.querySelectorAll("input")).filter((input) => {
76
+ if (input.disabled) return false;
77
+ if (input.type === "hidden") return false;
78
+ if (input.type === "checkbox" || input.type === "radio" || input.type === "file") return false;
79
+
80
+ // valida solo se required (o data-required="true")
81
+ if (!this.help.isRequired(input)) return false;
82
+
83
+ return this.help.isEmptyTextLike(input);
84
+ });
85
+
86
+ //== TEXTAREA
87
+ // required vuote
88
+ const textareas = Array.from(container.querySelectorAll("textarea")).filter((ta) => {
89
+ if (ta.disabled) return false;
90
+ if (!this.help.isRequired(ta)) return false;
91
+ return this.help.isEmptyTextLike(ta);
92
+ });
93
+
94
+ //== SELECT
95
+ // required vuoti
96
+ const selects = Array.from(container.querySelectorAll("select")).filter((sel) => {
97
+ if (sel.disabled) return false;
98
+ if (!this.help.isRequired(sel)) return false;
99
+ return this.help.isEmptySelect(sel);
100
+ });
101
+
102
+ //== RADIO
103
+ // required: se in un gruppo required non ce n'è uno checked => errore sul "primo" radio del gruppo
104
+ const radios = Array.from(container.querySelectorAll('input[type="radio"]')).filter((r) => !r.disabled);
105
+ const requiredRadioGroups = new Map(); // name -> [elements]
106
+ for (const r of radios) {
107
+ if (!this.help.isRequired(r)) continue;
108
+ const name = this.help.getGroupName(r);
109
+ if (!name) continue;
110
+ if (!requiredRadioGroups.has(name)) requiredRadioGroups.set(name, []);
111
+ requiredRadioGroups.get(name).push(r);
112
+ }
113
+ const radioGroupErrors = [];
114
+ for (const [name, group] of requiredRadioGroups.entries()) {
115
+ const anyChecked = group.some((r) => r.checked);
116
+ if (!anyChecked) {
117
+ // scegli dove mettere l'errore: tipicamente sul primo radio del gruppo
118
+ radioGroupErrors.push(group[0]);
119
+ }
120
+ }
121
+
122
+ //== CHECKBOX
123
+ // required: può essere singola checkbox required (checked obbligatorio)
124
+ // oppure gruppo di checkbox (stesso name) con almeno una selezionata
125
+ const checkboxes = Array.from(container.querySelectorAll('input[type="checkbox"]')).filter((c) => !c.disabled);
126
+ const requiredCheckboxSingles = [];
127
+ const requiredCheckboxGroups = new Map(); // name -> [elements]
128
+ for (const c of checkboxes) {
129
+ if (!this.help.isRequired(c)) continue;
130
+
131
+ const name = this.help.getGroupName(c);
132
+ if (!name) {
133
+ // checkbox senza name: trattala come singola required
134
+ if (!c.checked) requiredCheckboxSingles.push(c);
135
+ continue;
136
+ }
137
+
138
+ // se vuoi trattare come gruppo, raggruppa per name
139
+ if (!requiredCheckboxGroups.has(name)) requiredCheckboxGroups.set(name, []);
140
+ requiredCheckboxGroups.get(name).push(c);
141
+ }
142
+ const checkboxGroupErrors = [];
143
+ for (const [name, group] of requiredCheckboxGroups.entries()) {
144
+ // se è un gruppo (>=2) richiedi almeno una spuntata
145
+ // se è 1 sola, si comporta come singola
146
+ const anyChecked = group.some((c) => c.checked);
147
+ if (!anyChecked) checkboxGroupErrors.push(group[0]);
148
+ }
149
+
150
+ //== FILE
151
+ // required: se vuoi includerlo
152
+ const fileInputs = Array.from(container.querySelectorAll('input[type="file"]')).filter((f) => {
153
+ if (f.disabled) return false;
154
+ if (!this.help.isRequired(f)) return false;
155
+ return !(f.files && f.files.length > 0);
156
+ });
157
+
158
+ //== risultato finale: tutti i campi da marcare come errore
159
+ const invalidFields = [
160
+ textInputs,
161
+ textareas,
162
+ selects,
163
+ radioGroupErrors,
164
+ requiredCheckboxSingles,
165
+ checkboxGroupErrors,
166
+ fileInputs,
167
+ ruleInvalidFields
168
+ ];
169
+
170
+ return invalidFields.flat();
171
+ }
172
+
173
+ // + -------------------------
174
+ // + VALIDATE
175
+ // + -------------------------
176
+ validate() {
177
+ const invalid = this.#inputValue(this.container);
178
+ const allFields = this.container.querySelectorAll("input, textarea, select")
179
+
180
+ //== pulizia/aggiornamento errori:
181
+ // prima rimuovo errori dai campi "non più invalidi"
182
+ Array.from(allFields).filter((el) => !el.disabled);
183
+
184
+ //== per radio/checkbox in gruppo:
185
+ // rimuovi l'errore solo sull'elemento che lo ospita (qui: se presente)
186
+ for (const el of allFields) { if (!invalid.includes(el)) this.removeError(el); }
187
+
188
+ //== aggiungo errori dove serve
189
+ invalid.forEach((el, i) => this.addError(el, i))
190
+
191
+ //== rimuove l'errore al click
192
+ this.#removeErrorOnClick(allFields)
193
+
194
+ //== status form
195
+ if (invalid.length) {
196
+ this.container.setAttribute("data-form-status", "error");
197
+ return false;
198
+ } else {
199
+ this.container.setAttribute("data-form-status", "success");
200
+ return true;
201
+ }
202
+ }
203
+
204
+ ifSuccess(text = "Inviato con successo") {
205
+ if (this.validate() == true) PGS_toast.success(text)
206
+ }
207
+
208
+ // + -------------------------
209
+ // + ADD RULE
210
+ // + -------------------------
211
+ addNewRule(container) {
212
+ if (typeof container !== "function") throw new Error("Rule must be a function");
213
+ this._rules.push(container);
214
+ return this;
215
+ }
216
+ }
217
+
@@ -0,0 +1,74 @@
1
+ //# PGS_notification
2
+ function initNotification(type, containerToken, icon, text, timeout, methodDelete = "replace", link = null) {
3
+ let containerNotification = pgs(document).querySelector(containerToken);
4
+
5
+ //== Create Container
6
+ if (!containerNotification) {
7
+ const newContainer = document.createElement("div");
8
+ pgs(newContainer).add(containerToken);
9
+ newContainer.setAttribute("aria-live", "polite");
10
+ newContainer.setAttribute("aria-relevant", "additions");
11
+ document.body.appendChild(newContainer);
12
+ containerNotification = newContainer;
13
+ }
14
+
15
+ //== Create Notification
16
+ const notification = document.createElement(link ? "a" : "div");
17
+ if (methodDelete == "replace") containerNotification.innerHTML = "";
18
+ if (link) notification.href = link;
19
+ if (timeout > 0) notification.style.setProperty("--notification-timeout", timeout + "ms");
20
+ pgs(notification).state.add(type);
21
+ pgs(notification).add("notification-element");
22
+ notification.setAttribute("role", type == "error" ? "alert" : "status")
23
+ notification.innerHTML = `${icon} <p>${text}</p>`;
24
+ containerNotification.appendChild(notification);
25
+
26
+
27
+ //+ Animation delete
28
+ function deleteNotification() {
29
+ methodDelete == "stack" ? notification.style.translate = "120%" : notification.style.opacity = "0";
30
+ setTimeout(() => notification.remove(), 300);
31
+ }
32
+
33
+ //== Timeout delete
34
+ if (timeout > 0) setTimeout(() => { deleteNotification() }, timeout);
35
+
36
+ //== button delete
37
+ const btnDelete = document.createElement("button");
38
+ btnDelete.type = "button";
39
+ btnDelete.ariaLabel = "Rimuovi notifica";
40
+ btnDelete.innerHTML = '<i class="fa-solid fa-xmark"></i>';
41
+ btnDelete.setAttribute("pgs", "buttonClose");
42
+ notification.insertAdjacentElement("afterbegin", btnDelete);
43
+
44
+ //== event
45
+ btnDelete.addEventListener("click", function (e) {
46
+ e.preventDefault();
47
+ e.stopPropagation();
48
+ e.stopImmediatePropagation()
49
+ deleteNotification(e); // Esegue la tua funzione
50
+ });
51
+ }
52
+
53
+ function deleteALl(containerToken) {
54
+ let containerNotification = pgs(document).querySelector(containerToken);
55
+ if (containerNotification) containerNotification.innerHTML = "";
56
+ }
57
+
58
+
59
+
60
+ export let PGS_notification = {
61
+ error: (text = "Errore", link = null, timeout = 0, icon = '<i class="fa-solid fa-octagon-xmark"></i>') => initNotification("error", "notification", icon, text, timeout, "stack", link),
62
+ success: (text = "Aggiornato", link = null, timeout = 0, icon = '<i class="fa-solid fa-check"></i>') => initNotification("success", "notification", icon, text, timeout, "stack", link),
63
+ info: (text = "Aggiornamento", link = null, timeout = 0, icon = '<i class="fa-solid fa-circle-info"></i>',) => initNotification("info", "notification", icon, text, timeout, "stack", link),
64
+ warning: (text = "Attenzione", link = null, timeout = 0, icon = '<i class="fa-solid fa-triangle-exclamation"></i>') => initNotification("warning", "notification", icon, text, timeout, "stack", link),
65
+ deleteAllNotification: () => deleteALl("notification")
66
+ }
67
+
68
+ export let PGS_toast = {
69
+ error: (text = "Errore", timeout = 4000, icon = '<i class="fa-solid fa-octagon-xmark"></i>',) => initNotification("error", "toast", icon, text, timeout),
70
+ success: (text = "Aggiornato", timeout = 4000, icon = '<i class="fa-solid fa-check"></i>',) => initNotification("success", "toast", icon, text, timeout),
71
+ info: (text = "Aggiornamento", timeout = 0, icon = '<i class="fa-solid fa-circle-info"></i>',) => initNotification("info", "toast", icon, text, timeout),
72
+ warning: (text = "Attenzione", timeout = 4000, icon = '<i class="fa-solid fa-triangle-exclamation"></i>',) => initNotification("warning", "toast", icon, text, timeout),
73
+ deleteTost: () => deleteALl("toast")
74
+ }
@@ -0,0 +1,77 @@
1
+ export function PGS_scrollHorizontal(querySelector, dataSpeed) {
2
+ // Se hai più contenitori, selezionali tutti:
3
+ // Semplice "singleton" per stimare se la sorgente è trackpad
4
+ const TrackpadDetector = (() => {
5
+ let lastTs = 0;
6
+ let smallAndFast = 0;
7
+ let samples = 0;
8
+ let isTrackpad = false;
9
+
10
+ function update(e) {
11
+ const now = performance.now();
12
+ const dt = now - lastTs;
13
+
14
+ // Porta delta in px (0: px, 1: linee, 2: pagine)
15
+ let dy = Math.abs(e.deltaY);
16
+ if (e.deltaMode === 1) dy *= 16;
17
+ else if (e.deltaMode === 2) dy *= e.currentTarget?.clientHeight || 800;
18
+
19
+ // Heuristica: eventi piccoli e ravvicinati → prob. trackpad
20
+ const small = dy < 30; // soglia prudente
21
+ const fast = dt < 35; // alta frequenza
22
+ if (small && fast) smallAndFast++;
23
+
24
+ samples++;
25
+ if (samples >= 6) { // aggiorna il giudizio ogni N eventi
26
+ isTrackpad = smallAndFast >= 3;
27
+ smallAndFast = 0;
28
+ samples = 0;
29
+ }
30
+
31
+ lastTs = now;
32
+ return isTrackpad;
33
+ }
34
+
35
+ return {
36
+ update,
37
+ get value() { return isTrackpad; }
38
+ };
39
+ })();
40
+
41
+ //= Scorrimento orizzontale con rotella (evita il trackpad)
42
+
43
+ let el = querySelector
44
+ el.addEventListener('wheel', (e) => {
45
+ const speed = dataSpeed;
46
+
47
+ //== lascia lo scroll naturale del trackpad
48
+ if (TrackpadDetector.update(e)) return;
49
+
50
+ //== Evita interferenze con zoom o scroll orizzontale nativo
51
+ if (e.ctrlKey) return;
52
+ if (Math.abs(e.deltaX) > Math.abs(e.deltaY)) return;
53
+
54
+ //== Converti delta in px per lo shift orizzontale
55
+ let delta = e.deltaY;
56
+ if (e.deltaMode === 1) delta *= 16;
57
+ else if (e.deltaMode === 2) delta *= el.clientHeight;
58
+
59
+ //== Verifica se il contenitore può ancora scrollare orizzontalmente
60
+ const atStart = el.scrollLeft <= 0;
61
+ const atEnd = el.scrollLeft + el.clientWidth >= el.scrollWidth - 1;
62
+ const scrollingRight = delta > 0;
63
+ const scrollingLeft = delta < 0;
64
+ const canScrollHoriz =
65
+ (scrollingRight && !atEnd) ||
66
+ (scrollingLeft && !atStart);
67
+
68
+ // Se non può più scrollare in quella direzione, lascia che la pagina gestisca lo scroll verticale
69
+ if (!canScrollHoriz) return;
70
+
71
+ //== Previeni il default solo quando facciamo noi lo scroll orizzontale
72
+ e.preventDefault();
73
+
74
+ //== rotella giù => destra
75
+ el.scrollLeft += delta * speed;
76
+ }, { passive: false });
77
+ }
@@ -0,0 +1,100 @@
1
+ import { PGS_toast } from "../functions/_notifications.js";
2
+ export async function PGS_sendForm(
3
+ append = {
4
+ formAppend: [
5
+ { name: "", value: "" }
6
+ ],
7
+ wpnonce: {
8
+ name: "",
9
+ value: ""
10
+ },
11
+ action: ""
12
+ },
13
+ tost = {
14
+ succesText: "Inviato con succeso",
15
+ errorGenericText: "Si è verificato un errore",
16
+ infoText: "Invio in corso..."
17
+ }, log = true) {
18
+
19
+ PGS_toast.info(tost.infoText, -1)
20
+
21
+ //== FORM DATA
22
+ const formData = new FormData();
23
+
24
+ //=== Mandatory data
25
+ formData.append(append.wpnonce.name, append.wpnonce.value);
26
+ formData.append('action', append.action);
27
+
28
+ //=== append esempio
29
+ append.formAppend.forEach(item => formData.append(item.name, item.value));
30
+
31
+ let status
32
+
33
+ try {
34
+ const response = await fetch('/wp-admin/admin-ajax.php', {
35
+ method: 'POST',
36
+ body: formData
37
+ });
38
+
39
+ if (!response.ok) {
40
+ PGS_toast.error(`Errore: ${response.status}`)
41
+ status = { success: false, data: [], response: response };
42
+ throw new Error(`HTTP error! status: ${response.status}`);
43
+ }
44
+
45
+ const result = await response.json();
46
+
47
+ if (result.success) {
48
+ status = result;
49
+ PGS_toast.success(tost.succesText)
50
+
51
+ } else {
52
+ status = result;
53
+ PGS_toast.error(result.data ? result.data.message : 'Errore sconosciuto')
54
+ console.error(result);
55
+ }
56
+
57
+ } catch (error) {
58
+ status = status ? status : { success: false, data: []};
59
+ PGS_toast.error(`Si è verificato un errore nella richiesta.`)
60
+ console.error('Errore:', error);
61
+ }
62
+
63
+ if (log) console.log("Status:", status);
64
+ if (log) console.log("formData:", Array.from(formData.entries()));
65
+ return status;
66
+ }
67
+
68
+ /*
69
+ let documentForm = document.querySelector("#formExample");
70
+ if (documentForm) {
71
+ documentForm.addEventListener("submit", function (e) {
72
+ e.preventDefault();
73
+
74
+ let appendMandatory = {
75
+ formAppend: [
76
+ {
77
+ name: "",
78
+ value: ""
79
+ },
80
+ {
81
+ name: "",
82
+ value: ""
83
+ },
84
+ ],
85
+ wpnonce: {
86
+ name: "testName",
87
+ value: "testValue"
88
+ },
89
+ action: "test_submit_form"
90
+ };
91
+ let tost = {
92
+ succesText: "inviato ottimo",
93
+ errorGenericText: "errore invio",
94
+ infoText: "Invio in corso...",
95
+
96
+ }
97
+
98
+ sendBooking(appendMandatory, tost)
99
+ })
100
+ } */
@@ -0,0 +1,20 @@
1
+ //= PGS
2
+ import "./_pgs.js";
3
+
4
+ //= BASE
5
+ import "./base/_darkmode.js";
6
+ import "./base/_object.js";
7
+
8
+ //= CN
9
+ import "./components/_accordion.js";
10
+ import "./components/_dropdown.js";
11
+ import "./components/_exeNotifications.js";
12
+ import "./components/_menu.js"
13
+ import "./components/_modals.js";
14
+ import "./components/_slides.js"
15
+ import "./components/_steps.js";
16
+ import "./components/_tabs.js";
17
+
18
+ //= patterns
19
+ import "./patterns/_header.js";
20
+ import "./patterns/_cookieConsent.js";