@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.
@@ -1,12 +1,18 @@
1
1
  ---
2
2
  /**
3
- * Popover natif (attribut `popover`), sans JavaScript ni style imposé : ouvert par un bouton
4
- * `<button popovertarget="<id>">`, fermé au clic en dehors et par Échap. Placé sous son bouton
5
- * par le positionnement d'ancre CSS (`styles.css`), au centre de l'écran en repli.
6
- * Classe `zef-popover` ; `manual` : ne se ferme pas au clic en dehors.
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 id={id} popover={manual ? "manual" : "auto"} class:list={["zef-popover", className]} data-placement={placement}>
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agence-zef/astro",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Intégration Astro du CMS ZEF : routes, rendu des sections, aperçu, composants réutilisables sans style.",
5
5
  "license": "MIT",
6
6
  "type": "module",