mypgs 1.1.2 → 1.3.2
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 +124 -11
- package/assets/javascript/_imports.js +25 -0
- package/assets/javascript/_pgs.js +37 -1
- package/assets/javascript/components/_accordion.js +105 -66
- package/assets/javascript/components/_dropdown.js +51 -5
- package/assets/javascript/components/_menu.js +71 -6
- package/assets/javascript/components/_modals.js +45 -3
- package/assets/javascript/components/_notifications.js +163 -0
- package/assets/javascript/components/_slides.js +12 -1
- package/assets/javascript/components/_stepTabs.js +18 -5
- package/assets/javascript/components/_steps.js +33 -6
- package/assets/javascript/functions/_formValidate.js +4 -5
- package/assets/javascript/index.js +8 -4
- package/assets/javascript/patterns/_header.js +1 -1
- package/assets/javascript/pgs.d.ts +51 -16
- package/assets/scss/components/_dropdown.scss +19 -26
- package/assets/scss/components/_logo.scss +1 -1
- package/assets/scss/components/_tooltip.scss +9 -1
- package/assets/scss/layout/_pageShell.scss +15 -20
- package/assets/scss/mixin/_mx-button.scss +12 -12
- package/dist/css/index.css +94 -96
- package/dist/css/index.css.map +1 -1
- package/dist/css/index.min.css +1 -1
- package/dist/index.d.ts +50 -16
- package/dist/javascript/index.js +840 -271
- package/dist/javascript/index.js.map +1 -1
- package/dist/javascript/index.min.js +1 -1
- package/package.json +7 -2
- package/pgs-html-suggestions.vsix +0 -0
- package/templates/components/md-button.html +13 -7
- package/templates/components/md-card.html +11 -9
- package/templates/components/md-dropdown.html +1 -1
- package/templates/components/md-modal.html +4 -4
- package/templates/components/md-stepTabs.html +3 -3
- package/templates/components/md-tooltip.html +1 -0
- package/templates/demo.css +4 -0
- package/templates/demo.js +2 -2
- package/assets/javascript/components/_exeNotifications.js +0 -85
- package/assets/javascript/functions/_notifications.js +0 -74
- package/assets/javascript/functions/_sendForm.js +0 -100
package/README.md
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# MyPGS
|
|
2
2
|
|
|
3
|
-
Libreria frontend
|
|
3
|
+
Libreria frontend condivisa per componenti, layout e pattern basati sull'attributo `pgs`.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Contiene sorgenti SCSS, mixin, comportamenti JavaScript, template HTML di esempio e asset compilati pronti all'uso. Puo' essere usata sia importando direttamente CSS e JavaScript compilati, sia importando gli SCSS sorgenti dentro il build del progetto che la utilizza.
|
|
6
|
+
|
|
7
|
+
## Installazione come dipendenza
|
|
8
|
+
|
|
9
|
+
Da registry npm, se pubblicata:
|
|
6
10
|
|
|
7
11
|
```bash
|
|
8
12
|
npm install mypgs
|
|
@@ -10,24 +14,133 @@ npm install mypgs
|
|
|
10
14
|
|
|
11
15
|
## Utilizzo
|
|
12
16
|
|
|
13
|
-
|
|
17
|
+
### JavaScript
|
|
18
|
+
|
|
19
|
+
Importa la libreria nell'entrypoint JavaScript del progetto:
|
|
14
20
|
|
|
15
21
|
```js
|
|
16
|
-
import "mypgs";
|
|
17
|
-
|
|
22
|
+
import * as mypgs from "mypgs";
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
L'entrypoint di MyPGS inizializza:
|
|
26
|
+
|
|
27
|
+
- helper globale `pgs`
|
|
28
|
+
- dark mode
|
|
29
|
+
- gestione oggetti PGS
|
|
30
|
+
- accordion
|
|
31
|
+
- dropdown
|
|
32
|
+
- menu
|
|
33
|
+
- modali
|
|
34
|
+
- slides
|
|
35
|
+
- steps
|
|
36
|
+
- step tabs
|
|
37
|
+
- notifiche
|
|
38
|
+
- header
|
|
39
|
+
- cookie consent
|
|
40
|
+
|
|
41
|
+
Il bundle finale viene generato dal progetto che importa `mypgs`, in base alla propria configurazione Webpack o al proprio bundler.
|
|
42
|
+
|
|
43
|
+
### SCSS
|
|
44
|
+
|
|
45
|
+
Per integrare MyPGS nel build SCSS del progetto, importa mixin e stili sorgenti da `node_modules`:
|
|
46
|
+
|
|
47
|
+
```scss
|
|
48
|
+
@import "../../node_modules/mypgs/assets/scss/mixin/mixin.scss";
|
|
49
|
+
@import "../../node_modules/mypgs/assets/scss/index.scss";
|
|
18
50
|
```
|
|
19
51
|
|
|
20
|
-
|
|
52
|
+
Se servono solo i mixin, importa solo il file dedicato:
|
|
53
|
+
|
|
54
|
+
```scss
|
|
55
|
+
@import "../../node_modules/mypgs/assets/scss/mixin/mixin.scss";
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Molti layout, componenti e pattern sono inclusi sotto `[pgs~=initP]`, quindi il markup principale deve abilitare la libreria così:
|
|
59
|
+
|
|
60
|
+
```html
|
|
61
|
+
<html pgs="htmlBase initP">
|
|
62
|
+
<body pgs="bodyBase bodyImg bodyText bodyHeading">
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Markup PGS
|
|
66
|
+
|
|
67
|
+
I componenti vengono attivati usando token nell'attributo `pgs`, per esempio:
|
|
68
|
+
|
|
69
|
+
```html
|
|
70
|
+
<nav pgs="menuHorizontal"></nav>
|
|
71
|
+
<button pgs="button modal-button"></button>
|
|
72
|
+
<div pgs="accordion">
|
|
73
|
+
<button pgs="accordion-button"></button>
|
|
74
|
+
<div pgs="accordion-content"></div>
|
|
75
|
+
</div>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Gli stessi token devono restare coerenti tra markup, SCSS e JavaScript.
|
|
21
79
|
|
|
22
80
|
## Contenuto del pacchetto
|
|
23
81
|
|
|
24
|
-
- `
|
|
25
|
-
- `
|
|
26
|
-
- `
|
|
27
|
-
- `dist/
|
|
82
|
+
- `assets/scss/`: sorgenti SCSS, mixin, base, layout, componenti e pattern
|
|
83
|
+
- `assets/javascript/`: sorgenti JavaScript dei comportamenti PGS
|
|
84
|
+
- `templates/`: esempi HTML dei componenti e dei layout
|
|
85
|
+
- `dist/javascript/index.js`: bundle JavaScript non minificato
|
|
86
|
+
- `dist/javascript/index.min.js`: bundle JavaScript minificato
|
|
87
|
+
- `dist/css/index.css`: CSS compilato non minificato
|
|
88
|
+
- `dist/css/index.min.css`: CSS compilato minificato
|
|
89
|
+
- `dist/index.d.ts`: dichiarazioni TypeScript esportate dal pacchetto
|
|
90
|
+
|
|
91
|
+
## Export npm
|
|
92
|
+
|
|
93
|
+
Il pacchetto espone:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
".": "./dist/javascript/index.js",
|
|
98
|
+
"./style.css": "./dist/css/index.css",
|
|
99
|
+
"./style.min.css": "./dist/css/index.min.css"
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Uso diretto degli asset compilati:
|
|
104
|
+
|
|
105
|
+
```js
|
|
106
|
+
import "mypgs";
|
|
107
|
+
import "mypgs/style.css";
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
In alternativa, puoi importare gli SCSS sorgenti direttamente nel build del progetto quando vuoi ottenere un unico CSS finale.
|
|
28
111
|
|
|
29
112
|
## Sviluppo
|
|
30
113
|
|
|
114
|
+
Build una tantum:
|
|
115
|
+
|
|
31
116
|
```bash
|
|
32
|
-
npm run
|
|
117
|
+
npm run start
|
|
33
118
|
```
|
|
119
|
+
|
|
120
|
+
Build in watch mode:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
npm run "start watch"
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Per preparare un archivio locale installabile in un altro progetto:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
npm pack
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Quando modifichi MyPGS e vuoi provarlo in un progetto che la usa:
|
|
133
|
+
|
|
134
|
+
1. ricompila MyPGS con `npm run start`
|
|
135
|
+
2. crea un nuovo archivio con `npm pack`
|
|
136
|
+
3. copia o aggiorna il `.tgz` usato dal progetto
|
|
137
|
+
4. esegui `npm install` nel progetto
|
|
138
|
+
5. ricompila il progetto
|
|
139
|
+
|
|
140
|
+
## Convenzioni
|
|
141
|
+
|
|
142
|
+
- Usa l'attributo `pgs` per collegare markup, CSS e JavaScript.
|
|
143
|
+
- Mantieni i token PGS coerenti tra template HTML, componenti, moduli e script.
|
|
144
|
+
- Metti in MyPGS solo stili e comportamenti riutilizzabili.
|
|
145
|
+
- Mantieni nel progetto finale gli stili e le personalizzazioni specifiche.
|
|
146
|
+
- Esporta in `dist/` prima di creare il pacchetto locale `.tgz`.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { pgs } from "./_pgs.js";
|
|
2
|
+
|
|
3
|
+
import { PGS_accordion } from "./components/_accordion.js";
|
|
4
|
+
import { PGS_dropdown } from "./components/_dropdown.js";
|
|
5
|
+
import { PGS_menu } from "./components/_menu.js";
|
|
6
|
+
import { PGS_modal } from "./components/_modals.js";
|
|
7
|
+
import { PGS_notification } from "./components/_notifications.js";
|
|
8
|
+
import { PGS_slides } from "./components/_slides.js";
|
|
9
|
+
import { PGS_stepTabs } from "./components/_stepTabs.js";
|
|
10
|
+
import { PGS_steps } from "./components/_steps.js";
|
|
11
|
+
import { PGS_formValidate } from "./functions/_formValidate.js";
|
|
12
|
+
import { PGS_scrollHorizontal } from "./functions/_scrollY.js";
|
|
13
|
+
|
|
14
|
+
pgs.registerImport({
|
|
15
|
+
PGS_accordion,
|
|
16
|
+
PGS_dropdown,
|
|
17
|
+
PGS_menu,
|
|
18
|
+
PGS_modal,
|
|
19
|
+
PGS_notification,
|
|
20
|
+
PGS_slides,
|
|
21
|
+
PGS_stepTabs,
|
|
22
|
+
PGS_steps,
|
|
23
|
+
PGS_formValidate,
|
|
24
|
+
PGS_scrollHorizontal,
|
|
25
|
+
});
|
|
@@ -224,6 +224,42 @@ export function pgs(root) {
|
|
|
224
224
|
return api;
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
|
|
227
|
+
const PGS_IMPORTS = {};
|
|
228
|
+
|
|
229
|
+
function registerImportModule(name, module) {
|
|
230
|
+
const key = String(name || "").trim().replace(/^pgs[_-\s]*/i, "").toLowerCase();
|
|
228
231
|
|
|
232
|
+
if (!key) throw new TypeError("pgs.registerImport(...modules): ogni modulo deve avere name o PGS_name");
|
|
233
|
+
|
|
234
|
+
PGS_IMPORTS[key] = {
|
|
235
|
+
name,
|
|
236
|
+
module
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
pgs.registerImport = function (...modules) {
|
|
241
|
+
modules.flat().forEach(item => {
|
|
242
|
+
if (item && typeof item === "object" && !item.PGS_name && !item.name) {
|
|
243
|
+
Object.entries(item).forEach(([name, module]) => registerImportModule(name, module));
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
229
246
|
|
|
247
|
+
registerImportModule(item?.PGS_name || item?.name, item);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
return pgs;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
pgs.import = function (...names) {
|
|
254
|
+
return names.flat().reduce((imports, name) => {
|
|
255
|
+
const key = String(name || "").trim().replace(/^pgs[_-\s]*/i, "").toLowerCase();
|
|
256
|
+
const item = PGS_IMPORTS[key];
|
|
257
|
+
|
|
258
|
+
if (!item) throw new Error(`pgs.import(): modulo "${name}" non registrato`);
|
|
259
|
+
|
|
260
|
+
imports[item.name] = item.module;
|
|
261
|
+
return imports;
|
|
262
|
+
}, {});
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
globalThis.pgs ??= pgs;
|
|
@@ -1,77 +1,116 @@
|
|
|
1
|
-
//= ACCORDION
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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);
|
|
1
|
+
//= ACCORDION
|
|
2
|
+
const API = new WeakMap();
|
|
3
|
+
|
|
4
|
+
export function PGS_accordion_init(root = document) {
|
|
5
|
+
pgs(root).querySelectorAll("accordion").forEach((accordion, index) => {
|
|
6
|
+
if (API.has(accordion)) return;
|
|
7
|
+
|
|
8
|
+
const BUTTON = pgs(accordion).querySelector("accordion-button");
|
|
9
|
+
const CONTENT = pgs(accordion).querySelector("accordion-content");
|
|
10
|
+
if (!BUTTON || !CONTENT) return;
|
|
11
|
+
|
|
12
|
+
//== ID univoci per aria-controls / aria-labelledby
|
|
13
|
+
const ID = index + 1;
|
|
14
|
+
const btnId = `acc-btn-${ID}`;
|
|
15
|
+
const panelId = `acc-panel-${ID}`;
|
|
16
|
+
|
|
17
|
+
//== Stato iniziale
|
|
18
|
+
const isOpenInit = pgs(accordion).state.contains("open");
|
|
19
|
+
|
|
20
|
+
//== Accessibilità (setup una volta)
|
|
21
|
+
BUTTON.setAttribute("role", "button");
|
|
22
|
+
BUTTON.setAttribute("tabindex", "0");
|
|
23
|
+
BUTTON.setAttribute("id", btnId);
|
|
24
|
+
BUTTON.setAttribute("aria-controls", panelId);
|
|
25
|
+
|
|
26
|
+
CONTENT.setAttribute("id", panelId);
|
|
27
|
+
CONTENT.setAttribute("role", "region");
|
|
28
|
+
CONTENT.setAttribute("aria-labelledby", btnId);
|
|
29
|
+
|
|
30
|
+
//+ Accessibility (applica stato aperto/chiuso)
|
|
31
|
+
function accordionAccessibility(isOpen, button, content) {
|
|
32
|
+
const text = (button?.textContent || "").trim().replace(/\s+/g, " ");
|
|
33
|
+
button.setAttribute("aria-label", `${isOpen ? "Chiudi" : "Apri"} ${text || "sezione"}`);
|
|
34
|
+
button.setAttribute("aria-expanded", String(isOpen));
|
|
35
|
+
content.hidden = !isOpen;
|
|
46
36
|
}
|
|
47
|
-
}
|
|
48
37
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
38
|
+
//+ Chiudi tutti gli altri
|
|
39
|
+
function closeOltherAccordion() {
|
|
40
|
+
for (const otherLi of pgs(document).querySelectorAll("accordion")) {
|
|
41
|
+
if (otherLi === accordion) continue;
|
|
53
42
|
|
|
54
|
-
|
|
55
|
-
|
|
43
|
+
const otherBtn = pgs(otherLi).querySelector("accordion-button");
|
|
44
|
+
const otherContent = pgs(otherLi).querySelector("accordion-content");
|
|
45
|
+
if (!otherBtn || !otherContent) continue;
|
|
56
46
|
|
|
57
|
-
|
|
47
|
+
pgs(otherLi).state().remove("open");
|
|
48
|
+
accordionAccessibility(false, otherBtn, otherContent);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
//+ FN ACCORDION
|
|
53
|
+
function accordionFunction() {
|
|
54
|
+
const isOpen = pgs(accordion).state.contains("open");
|
|
55
|
+
const nowOpen = !isOpen;
|
|
58
56
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
57
|
+
pgs(accordion).state.toggle("open", nowOpen);
|
|
58
|
+
accordionAccessibility(nowOpen, BUTTON, CONTENT);
|
|
62
59
|
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
closeOltherAccordion();
|
|
61
|
+
|
|
62
|
+
//== scroll to view
|
|
63
|
+
if (nowOpen) setTimeout(() => accordion.scrollIntoView({ block: "nearest", inline: "nearest" }), 100);
|
|
64
|
+
}
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
function open() {
|
|
67
|
+
if (!pgs(accordion).state.contains("open")) accordionFunction();
|
|
68
|
+
}
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (e.key === "Enter" || e.key === " ") {
|
|
72
|
-
e.preventDefault();
|
|
73
|
-
accordionFunction();
|
|
70
|
+
function close() {
|
|
71
|
+
if (pgs(accordion).state.contains("open")) accordionFunction();
|
|
74
72
|
}
|
|
73
|
+
|
|
74
|
+
// applica stato iniziale
|
|
75
|
+
accordionAccessibility(isOpenInit, BUTTON, CONTENT);
|
|
76
|
+
|
|
77
|
+
//- Eventi
|
|
78
|
+
BUTTON.addEventListener("click", accordionFunction);
|
|
79
|
+
|
|
80
|
+
//- Tastiera: Enter / Space
|
|
81
|
+
BUTTON.addEventListener("keydown", (e) => {
|
|
82
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
83
|
+
e.preventDefault();
|
|
84
|
+
accordionFunction();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
API.set(accordion, {
|
|
89
|
+
element: accordion,
|
|
90
|
+
button: BUTTON,
|
|
91
|
+
content: CONTENT,
|
|
92
|
+
open,
|
|
93
|
+
close,
|
|
94
|
+
toggle: accordionFunction,
|
|
95
|
+
refresh: () => {
|
|
96
|
+
PGS_accordion_init(accordion.parentNode || document);
|
|
97
|
+
return API.get(accordion);
|
|
98
|
+
},
|
|
99
|
+
isOpen: () => pgs(accordion).state.contains("open"),
|
|
100
|
+
});
|
|
75
101
|
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
//# INIT
|
|
105
|
+
PGS_accordion_init();
|
|
106
|
+
|
|
107
|
+
//# API
|
|
108
|
+
export function PGS_accordion_api(selector) {
|
|
109
|
+
return API.get(selector);
|
|
110
|
+
}
|
|
76
111
|
|
|
77
|
-
|
|
112
|
+
export const PGS_accordion = {
|
|
113
|
+
PGS_name: "PGS_accordion",
|
|
114
|
+
init: PGS_accordion_init,
|
|
115
|
+
api: PGS_accordion_api
|
|
116
|
+
};
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// + dropdown (Popover API)
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
const API = new WeakMap();
|
|
3
|
+
|
|
4
|
+
export function PGS_dropdown_init(root = document) {
|
|
5
|
+
pgs(root).querySelectorAll("dropdown").forEach((DROPDOWN, index) => {
|
|
6
|
+
if (API.has(DROPDOWN)) return;
|
|
7
|
+
|
|
4
8
|
const BUTTON = pgs(DROPDOWN).querySelector("dropdown-button");
|
|
5
9
|
const CONTENT = pgs(DROPDOWN).querySelector("dropdown-content");
|
|
6
10
|
|
|
@@ -34,9 +38,10 @@ export function pgs_dropdown() {
|
|
|
34
38
|
CSS.supports("position-anchor: --dropdown-anchor") &&
|
|
35
39
|
CSS.supports("position-area: bottom") &&
|
|
36
40
|
CSS.supports("top: anchor(bottom)");
|
|
41
|
+
const USE_FALLBACK_POSITIONING = pgs(DROPDOWN).contains("tooltip") || !HAS_ANCHOR_POSITIONING;
|
|
37
42
|
|
|
38
43
|
const updatePopoverPosition = () => {
|
|
39
|
-
if (
|
|
44
|
+
if (!USE_FALLBACK_POSITIONING) return;
|
|
40
45
|
|
|
41
46
|
const buttonRect = BUTTON.getBoundingClientRect();
|
|
42
47
|
const style = getComputedStyle(DROPDOWN);
|
|
@@ -71,7 +76,7 @@ export function pgs_dropdown() {
|
|
|
71
76
|
};
|
|
72
77
|
|
|
73
78
|
BUTTON.addEventListener("click", e => {
|
|
74
|
-
if (
|
|
79
|
+
if (!USE_FALLBACK_POSITIONING) return;
|
|
75
80
|
|
|
76
81
|
e.preventDefault();
|
|
77
82
|
if (CONTENT.matches(":popover-open")) {
|
|
@@ -100,8 +105,49 @@ export function pgs_dropdown() {
|
|
|
100
105
|
window.addEventListener("scroll", () => {
|
|
101
106
|
if (CONTENT.matches(":popover-open")) updatePopoverPosition();
|
|
102
107
|
}, true);
|
|
108
|
+
|
|
109
|
+
function open() {
|
|
110
|
+
if (CONTENT.matches(":popover-open")) return;
|
|
111
|
+
updatePopoverPosition();
|
|
112
|
+
CONTENT.showPopover();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function close() {
|
|
116
|
+
if (!CONTENT.matches(":popover-open")) return;
|
|
117
|
+
CONTENT.hidePopover();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function toggle() {
|
|
121
|
+
CONTENT.matches(":popover-open") ? close() : open();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
API.set(DROPDOWN, {
|
|
125
|
+
element: DROPDOWN,
|
|
126
|
+
button: BUTTON,
|
|
127
|
+
content: CONTENT,
|
|
128
|
+
open,
|
|
129
|
+
close,
|
|
130
|
+
toggle,
|
|
131
|
+
updatePosition: updatePopoverPosition,
|
|
132
|
+
refresh: () => {
|
|
133
|
+
PGS_dropdown_init(DROPDOWN.parentNode || document);
|
|
134
|
+
return API.get(DROPDOWN);
|
|
135
|
+
},
|
|
136
|
+
isOpen: () => CONTENT.matches(":popover-open"),
|
|
137
|
+
});
|
|
103
138
|
});
|
|
104
139
|
}
|
|
105
140
|
|
|
106
141
|
// # INIT
|
|
107
|
-
|
|
142
|
+
PGS_dropdown_init();
|
|
143
|
+
|
|
144
|
+
// # API
|
|
145
|
+
export function PGS_dropdown_api(selector) {
|
|
146
|
+
return API.get(selector);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export const PGS_dropdown = {
|
|
150
|
+
PGS_name: "PGS_dropdown",
|
|
151
|
+
init: PGS_dropdown_init,
|
|
152
|
+
api: PGS_dropdown_api
|
|
153
|
+
};
|
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PGS_dropdown_init, PGS_dropdown_api } from "./_dropdown";
|
|
2
|
+
|
|
3
|
+
const API = new WeakMap();
|
|
2
4
|
|
|
3
5
|
//= DROP DOWN MENU
|
|
4
|
-
export function
|
|
6
|
+
export function PGS_menu_init(root = document) {
|
|
5
7
|
|
|
6
|
-
pgs(
|
|
8
|
+
pgs(root).querySelectorAll('menu-horizontal').forEach(MENU => {
|
|
9
|
+
if (API.has(MENU)) return;
|
|
7
10
|
|
|
8
11
|
MENU.querySelectorAll('nav > ul > li.menu-item-has-children').forEach(li => {
|
|
9
12
|
if (li.querySelector("ul")) {
|
|
10
13
|
const ul = li.querySelector("ul");
|
|
14
|
+
if (pgs(li).querySelector("dropdown-button")) return;
|
|
11
15
|
|
|
12
16
|
const button = document.createElement("button");
|
|
13
17
|
button.className = "icon-down";
|
|
@@ -22,14 +26,28 @@ export function PGS_menu() {
|
|
|
22
26
|
pgs(ul).add("menu-vertical")
|
|
23
27
|
}
|
|
24
28
|
});
|
|
29
|
+
|
|
30
|
+
API.set(MENU, {
|
|
31
|
+
element: MENU,
|
|
32
|
+
type: "horizontal",
|
|
33
|
+
items: () => Array.from(MENU.querySelectorAll('nav > ul > li')),
|
|
34
|
+
submenus: () => Array.from(MENU.querySelectorAll('.menu-item-has-children > ul')),
|
|
35
|
+
dropdowns: () => Array.from(MENU.querySelectorAll('.menu-item-has-children')).map(PGS_dropdown_api).filter(Boolean),
|
|
36
|
+
refresh: () => {
|
|
37
|
+
PGS_menu_init(MENU.parentNode || document);
|
|
38
|
+
return API.get(MENU);
|
|
39
|
+
},
|
|
40
|
+
});
|
|
25
41
|
});
|
|
26
42
|
|
|
27
|
-
pgs(
|
|
43
|
+
pgs(root).querySelectorAll('menu-vertical').forEach(MENU => {
|
|
44
|
+
if (API.has(MENU)) return;
|
|
28
45
|
|
|
29
46
|
MENU.querySelectorAll('.menu-item-has-children').forEach((li, index) => {
|
|
30
47
|
const ul = li.querySelector("ul");
|
|
31
48
|
|
|
32
49
|
if (!ul) return
|
|
50
|
+
if (li.querySelector(":scope > button")) return;
|
|
33
51
|
|
|
34
52
|
const button = document.createElement("button");
|
|
35
53
|
button.className = "icon-down buttonIcon";
|
|
@@ -64,6 +82,42 @@ export function PGS_menu() {
|
|
|
64
82
|
});
|
|
65
83
|
});
|
|
66
84
|
|
|
85
|
+
function submenu(index) {
|
|
86
|
+
return MENU.querySelectorAll('.menu-item-has-children > ul')[index];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function setSubmenu(index, open) {
|
|
90
|
+
const ul = submenu(index);
|
|
91
|
+
const button = ul?.parentElement?.querySelector(":scope > button");
|
|
92
|
+
if (!ul || !button) return;
|
|
93
|
+
|
|
94
|
+
pgs(ul).state.toggle("open", open);
|
|
95
|
+
button.setAttribute("aria-expanded", String(open));
|
|
96
|
+
button.setAttribute("aria-label", open ? "Chiudi sottomenu" : "Apri sottomenu");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
API.set(MENU, {
|
|
100
|
+
element: MENU,
|
|
101
|
+
type: "vertical",
|
|
102
|
+
items: () => Array.from(MENU.querySelectorAll(':scope > li')),
|
|
103
|
+
submenus: () => Array.from(MENU.querySelectorAll('.menu-item-has-children > ul')),
|
|
104
|
+
openSubmenu: (index) => setSubmenu(index, true),
|
|
105
|
+
closeSubmenu: (index) => setSubmenu(index, false),
|
|
106
|
+
toggleSubmenu: (index) => {
|
|
107
|
+
const ul = submenu(index);
|
|
108
|
+
if (!ul) return;
|
|
109
|
+
setSubmenu(index, !pgs(ul).state.contains("open"));
|
|
110
|
+
},
|
|
111
|
+
isSubmenuOpen: (index) => {
|
|
112
|
+
const ul = submenu(index);
|
|
113
|
+
return ul ? pgs(ul).state.contains("open") : false;
|
|
114
|
+
},
|
|
115
|
+
refresh: () => {
|
|
116
|
+
PGS_menu_init(MENU.parentNode || document);
|
|
117
|
+
return API.get(MENU);
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
67
121
|
// pgs(document).querySelectorAll('menu-vertical').forEach(MENU => {
|
|
68
122
|
|
|
69
123
|
// MENU.querySelectorAll('.menu-item-has-children').forEach(li => {
|
|
@@ -85,8 +139,19 @@ export function PGS_menu() {
|
|
|
85
139
|
// });
|
|
86
140
|
// });
|
|
87
141
|
});
|
|
88
|
-
|
|
142
|
+
PGS_dropdown_init()
|
|
89
143
|
}
|
|
90
144
|
|
|
91
145
|
//# INIT PGS_menu
|
|
92
|
-
|
|
146
|
+
PGS_menu_init()
|
|
147
|
+
|
|
148
|
+
//# API
|
|
149
|
+
export function PGS_menu_api(selector) {
|
|
150
|
+
return API.get(selector);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export const PGS_menu = {
|
|
154
|
+
PGS_name: "PGS_menu",
|
|
155
|
+
init: PGS_menu_init,
|
|
156
|
+
api: PGS_menu_api
|
|
157
|
+
};
|