@hgdc/ui 0.12.1 → 0.12.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.
@@ -8,7 +8,8 @@ export declare const PRODUCT_RAIL_OPEN_DELAY = 150;
8
8
  * d'intention) ou au focus clavier, elle s'élargit et écrit le nom de chaque produit. Elle ne
9
9
  * recouvre jamais la page : `AppShell` plie la barre latérale du produit le temps du survol, la
10
10
  * barre prend exactement la place libérée (248 px) ; si la barre latérale est déjà pliée, elle
11
- * pousse la page (220 px). Tout revient dès que la souris sort.
11
+ * pousse la page (220 px). Un menu ouvert depuis la barre (sélecteur d'organisation) la garde
12
+ * élargie ; tout revient quand la souris est sortie et que le menu est fermé.
12
13
  */
13
14
  export declare function ProductRail({ current, organization, accountHref, catalog, onExpandedChange, }: {
14
15
  /** Le mot du produit affiché (« host ») : marqué en teinte. */
@@ -15,17 +15,24 @@ function ProductLink({ entry }) {
15
15
  * d'intention) ou au focus clavier, elle s'élargit et écrit le nom de chaque produit. Elle ne
16
16
  * recouvre jamais la page : `AppShell` plie la barre latérale du produit le temps du survol, la
17
17
  * barre prend exactement la place libérée (248 px) ; si la barre latérale est déjà pliée, elle
18
- * pousse la page (220 px). Tout revient dès que la souris sort.
18
+ * pousse la page (220 px). Un menu ouvert depuis la barre (sélecteur d'organisation) la garde
19
+ * élargie ; tout revient quand la souris est sortie et que le menu est fermé.
19
20
  */
20
21
  export function ProductRail({ current, organization, accountHref, catalog, onExpandedChange, }) {
21
- const [expanded, setExpanded] = useState(false);
22
+ // Trois raisons de rester élargie : le survol (après le délai d'intention), le focus clavier, et
23
+ // un menu ouvert depuis la barre (sélecteur d'organisation ou tout autre) : la souris qui quitte
24
+ // la barre pour aller dans le menu ne doit pas la replier sous elle.
25
+ const [hovered, setHovered] = useState(false);
26
+ const [focused, setFocused] = useState(false);
27
+ const [menuOpen, setMenuOpen] = useState(false);
28
+ const expanded = hovered || focused || menuOpen;
22
29
  useEffect(() => {
23
30
  onExpandedChange?.(expanded);
24
31
  }, [expanded, onExpandedChange]);
25
32
  const ref = useRef(null);
26
33
  const { products, account } = productRailEntries({ current, accountHref, catalog });
27
- // Écouteurs natifs : la barre est un repère `nav`, pas un contrôle ; c'est son survol et le
28
- // focus de ses liens qui l'élargissent.
34
+ // Écouteurs natifs : la barre est un repère `nav`, pas un contrôle ; c'est son survol, le focus
35
+ // de ses liens et les menus qu'elle ouvre qui l'élargissent.
29
36
  useEffect(() => {
30
37
  const rail = ref.current;
31
38
  if (!rail)
@@ -39,27 +46,44 @@ export function ProductRail({ current, organization, accountHref, catalog, onExp
39
46
  if (event.pointerType !== "mouse")
40
47
  return;
41
48
  cancel();
42
- timer = window.setTimeout(() => setExpanded(true), PRODUCT_RAIL_OPEN_DELAY);
49
+ timer = window.setTimeout(() => setHovered(true), PRODUCT_RAIL_OPEN_DELAY);
43
50
  };
44
51
  const onLeave = () => {
45
52
  cancel();
46
- setExpanded(false);
53
+ setHovered(false);
54
+ };
55
+ // Au clavier seulement : un clic de souris ne laisse pas la barre ouverte, ni le focus rendu au
56
+ // déclencheur quand un menu ouvert à la souris se ferme. La dernière interaction fait foi.
57
+ let keyboard = false;
58
+ const onDocKey = () => {
59
+ keyboard = true;
60
+ };
61
+ const onDocPointer = () => {
62
+ keyboard = false;
47
63
  };
48
- // Au clavier seulement : un clic de souris ne laisse pas la barre ouverte.
49
64
  const onFocusIn = (event) => {
50
- if (event.target && focusVisible(event.target))
51
- setExpanded(true);
65
+ if (keyboard && event.target && focusVisible(event.target))
66
+ setFocused(true);
52
67
  };
53
68
  const onFocusOut = (event) => {
54
69
  if (!(event.relatedTarget instanceof Node) || !rail.contains(event.relatedTarget))
55
- setExpanded(false);
70
+ setFocused(false);
56
71
  };
57
72
  const onKey = (event) => {
58
73
  if (event.key !== "Escape")
59
74
  return;
60
75
  cancel();
61
- setExpanded(false);
76
+ setHovered(false);
77
+ setFocused(false);
62
78
  };
79
+ // Un menu ouvert depuis la barre : son déclencheur porte `aria-expanded="true"` (Base UI, Radix,
80
+ // natif). Le menu lui-même vit dans un portail, hors de la barre : on suit donc le déclencheur.
81
+ const syncMenu = () => setMenuOpen(rail.querySelector('[aria-expanded="true"]') !== null);
82
+ const observer = new MutationObserver(syncMenu);
83
+ observer.observe(rail, { subtree: true, attributes: true, attributeFilter: ["aria-expanded"], childList: true });
84
+ syncMenu();
85
+ document.addEventListener("keydown", onDocKey, true);
86
+ document.addEventListener("pointerdown", onDocPointer, true);
63
87
  rail.addEventListener("pointerenter", onEnter);
64
88
  rail.addEventListener("pointerleave", onLeave);
65
89
  rail.addEventListener("focusin", onFocusIn);
@@ -67,6 +91,9 @@ export function ProductRail({ current, organization, accountHref, catalog, onExp
67
91
  rail.addEventListener("keydown", onKey);
68
92
  return () => {
69
93
  cancel();
94
+ observer.disconnect();
95
+ document.removeEventListener("keydown", onDocKey, true);
96
+ document.removeEventListener("pointerdown", onDocPointer, true);
70
97
  rail.removeEventListener("pointerenter", onEnter);
71
98
  rail.removeEventListener("pointerleave", onLeave);
72
99
  rail.removeEventListener("focusin", onFocusIn);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hgdc/ui",
3
- "version": "0.12.1",
3
+ "version": "0.12.2",
4
4
  "description": "Interface de la H family : jetons de la DA, thèmes, polices et composants.",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://github.com/hugogebel-boop/hgdc-kit/tree/main/packages/ui#readme",