@hgdc/ui 0.12.0 → 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.
- package/dist/product-rail.d.ts +2 -1
- package/dist/product-rail.js +38 -11
- package/package.json +1 -1
- package/styles.css +5 -6
package/dist/product-rail.d.ts
CHANGED
|
@@ -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).
|
|
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. */
|
package/dist/product-rail.js
CHANGED
|
@@ -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).
|
|
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
|
-
|
|
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
|
|
28
|
-
//
|
|
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(() =>
|
|
49
|
+
timer = window.setTimeout(() => setHovered(true), PRODUCT_RAIL_OPEN_DELAY);
|
|
43
50
|
};
|
|
44
51
|
const onLeave = () => {
|
|
45
52
|
cancel();
|
|
46
|
-
|
|
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
|
-
|
|
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
|
-
|
|
70
|
+
setFocused(false);
|
|
56
71
|
};
|
|
57
72
|
const onKey = (event) => {
|
|
58
73
|
if (event.key !== "Escape")
|
|
59
74
|
return;
|
|
60
75
|
cancel();
|
|
61
|
-
|
|
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.
|
|
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",
|
package/styles.css
CHANGED
|
@@ -485,17 +485,16 @@
|
|
|
485
485
|
opacity: 0;
|
|
486
486
|
animation: family-loader-in var(--duration-base) var(--ease-out) var(--family-loader-delay, 200ms) forwards;
|
|
487
487
|
}
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
}
|
|
488
|
+
/* Le nom dans chaque raccourci : sans lui, lightningcss (minification de Vite) réduit
|
|
489
|
+
`animation: 1.8s infinite both` à `animation: none`. */
|
|
491
490
|
.family-loader [data-piece="left"] {
|
|
492
|
-
animation
|
|
491
|
+
animation: family-loader-left 1.8s infinite both;
|
|
493
492
|
}
|
|
494
493
|
.family-loader [data-piece="bar"] {
|
|
495
|
-
animation
|
|
494
|
+
animation: family-loader-bar 1.8s infinite both;
|
|
496
495
|
}
|
|
497
496
|
.family-loader [data-piece="right"] {
|
|
498
|
-
animation
|
|
497
|
+
animation: family-loader-right 1.8s infinite both;
|
|
499
498
|
}
|
|
500
499
|
@keyframes family-loader-in {
|
|
501
500
|
to {
|