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.
- package/README.md +33 -0
- package/assets/javascript/_pgs.js +214 -0
- package/assets/javascript/base/_darkmode.js +121 -0
- package/assets/javascript/base/_object.js +42 -0
- package/assets/javascript/components/_accordion.js +77 -0
- package/assets/javascript/components/_dropdown.js +107 -0
- package/assets/javascript/components/_exeNotifications.js +85 -0
- package/assets/javascript/components/_menu.js +92 -0
- package/assets/javascript/components/_modals.js +123 -0
- package/assets/javascript/components/_slides.js +183 -0
- package/assets/javascript/components/_steps.js +27 -0
- package/assets/javascript/components/_tabs.js +134 -0
- package/assets/javascript/functions/_formValidate.js +217 -0
- package/assets/javascript/functions/_notifications.js +74 -0
- package/assets/javascript/functions/_scrollY.js +77 -0
- package/assets/javascript/functions/_sendForm.js +100 -0
- package/assets/javascript/index.js +20 -0
- package/assets/javascript/patterns/_cookieConsent.js +209 -0
- package/assets/javascript/patterns/_header.js +134 -0
- package/assets/javascript/pgs.d.ts +23 -0
- package/assets/scss/base/_body.scss +151 -0
- package/assets/scss/base/_color.scss +56 -0
- package/assets/scss/base/_general.scss +58 -0
- package/assets/scss/base/_heading.scss +20 -0
- package/assets/scss/base/_html.scss +4 -0
- package/assets/scss/base/_reset.scss +15 -0
- package/assets/scss/base/_variables.scss +39 -0
- package/assets/scss/components/_accordion.scss +91 -0
- package/assets/scss/components/_breadcumbs.scss +40 -0
- package/assets/scss/components/_button.scss +28 -0
- package/assets/scss/components/_dropdown.scss +106 -0
- package/assets/scss/components/_form.scss +53 -0
- package/assets/scss/components/_logo.scss +51 -0
- package/assets/scss/components/_menu.scss +23 -0
- package/assets/scss/components/_modals.scss +120 -0
- package/assets/scss/components/_notification.scss +193 -0
- package/assets/scss/components/_searchbar.scss +68 -0
- package/assets/scss/components/_slides.scss +198 -0
- package/assets/scss/components/_steps.scss +111 -0
- package/assets/scss/components/_table.scss +59 -0
- package/assets/scss/components/_tabs.scss +69 -0
- package/assets/scss/components/_tooltip.scss +16 -0
- package/assets/scss/index.scss +64 -0
- package/assets/scss/layout/_flex.scss +47 -0
- package/assets/scss/layout/_gap.scss +12 -0
- package/assets/scss/layout/_grid.scss +11 -0
- package/assets/scss/layout/_layout.scss +41 -0
- package/assets/scss/layout/_pageShell.scss +171 -0
- package/assets/scss/mixin/_mx-base.scss +126 -0
- package/assets/scss/mixin/_mx-button.scss +186 -0
- package/assets/scss/mixin/_mx-card.scss +77 -0
- package/assets/scss/mixin/_mx-color.scss +34 -0
- package/assets/scss/mixin/_mx-form-addon.scss +180 -0
- package/assets/scss/mixin/_mx-form.scss +512 -0
- package/assets/scss/mixin/_mx-menu.scss +154 -0
- package/assets/scss/mixin/_mx-responsive.scss +353 -0
- package/assets/scss/mixin/_mx-semantic.scss +147 -0
- package/assets/scss/mixin/mixin.scss +36 -0
- package/assets/scss/patterns/_cookieConsent.scss +101 -0
- package/assets/scss/patterns/_footer.scss +180 -0
- package/assets/scss/patterns/_header.scss +143 -0
- package/dist/css/index.css +7026 -0
- package/dist/css/index.css.map +1 -0
- package/dist/css/index.min.css +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/javascript/index.js +2034 -0
- package/dist/javascript/index.js.map +1 -0
- package/dist/javascript/index.min.js +1 -0
- package/package.json +40 -0
- package/templates/components/md-breadcumbs.html +23 -0
- package/templates/components/md-menu.html +6 -0
- package/templates/components/md-notification.html +10 -0
- package/templates/components/md-searchbar.html +24 -0
- package/templates/components/md-tooltip.html +5 -0
- package/templates/layout/md-body.html +20 -0
- package/templates/layout/md-cookieConsent.html +51 -0
- package/templates/layout/md-footer.html +16 -0
- package/templates/layout/md-header.html +43 -0
- package/webpack.config.js +131 -0
package/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# MyPGS
|
|
2
|
+
|
|
3
|
+
Libreria frontend con asset JavaScript e CSS precompilati per i componenti e i pattern MyPGS.
|
|
4
|
+
|
|
5
|
+
## Installazione
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install mypgs
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Utilizzo
|
|
12
|
+
|
|
13
|
+
Importa gli asset compilati nel tuo progetto:
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import "mypgs";
|
|
17
|
+
import "mypgs/style.css";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Lo script inizializza le funzionalita' globali della libreria ed espone `pgs` su `globalThis`.
|
|
21
|
+
|
|
22
|
+
## Contenuto del pacchetto
|
|
23
|
+
|
|
24
|
+
- `dist/javascript/index.js`
|
|
25
|
+
- `dist/javascript/index.min.js`
|
|
26
|
+
- `dist/css/index.css`
|
|
27
|
+
- `dist/css/index.min.css`
|
|
28
|
+
|
|
29
|
+
## Sviluppo
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm run build
|
|
33
|
+
```
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {Element | Document} root
|
|
3
|
+
*/
|
|
4
|
+
export function pgs(root) {
|
|
5
|
+
const ATTR = "pgs";
|
|
6
|
+
if (!root) throw new TypeError("pgs(root): root richiesto");
|
|
7
|
+
|
|
8
|
+
const canAttr = typeof root.getAttribute === "function" && typeof root.setAttribute === "function";
|
|
9
|
+
const canQuery = typeof root.querySelector === "function" && typeof root.querySelectorAll === "function";
|
|
10
|
+
|
|
11
|
+
if (!canQuery) {
|
|
12
|
+
throw new TypeError("pgs(root): root deve supportare querySelector/querySelectorAll");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
//+
|
|
16
|
+
function attrOnlyForElements(methodName) {
|
|
17
|
+
throw new TypeError(`pgs(${root.nodeName || "root"}).${methodName}(): disponibile solo su Element (non su Document)`);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
//+
|
|
21
|
+
function concactSelector(value) {
|
|
22
|
+
if (Array.isArray(value)) value = value.join(",");
|
|
23
|
+
return String(value)
|
|
24
|
+
.split(",")
|
|
25
|
+
.map(v => v.trim())
|
|
26
|
+
.filter(Boolean)
|
|
27
|
+
.map(v => `[${ATTR}~="${v}"]`)
|
|
28
|
+
.join(",");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//+
|
|
32
|
+
function getValues(el, separator = " ") {
|
|
33
|
+
return (el.getAttribute(ATTR) || "")
|
|
34
|
+
.split(separator)
|
|
35
|
+
.filter(Boolean);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
//+
|
|
39
|
+
function setValues(el, values, separator = " ") {
|
|
40
|
+
el.setAttribute(ATTR, values.join(separator));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
//= BASE PGS
|
|
44
|
+
function createBasePgs() {
|
|
45
|
+
function api() {
|
|
46
|
+
return api;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
api.querySelector = function (value) {
|
|
50
|
+
return root.querySelector(concactSelector(value));
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
api.querySelectorAll = function (value) {
|
|
54
|
+
return root.querySelectorAll(concactSelector(value));
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
api.add = function (...values) {
|
|
58
|
+
if (!canAttr) return attrOnlyForElements("add");
|
|
59
|
+
const current = getValues(root);
|
|
60
|
+
for (const v of values) if (!current.includes(v)) current.push(v);
|
|
61
|
+
setValues(root, current);
|
|
62
|
+
return api;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
api.remove = function (...values) {
|
|
66
|
+
if (!canAttr) return attrOnlyForElements("remove");
|
|
67
|
+
setValues(root, getValues(root).filter(v => !values.includes(v)));
|
|
68
|
+
return api;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
api.toggle = function (value, force) {
|
|
72
|
+
if (!canAttr) return attrOnlyForElements("toggle");
|
|
73
|
+
|
|
74
|
+
const exists = getValues(root).includes(value);
|
|
75
|
+
|
|
76
|
+
if (force !== undefined) {
|
|
77
|
+
if (force && !exists) api.add(value);
|
|
78
|
+
if (!force && exists) api.remove(value);
|
|
79
|
+
return !!force;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (exists) {
|
|
83
|
+
api.remove(value);
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
api.add(value);
|
|
88
|
+
return true;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
api.contains = function (value) {
|
|
92
|
+
if (!canAttr) return attrOnlyForElements("contains");
|
|
93
|
+
return getValues(root).includes(value);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
Object.defineProperty(api, "value", {
|
|
97
|
+
get() {
|
|
98
|
+
if (!canAttr) return undefined;
|
|
99
|
+
return root.getAttribute(ATTR);
|
|
100
|
+
},
|
|
101
|
+
set(v) {
|
|
102
|
+
if (!canAttr) return attrOnlyForElements("value");
|
|
103
|
+
root.setAttribute(ATTR, v);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
return api;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
//= STATE
|
|
111
|
+
function createState(attribute) {
|
|
112
|
+
if (!canAttr) return undefined;
|
|
113
|
+
|
|
114
|
+
const read = (sep = " ") =>
|
|
115
|
+
(root.getAttribute(attribute) || "").split(sep).filter(Boolean);
|
|
116
|
+
|
|
117
|
+
const write = (vals, sep = " ") =>
|
|
118
|
+
root.setAttribute(attribute, vals.join(sep));
|
|
119
|
+
|
|
120
|
+
// funzione chiamabile: state("active") == add("active")
|
|
121
|
+
function api(...values) {
|
|
122
|
+
api.add(...values);
|
|
123
|
+
return api;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
api.add = function (...values) {
|
|
127
|
+
const toAdd = values.flat().map(v => String(v).trim()).filter(Boolean);
|
|
128
|
+
const current = read();
|
|
129
|
+
for (const v of toAdd) if (!current.includes(v)) current.push(v);
|
|
130
|
+
write(current);
|
|
131
|
+
return api;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
api.remove = function (...values) {
|
|
135
|
+
const toRemove = values.flat().map(v => String(v).trim()).filter(Boolean);
|
|
136
|
+
const current = read().filter(v => !toRemove.includes(v));
|
|
137
|
+
write(current);
|
|
138
|
+
return api;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
api.toggle = function (value) {
|
|
142
|
+
const v = String(value).trim();
|
|
143
|
+
if (!v) return false;
|
|
144
|
+
|
|
145
|
+
const current = read();
|
|
146
|
+
if (current.includes(v)) {
|
|
147
|
+
write(current.filter(x => x !== v));
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
current.push(v);
|
|
151
|
+
write(current);
|
|
152
|
+
return true;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
api.contains = function (value) {
|
|
156
|
+
const v = String(value).trim();
|
|
157
|
+
if (!v) return false;
|
|
158
|
+
return read().includes(v);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
Object.defineProperty(api, "value", {
|
|
162
|
+
get() { return root.getAttribute(attribute); },
|
|
163
|
+
set(v) { root.setAttribute(attribute, v); }
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
return api;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
//= OPTION
|
|
170
|
+
function createOption(attribute) {
|
|
171
|
+
if (!canAttr) return undefined;
|
|
172
|
+
|
|
173
|
+
function api() {
|
|
174
|
+
return api;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
api.contains = function (key) {
|
|
178
|
+
const source = root.getAttribute(attribute) || "";
|
|
179
|
+
const safeKey = String(key).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
180
|
+
|
|
181
|
+
return new RegExp(`(?:^|\\s)${safeKey}(?:\\[[^\\]]*\\])?(?=\\s|$)`)
|
|
182
|
+
.test(source);
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
api.getValueBrackets = function (key) {
|
|
186
|
+
const source = root.getAttribute(attribute) || "";
|
|
187
|
+
const safeKey = String(key).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
188
|
+
|
|
189
|
+
const match = source.match(
|
|
190
|
+
new RegExp(`(?:^|\\s)${safeKey}\\[([^\\]]*)\\]`)
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
return match ? match[1] : undefined;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
Object.defineProperty(api, "value", {
|
|
197
|
+
get() { return root.getAttribute(attribute); },
|
|
198
|
+
set(v) { root.setAttribute(attribute, v); }
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
return api;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
//# RETURN
|
|
205
|
+
const api = createBasePgs();
|
|
206
|
+
api.state = createState("pgs-state");
|
|
207
|
+
api.option = createOption("pgs-option");
|
|
208
|
+
api.modules = createOption("pgs-modules");
|
|
209
|
+
return api;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
globalThis.pgs ??= pgs;
|
|
213
|
+
|
|
214
|
+
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
//# DARKMODE
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
//= CHANGE COLOR SVG & LOTTIE
|
|
5
|
+
//+ SEARCH COLOR
|
|
6
|
+
function searchColor(type = "svg") {
|
|
7
|
+
const ROOT = getComputedStyle(document.documentElement);
|
|
8
|
+
const colors = []
|
|
9
|
+
for (let I = 0; I < 20; I++) {
|
|
10
|
+
const color = ROOT.getPropertyValue('--' + type + '-color-' + I).toLocaleLowerCase().split("&");
|
|
11
|
+
if (!color[0] == "" && !color[1] == "") {
|
|
12
|
+
let lightDark = [color[0], color[1]]
|
|
13
|
+
colors.push(lightDark)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return colors;
|
|
17
|
+
}
|
|
18
|
+
const colors_svg_lottie = [searchColor("svg"), searchColor("lottie")];
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
//+ change COLORS
|
|
22
|
+
function darkmodeColorSVG() {
|
|
23
|
+
function changecolor(svgDoc, type = "svg") {
|
|
24
|
+
let isDarkMode = (document.documentElement.getAttribute("data-darkmode") === "true") ? false : true;
|
|
25
|
+
|
|
26
|
+
svgDoc.querySelectorAll('[fill], [stroke]').forEach(fillStroke => {
|
|
27
|
+
|
|
28
|
+
for (const colors of colors_svg_lottie) {
|
|
29
|
+
for (const color of colors) {
|
|
30
|
+
let OLD = (color[0] || '').replace(/\s/g, '');
|
|
31
|
+
let NEW = (color[1] || '').replace(/\s/g, '');
|
|
32
|
+
|
|
33
|
+
["fill", "stroke"].forEach(attr => {
|
|
34
|
+
const current = fillStroke.getAttribute(attr);
|
|
35
|
+
|
|
36
|
+
fillStroke.style.transition = "fill 0.5s ease, stroke 0.5s ease";
|
|
37
|
+
|
|
38
|
+
if (!isDarkMode) {
|
|
39
|
+
if (current == OLD) fillStroke.setAttribute(attr, NEW)
|
|
40
|
+
} else {
|
|
41
|
+
if (current == NEW) fillStroke.setAttribute(attr, OLD)
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
//== OBJECTS
|
|
50
|
+
const objects = document.querySelectorAll('object[type="image/svg+xml"]');
|
|
51
|
+
objects.forEach(obj => {
|
|
52
|
+
|
|
53
|
+
//=== ALL FILL / STROKE
|
|
54
|
+
obj.addEventListener("load", () => {
|
|
55
|
+
const svgDoc = obj.contentDocument;
|
|
56
|
+
if (svgDoc) changecolor(svgDoc, "svg")
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
//=== In caso l'object sia già caricato
|
|
60
|
+
if (obj.contentDocument) {
|
|
61
|
+
const event = new Event("load");
|
|
62
|
+
obj.dispatchEvent(event);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
//== LOTTIE
|
|
67
|
+
const lottiePlayers = document.querySelectorAll('lottie-player');
|
|
68
|
+
lottiePlayers.forEach(lottiePlayer => {
|
|
69
|
+
|
|
70
|
+
//=== ALL FILL / STROKE
|
|
71
|
+
lottiePlayer.addEventListener("load", () => {
|
|
72
|
+
const svg = lottiePlayer.shadowRoot.querySelector('svg');
|
|
73
|
+
if (svg) changecolor(svg, "lottie")
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
//=== In caso lottie sia già caricato
|
|
77
|
+
if (lottiePlayer.shadowRoot) {
|
|
78
|
+
const event = new Event("load");
|
|
79
|
+
lottiePlayer.dispatchEvent(event);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
//= BUTTON DARKMODE
|
|
87
|
+
//+ CHANGE ICON AND SVG
|
|
88
|
+
let toggleDarkmode = pgs(document).querySelectorAll("toggleDarkmode");
|
|
89
|
+
if (localStorage.getItem("screenIsDarkMode") === "true") {
|
|
90
|
+
document.body.classList.add("darkmode");
|
|
91
|
+
document.querySelector(":root").setAttribute("data-darkmode", "true");
|
|
92
|
+
document.body.setAttribute("data-darkmode", "true");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function change(selector, isDarkMode) {
|
|
96
|
+
selector.forEach(button => {
|
|
97
|
+
const ICON = button.querySelector("i");
|
|
98
|
+
if (!ICON) return;
|
|
99
|
+
ICON.classList.toggle("fa-moon", !isDarkMode);
|
|
100
|
+
ICON.classList.toggle("fa-sun", isDarkMode);
|
|
101
|
+
});
|
|
102
|
+
darkmodeColorSVG();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
toggleDarkmode.forEach(button => {
|
|
106
|
+
|
|
107
|
+
//== EXECUTE IMMEDIATELY
|
|
108
|
+
let isDarkMode = document.documentElement.getAttribute("data-darkmode") === "true";
|
|
109
|
+
change(toggleDarkmode, isDarkMode);
|
|
110
|
+
|
|
111
|
+
//== CLICK BUTTON
|
|
112
|
+
button.addEventListener("click", () => {
|
|
113
|
+
let isDarkMode = (document.documentElement.getAttribute("data-darkmode") === "true") ? false : true;
|
|
114
|
+
localStorage.setItem("screenIsDarkMode", isDarkMode);
|
|
115
|
+
|
|
116
|
+
document.body.classList.toggle("darkmode", isDarkMode);
|
|
117
|
+
document.body.setAttribute("data-darkmode", isDarkMode);
|
|
118
|
+
document.querySelector(":root").setAttribute("data-darkmode", isDarkMode);
|
|
119
|
+
change(toggleDarkmode, isDarkMode);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
2
|
+
const objects = document.querySelectorAll('object[type="image/svg+xml"]');
|
|
3
|
+
|
|
4
|
+
objects.forEach(obj => {
|
|
5
|
+
function applyAspectRatio(svgDoc) {
|
|
6
|
+
if (!svgDoc) return;
|
|
7
|
+
const svg = svgDoc.querySelector("svg");
|
|
8
|
+
if (!svg) return;
|
|
9
|
+
|
|
10
|
+
const computedStyle = window.getComputedStyle(obj);
|
|
11
|
+
svg.setAttribute("preserveAspectRatio", computedStyle.objectFit === 'cover' ? "xMidYMid slice" : "xMidYMid meet");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function init() {
|
|
15
|
+
const svgDoc = obj.contentDocument;
|
|
16
|
+
if (!svgDoc) return;
|
|
17
|
+
|
|
18
|
+
const svg = svgDoc.querySelector('svg');
|
|
19
|
+
if (!svg) return;
|
|
20
|
+
|
|
21
|
+
applyAspectRatio(svgDoc);
|
|
22
|
+
if (obj.__objectResizeObserver) return;
|
|
23
|
+
|
|
24
|
+
let rafId = 0;
|
|
25
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
26
|
+
if (rafId) return;
|
|
27
|
+
rafId = requestAnimationFrame(() => {
|
|
28
|
+
rafId = 0;
|
|
29
|
+
applyAspectRatio(svgDoc);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
resizeObserver.observe(obj);
|
|
34
|
+
obj.__objectResizeObserver = resizeObserver;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (obj.contentDocument && obj.contentDocument.querySelector('svg')) init();
|
|
38
|
+
else obj.addEventListener('load', init, { once: true });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
document.body.classList.add("object-loaded");
|
|
42
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
//= ACCORDION
|
|
2
|
+
const allAccordion = pgs(document).querySelectorAll("accordion")
|
|
3
|
+
|
|
4
|
+
allAccordion.forEach((accordion, index) => {
|
|
5
|
+
|
|
6
|
+
const BUTTON = pgs(accordion).querySelector("accordion-button");
|
|
7
|
+
const CONTENT = pgs(accordion).querySelector("accordion-content");
|
|
8
|
+
|
|
9
|
+
//== ID univoci per aria-controls / aria-labelledby
|
|
10
|
+
const ID = index + 1;
|
|
11
|
+
const btnId = `acc-btn-${ID}`;
|
|
12
|
+
const panelId = `acc-panel-${ID}`;
|
|
13
|
+
|
|
14
|
+
//== Stato iniziale
|
|
15
|
+
const isOpenInit = pgs(accordion).state.contains("open");
|
|
16
|
+
|
|
17
|
+
//== Accessibilità (setup una volta)
|
|
18
|
+
BUTTON.setAttribute("role", "button");
|
|
19
|
+
BUTTON.setAttribute("tabindex", "0");
|
|
20
|
+
BUTTON.setAttribute("id", btnId);
|
|
21
|
+
BUTTON.setAttribute("aria-controls", panelId);
|
|
22
|
+
|
|
23
|
+
CONTENT.setAttribute("id", panelId);
|
|
24
|
+
CONTENT.setAttribute("role", "region");
|
|
25
|
+
CONTENT.setAttribute("aria-labelledby", btnId);
|
|
26
|
+
|
|
27
|
+
//+ Accessibility (applica stato aperto/chiuso)
|
|
28
|
+
function accordionAccessibility(isOpen, button, content) {
|
|
29
|
+
const text = (button?.textContent || "").trim().replace(/\s+/g, " ");
|
|
30
|
+
button.setAttribute("aria-label", `${isOpen ? "Chiudi" : "Apri"} ${text || "sezione"}`);
|
|
31
|
+
button.setAttribute("aria-expanded", String(isOpen));
|
|
32
|
+
content.hidden = !isOpen;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
//+ Chiudi tutti gli altri
|
|
36
|
+
function closeOltherAccordion() {
|
|
37
|
+
for (const otherLi of allAccordion) {
|
|
38
|
+
if (otherLi === accordion) continue;
|
|
39
|
+
|
|
40
|
+
const otherBtn = pgs(otherLi).querySelector("accordion-button");
|
|
41
|
+
const otherContent = pgs(otherLi).querySelector("accordion-content");
|
|
42
|
+
if (!otherBtn || !otherContent) continue;
|
|
43
|
+
|
|
44
|
+
pgs(otherLi).state().remove("open");
|
|
45
|
+
accordionAccessibility(false, otherBtn, otherContent);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
//+ FN ACCORDION
|
|
50
|
+
function accordionFunction() {
|
|
51
|
+
const isOpen = pgs(accordion).state.contains("open");
|
|
52
|
+
const nowOpen = !isOpen;
|
|
53
|
+
|
|
54
|
+
pgs(accordion).state.toggle("open", nowOpen);
|
|
55
|
+
accordionAccessibility(nowOpen, BUTTON, CONTENT);
|
|
56
|
+
|
|
57
|
+
closeOltherAccordion();
|
|
58
|
+
|
|
59
|
+
//== scroll to view
|
|
60
|
+
if (nowOpen) setTimeout(() => accordion.scrollIntoView({ block: "nearest", inline: "nearest" }), 100);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// applica stato iniziale
|
|
64
|
+
accordionAccessibility(isOpenInit, BUTTON, CONTENT);
|
|
65
|
+
|
|
66
|
+
//- Eventi
|
|
67
|
+
BUTTON.addEventListener("click", accordionFunction);
|
|
68
|
+
|
|
69
|
+
//- Tastiera: Enter / Space
|
|
70
|
+
BUTTON.addEventListener("keydown", (e) => {
|
|
71
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
accordionFunction();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// + dropdown (Popover API)
|
|
2
|
+
export function pgs_dropdown() {
|
|
3
|
+
pgs(document).querySelectorAll("dropdown").forEach((DROPDOWN, index) => {
|
|
4
|
+
const BUTTON = pgs(DROPDOWN).querySelector("dropdown-button");
|
|
5
|
+
const CONTENT = pgs(DROPDOWN).querySelector("dropdown-content");
|
|
6
|
+
|
|
7
|
+
if (!BUTTON || !CONTENT) return;
|
|
8
|
+
|
|
9
|
+
// = INIT
|
|
10
|
+
if (DROPDOWN.getAttribute("data-initialize") === "true") return;
|
|
11
|
+
DROPDOWN.setAttribute("data-initialize", "true");
|
|
12
|
+
|
|
13
|
+
// == CSS dropdown-anchor
|
|
14
|
+
const ANCHOR_NAME = `--dropdown-anchor-${index}`;
|
|
15
|
+
DROPDOWN.style.setProperty("--dropdown-anchor", ANCHOR_NAME);
|
|
16
|
+
|
|
17
|
+
// == IDs + ACCESSIBILITY
|
|
18
|
+
if (!BUTTON.id) BUTTON.id = `dropdown-btn-${index}`;
|
|
19
|
+
if (!CONTENT.id) CONTENT.id = `dropdown-panel-${index}`;
|
|
20
|
+
BUTTON.setAttribute("type", "button");
|
|
21
|
+
BUTTON.setAttribute("aria-haspopup", "true");
|
|
22
|
+
BUTTON.setAttribute("aria-controls", CONTENT.id);
|
|
23
|
+
BUTTON.setAttribute("aria-expanded", "false");
|
|
24
|
+
CONTENT.setAttribute("aria-labelledby", BUTTON.id);
|
|
25
|
+
|
|
26
|
+
// == POPVER SETUP
|
|
27
|
+
if (!CONTENT.hasAttribute("popover")) CONTENT.setAttribute("popover", "auto");
|
|
28
|
+
BUTTON.setAttribute("popovertarget", CONTENT.id);
|
|
29
|
+
BUTTON.setAttribute("popovertargetaction", "toggle");
|
|
30
|
+
|
|
31
|
+
//-( Safari / legacy fallback: popover is in the top layer, so fixed coords are viewport-based.
|
|
32
|
+
const HAS_ANCHOR_POSITIONING =
|
|
33
|
+
CSS.supports("anchor-name: --dropdown-anchor") &&
|
|
34
|
+
CSS.supports("position-anchor: --dropdown-anchor") &&
|
|
35
|
+
CSS.supports("position-area: bottom") &&
|
|
36
|
+
CSS.supports("top: anchor(bottom)");
|
|
37
|
+
|
|
38
|
+
const updatePopoverPosition = () => {
|
|
39
|
+
if (HAS_ANCHOR_POSITIONING) return;
|
|
40
|
+
|
|
41
|
+
const buttonRect = BUTTON.getBoundingClientRect();
|
|
42
|
+
const style = getComputedStyle(DROPDOWN);
|
|
43
|
+
const offset = parseFloat(style.getPropertyValue("--dropdown-offset")) || 10;
|
|
44
|
+
const padding = parseFloat(style.getPropertyValue("--dropdown-padding")) || 0;
|
|
45
|
+
const arrowSize = parseFloat(style.getPropertyValue("--dropdown-arrow-size")) || 12;
|
|
46
|
+
const viewportGap = 8;
|
|
47
|
+
const viewportWidth = window.innerWidth;
|
|
48
|
+
const maxWidth = viewportWidth - viewportGap * 2;
|
|
49
|
+
const contentStyle = getComputedStyle(CONTENT);
|
|
50
|
+
const cssMaxWidth = parseFloat(contentStyle.maxWidth);
|
|
51
|
+
const dropdownMaxWidth = Number.isFinite(cssMaxWidth) ? Math.min(cssMaxWidth, maxWidth) : maxWidth;
|
|
52
|
+
const contentWidth = Math.min(
|
|
53
|
+
Math.max(CONTENT.scrollWidth + padding * 2, buttonRect.width),
|
|
54
|
+
dropdownMaxWidth
|
|
55
|
+
);
|
|
56
|
+
const top = buttonRect.bottom + offset;
|
|
57
|
+
const centeredLeft = buttonRect.left + buttonRect.width / 2 - contentWidth / 2;
|
|
58
|
+
const left = Math.min(
|
|
59
|
+
Math.max(centeredLeft, viewportGap),
|
|
60
|
+
viewportWidth - contentWidth - viewportGap
|
|
61
|
+
);
|
|
62
|
+
const buttonCenter = buttonRect.left + buttonRect.width / 2;
|
|
63
|
+
const arrowLeft = Math.min(
|
|
64
|
+
Math.max(buttonCenter - left, padding + arrowSize),
|
|
65
|
+
contentWidth - padding - arrowSize
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
DROPDOWN.style.setProperty("--dropdown-fallback-top", `${top}px`);
|
|
69
|
+
DROPDOWN.style.setProperty("--dropdown-fallback-left", `${left}px`);
|
|
70
|
+
DROPDOWN.style.setProperty("--dropdown-arrow-left", `${arrowLeft}px`);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
BUTTON.addEventListener("click", e => {
|
|
74
|
+
if (HAS_ANCHOR_POSITIONING) return;
|
|
75
|
+
|
|
76
|
+
e.preventDefault();
|
|
77
|
+
if (CONTENT.matches(":popover-open")) {
|
|
78
|
+
CONTENT.hidePopover();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
updatePopoverPosition();
|
|
83
|
+
CONTENT.showPopover();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// == sync ARIA + data-open quando apre/chiude
|
|
87
|
+
CONTENT.addEventListener("toggle", e => {
|
|
88
|
+
const open = CONTENT.matches(":popover-open");
|
|
89
|
+
BUTTON.setAttribute("aria-expanded", open ? "true" : "false");
|
|
90
|
+
pgs(DROPDOWN).state.toggle("open", open);
|
|
91
|
+
if (open) {
|
|
92
|
+
updatePopoverPosition();
|
|
93
|
+
CONTENT.querySelector('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])')?.focus();
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
window.addEventListener("resize", () => {
|
|
98
|
+
if (CONTENT.matches(":popover-open")) updatePopoverPosition();
|
|
99
|
+
});
|
|
100
|
+
window.addEventListener("scroll", () => {
|
|
101
|
+
if (CONTENT.matches(":popover-open")) updatePopoverPosition();
|
|
102
|
+
}, true);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// # INIT
|
|
107
|
+
pgs_dropdown();
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { PGS_notification, PGS_toast } from "../functions/_notifications.js";
|
|
2
|
+
|
|
3
|
+
function escapeHtml(value) {
|
|
4
|
+
return String(value ?? "");
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function getDuration(notification) {
|
|
8
|
+
const rawDuration = notification.duration;
|
|
9
|
+
const duration = Number.parseInt(rawDuration, 10);
|
|
10
|
+
|
|
11
|
+
return Number.isNaN(duration) ? 5000 : duration;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function getNotificationApi(notification) {
|
|
15
|
+
const element = String(notification.element || "notification").trim();
|
|
16
|
+
|
|
17
|
+
return element === "toast" ? PGS_toast : PGS_notification;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getNotificationType(notification, api) {
|
|
21
|
+
const type = String(notification.type || "info").trim();
|
|
22
|
+
|
|
23
|
+
return typeof api[type] === "function" ? type : "info";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getNotificationData(root) {
|
|
27
|
+
try {
|
|
28
|
+
return JSON.parse(root.dataset.notification || "{}");
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.warn("PGS notification: dati non validi", error);
|
|
31
|
+
return {};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getNotificationContent(title, content) {
|
|
36
|
+
const safeContent = escapeHtml(content);
|
|
37
|
+
const safeTitle = escapeHtml(title);
|
|
38
|
+
|
|
39
|
+
if (!safeTitle) return safeContent;
|
|
40
|
+
if (!safeContent) return `<span pgs="notification-element-title">${safeTitle}</span>`;
|
|
41
|
+
|
|
42
|
+
return `
|
|
43
|
+
<span pgs="notification-element-title">${safeTitle}</span>
|
|
44
|
+
<br>
|
|
45
|
+
<span pgs="notification-element-content">${safeContent}</span>
|
|
46
|
+
`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function PGS_md_notification(root) {
|
|
50
|
+
if (!root || root.dataset.initialize === "true") return;
|
|
51
|
+
root.dataset.initialize = "true";
|
|
52
|
+
|
|
53
|
+
const notification = getNotificationData(root);
|
|
54
|
+
const title = String(notification.title || "").trim();
|
|
55
|
+
const content = String(notification.message || "").trim();
|
|
56
|
+
if (!title && !content) {
|
|
57
|
+
root.remove();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const link = notification.link || null;
|
|
62
|
+
const icon = notification.icon || undefined;
|
|
63
|
+
const duration = getDuration(notification);
|
|
64
|
+
const api = getNotificationApi(notification);
|
|
65
|
+
const type = getNotificationType(notification, api);
|
|
66
|
+
const formattedContent = getNotificationContent(title, content);
|
|
67
|
+
|
|
68
|
+
if (api === PGS_toast) {
|
|
69
|
+
api[type](formattedContent, duration, icon);
|
|
70
|
+
} else {
|
|
71
|
+
api[type](formattedContent, link, duration, icon);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
root.remove();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function initMdNotifications() {
|
|
78
|
+
pgs(document).querySelectorAll("notificationTrigger").forEach(PGS_md_notification);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (document.readyState === "loading") {
|
|
82
|
+
document.addEventListener("DOMContentLoaded", initMdNotifications);
|
|
83
|
+
} else {
|
|
84
|
+
initMdNotifications();
|
|
85
|
+
}
|