@agence-zef/astro 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/Popover.astro +74 -5
- package/package.json +1 -1
package/components/Popover.astro
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
/**
|
|
3
|
-
* Popover natif (attribut `popover`), sans
|
|
4
|
-
* `<button popovertarget="<id>">`,
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* Popover natif (attribut `popover`), sans style imposé : ouvert par un ou plusieurs boutons
|
|
4
|
+
* `<button popovertarget="<id>">`, placé contre le bouton qui l'a ouvert par le positionnement
|
|
5
|
+
* d'ancre CSS (`styles.css`, au centre de l'écran en repli). Classe `zef-popover`.
|
|
6
|
+
*
|
|
7
|
+
* Comportement (script ci-dessous, partagé par toute la page) :
|
|
8
|
+
* - un autre bouton du même popover pendant qu'il est ouvert : le popover se déplace vers lui
|
|
9
|
+
* tout de suite, sans se fermer ni se rouvrir (le site met son contenu à jour dans son propre
|
|
10
|
+
* `click`) ; le même bouton : il se ferme ;
|
|
11
|
+
* - clic en dehors ou Échap : il se ferme (sauf `manual`).
|
|
7
12
|
*/
|
|
8
13
|
interface Props {
|
|
9
14
|
id: string;
|
|
15
|
+
/** Ne se ferme ni au clic en dehors ni par Échap (seulement par son bouton). */
|
|
10
16
|
manual?: boolean;
|
|
11
17
|
/** Position par rapport au bouton : en dessous (défaut), au-dessus, à droite, à gauche. */
|
|
12
18
|
placement?: "bottom" | "top" | "right" | "left";
|
|
@@ -16,6 +22,69 @@ interface Props {
|
|
|
16
22
|
const { id, manual = false, placement = "bottom", class: className } = Astro.props;
|
|
17
23
|
---
|
|
18
24
|
|
|
19
|
-
<div
|
|
25
|
+
<div
|
|
26
|
+
id={id}
|
|
27
|
+
popover="manual"
|
|
28
|
+
class:list={["zef-popover", className]}
|
|
29
|
+
data-placement={placement}
|
|
30
|
+
data-zef-popover={manual ? "manual" : "auto"}
|
|
31
|
+
>
|
|
20
32
|
<slot />
|
|
21
33
|
</div>
|
|
34
|
+
|
|
35
|
+
<script>
|
|
36
|
+
/** Bouton qui a ouvert chaque popover (ancre CSS). */
|
|
37
|
+
const opener = new WeakMap<HTMLElement, HTMLElement>();
|
|
38
|
+
const popovers = () => document.querySelectorAll<HTMLElement>("[data-zef-popover]");
|
|
39
|
+
const isOpen = (pop: HTMLElement) => pop.matches(":popover-open");
|
|
40
|
+
|
|
41
|
+
/** Accroche le popover à ce bouton : l'ancre suit le bouton, même pendant l'ouverture. */
|
|
42
|
+
function anchorTo(pop: HTMLElement, trigger: HTMLElement) {
|
|
43
|
+
const name = `--zef-popover-${pop.id}`;
|
|
44
|
+
const previous = opener.get(pop);
|
|
45
|
+
if (previous && previous !== trigger) {
|
|
46
|
+
previous.style.removeProperty("anchor-name");
|
|
47
|
+
previous.setAttribute("aria-expanded", "false");
|
|
48
|
+
}
|
|
49
|
+
trigger.style.setProperty("anchor-name", name);
|
|
50
|
+
trigger.setAttribute("aria-expanded", "true");
|
|
51
|
+
pop.style.setProperty("position-anchor", name);
|
|
52
|
+
opener.set(pop, trigger);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
document.addEventListener("click", (event) => {
|
|
56
|
+
const target = event.target as Element;
|
|
57
|
+
const trigger = target.closest<HTMLElement>("[popovertarget]");
|
|
58
|
+
const pop = trigger ? document.getElementById(trigger.getAttribute("popovertarget") ?? "") : null;
|
|
59
|
+
if (trigger && pop?.hasAttribute("data-zef-popover")) {
|
|
60
|
+
// Le comportement natif (basculer) est remplacé : déplacer plutôt que fermer.
|
|
61
|
+
event.preventDefault();
|
|
62
|
+
if (isOpen(pop) && opener.get(pop) === trigger) return pop.hidePopover();
|
|
63
|
+
anchorTo(pop, trigger);
|
|
64
|
+
if (!isOpen(pop)) pop.showPopover();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
// Clic en dehors : les popovers ouverts se ferment.
|
|
68
|
+
for (const open of popovers()) {
|
|
69
|
+
if (isOpen(open) && open.dataset.zefPopover !== "manual" && !open.contains(target)) {
|
|
70
|
+
open.hidePopover();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
document.addEventListener("keydown", (event) => {
|
|
75
|
+
if (event.key !== "Escape") return;
|
|
76
|
+
for (const open of popovers()) {
|
|
77
|
+
if (isOpen(open) && open.dataset.zefPopover !== "manual") {
|
|
78
|
+
open.hidePopover();
|
|
79
|
+
opener.get(open)?.focus();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
for (const pop of popovers()) {
|
|
84
|
+
pop.addEventListener("toggle", (event) => {
|
|
85
|
+
if ((event as ToggleEvent).newState === "closed") {
|
|
86
|
+
opener.get(pop)?.setAttribute("aria-expanded", "false");
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
</script>
|
package/package.json
CHANGED