@hgdc/ui 0.63.1 → 0.64.0

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/styles.css CHANGED
@@ -19,11 +19,8 @@
19
19
 
20
20
  @custom-variant dark (&:is(.dark *));
21
21
  /* Barre latérale réduite en rail : `data-sidebar="rail"` sur <html>, posé avant la première
22
- peinture par `sidebarInitScript` (`@hgdc/ui/app-shell`), ou `data-rail-peek` sur la coque le
23
- temps que la barre des produits est élargie (ADR 0011). */
24
- @custom-variant rail (&:where([data-sidebar="rail"] *, [data-rail-peek] *));
25
- /* Pliée par la personne seulement (pas le temps du survol de la barre des produits). */
26
- @custom-variant folded (&:where([data-sidebar="rail"] *));
22
+ peinture par `sidebarInitScript` (`@hgdc/ui/app-shell`). */
23
+ @custom-variant rail (&:where([data-sidebar="rail"] *));
27
24
  /* Écran tactile, quelle que soit la largeur : `coarse:min-h-11` rend ses 44 px à une entrée que
28
25
  `lg:` avait resserrée pour la souris. Déclarée après les points de rupture, elle les emporte. */
29
26
  @custom-variant coarse (@media (pointer: coarse));
@@ -772,10 +769,9 @@
772
769
 
773
770
  /* Scène du chargement (ADR 0015) : la zone de contenu de la coque, calculée depuis l'écran, pour
774
771
  que le H soit au même pixel dans la coque, avant elle (rendu serveur, page d'entrée) et d'un
775
- produit à l'autre. Téléphone : sous l'en-tête de 56 px. Ordinateur : barre des produits (56 px),
776
- barre latérale (256 px, 64 en rail), marge de 8 px, en-tête de 56 px. */
772
+ produit à l'autre. Téléphone : sous l'en-tête de 56 px. Ordinateur : barre latérale
773
+ (256 px, 64 en rail), marge de 8 px, en-tête de 56 px. */
777
774
  :root {
778
- --hgdc-rail-w: 0px;
779
775
  --hgdc-content-left: 0px;
780
776
  --hgdc-stage-top: calc(env(safe-area-inset-top, 0px) + 3.5rem);
781
777
  --hgdc-stage-right: 0px;
@@ -784,9 +780,8 @@
784
780
  }
785
781
  @media (width >= 64rem) {
786
782
  :root {
787
- --hgdc-rail-w: 3.5rem;
788
783
  --hgdc-sidebar-w: 16rem;
789
- --hgdc-content-left: calc(var(--hgdc-rail-w) + var(--hgdc-sidebar-w));
784
+ --hgdc-content-left: var(--hgdc-sidebar-w);
790
785
  --hgdc-stage-top: 4rem;
791
786
  --hgdc-stage-right: 0.5rem;
792
787
  --hgdc-stage-bottom: 0.5rem;
@@ -795,9 +790,6 @@
795
790
  :root[data-sidebar="rail"] {
796
791
  --hgdc-sidebar-w: 4rem;
797
792
  }
798
- :root:has([data-slot="app-shell"]:not([data-product-rail])) {
799
- --hgdc-rail-w: 0px;
800
- }
801
793
  }
802
794
 
803
795
  /* Le chargeur du document (`FamilyLoaderRoot`) : un seul H par page, en position fixe sur la scène.
@@ -844,21 +836,17 @@
844
836
  :root:not(:has([data-slot="app-shell"])) [data-slot="family-loader-root"] > [data-frame] {
845
837
  display: block;
846
838
  }
847
- /* Sur ordinateur, le cadre est la coque elle-même (`ShellFrame`) : barre des produits complète,
848
- marque du produit, navigation en squelette, carte du contenu (ADR 0020). */
839
+ /* Sur ordinateur, le cadre est la coque elle-même (`ShellFrame`) : organisation retenue,
840
+ nom du module, navigation en squelette, carte du contenu (ADR 0020). */
849
841
  @media (width >= 64rem) {
850
842
  [data-slot="family-loader-root"] > [data-frame] {
851
843
  background: var(--shell);
852
844
  }
853
845
  }
854
- /* La marque du produit de l'adresse (`<html data-hgdc-product>`, `sidebarInitScript`), seule ; le
855
- produit courant marqué dans la barre, comme `aria-current` dans la coque. */
846
+ /* Le nom du module de l'adresse (`<html data-hgdc-product>`, `sidebarInitScript`), seul. */
856
847
  [data-slot="shell-frame"] [data-product] {
857
848
  display: none;
858
849
  }
859
- [data-slot="shell-frame-rail"] [data-product] {
860
- display: flex;
861
- }
862
850
  :root[data-hgdc-product="account"] [data-slot="shell-frame"] [data-product="account"],
863
851
  :root[data-hgdc-product="host"] [data-slot="shell-frame"] [data-product="host"],
864
852
  :root[data-hgdc-product="mail"] [data-slot="shell-frame"] [data-product="mail"],
@@ -871,20 +859,6 @@
871
859
  :root[data-hgdc-product="manager"] [data-slot="shell-frame"] [data-product="manager"] {
872
860
  display: contents;
873
861
  }
874
- :root[data-hgdc-product="account"] [data-slot="shell-frame-rail"] [data-product="account"],
875
- :root[data-hgdc-product="host"] [data-slot="shell-frame-rail"] [data-product="host"],
876
- :root[data-hgdc-product="mail"] [data-slot="shell-frame-rail"] [data-product="mail"],
877
- :root[data-hgdc-product="portal"] [data-slot="shell-frame-rail"] [data-product="portal"],
878
- :root[data-hgdc-product="status"] [data-slot="shell-frame-rail"] [data-product="status"],
879
- :root[data-hgdc-product="analytics"] [data-slot="shell-frame-rail"] [data-product="analytics"],
880
- :root[data-hgdc-product="docs"] [data-slot="shell-frame-rail"] [data-product="docs"],
881
- :root[data-hgdc-product="iassistant"] [data-slot="shell-frame-rail"] [data-product="iassistant"],
882
- :root[data-hgdc-product="id"] [data-slot="shell-frame-rail"] [data-product="id"],
883
- :root[data-hgdc-product="manager"] [data-slot="shell-frame-rail"] [data-product="manager"] {
884
- display: flex;
885
- background: var(--shell-active);
886
- color: var(--shell-foreground);
887
- }
888
862
  /* Navigation en squelette, à la hauteur retenue pour ce produit (`--hgdc-nav-height`). */
889
863
  [data-slot="shell-frame-nav"] {
890
864
  max-height: var(--hgdc-nav-height, 7.5rem);
@@ -922,7 +896,7 @@
922
896
  }
923
897
 
924
898
  /* Passage d'un produit à l'autre sous la même origine (`app.hgdc.ch/<produit>`, ADR 0020) : une
925
- transition de vue entre documents. La barre des produits, la barre latérale et la barre du haut
899
+ transition de vue entre documents. La barre latérale et la barre du haut
926
900
  gardent leur place (même nom des deux côtés : elles ne bougent pas, leur contenu passe en
927
901
  fondu) ; le reste de la page passe en fondu court. Le navigateur garde l'ancienne page à l'écran
928
902
  jusqu'à ce que la nouvelle soit prête : pas de page blanche. Chrome, Edge et Safari 18.2+ ;
@@ -934,9 +908,6 @@
934
908
  navigation: auto;
935
909
  }
936
910
  }
937
- [data-slot="product-rail"] {
938
- view-transition-name: hgdc-product-rail;
939
- }
940
911
  [data-slot="app-shell-sidebar"] {
941
912
  view-transition-name: hgdc-sidebar;
942
913
  }
@@ -946,9 +917,6 @@
946
917
  /* Le cadre du chargeur (hors de la coque) et le H lui-même portent les mêmes noms, seulement quand
947
918
  ils sont à l'écran : mêmes conditions que l'affichage du chargeur, plus haut. */
948
919
  :root:has([data-slot="page-pending"]):not(:has([data-slot="app-shell"])) {
949
- & [data-slot="shell-frame-rail"] {
950
- view-transition-name: hgdc-product-rail;
951
- }
952
920
  & [data-slot="shell-frame-sidebar"] {
953
921
  view-transition-name: hgdc-sidebar;
954
922
  }
@@ -963,12 +931,6 @@
963
931
  view-transition-name: hgdc-loader;
964
932
  }
965
933
  }
966
- /* Un clic dans la barre des produits : la barre se replie et la barre latérale revient d'un coup,
967
- pour que l'image de départ soit déjà celle de l'arrivée (`ProductRail`). */
968
- :root[data-hgdc-switching] [data-slot="product-rail"],
969
- :root[data-hgdc-switching] [data-slot="app-shell-sidebar"] {
970
- transition: none;
971
- }
972
934
  ::view-transition-old(root),
973
935
  ::view-transition-new(root) {
974
936
  animation-duration: var(--duration-fast);
@@ -985,27 +947,21 @@
985
947
  animation-duration: var(--duration-base);
986
948
  animation-timing-function: var(--ease-ios);
987
949
  }
988
- /* Barre des produits et H : identiques des deux côtés (la barre ne change que d'indicateur, le H
989
- est à la même phase, calé sur l'horloge). Ni fondu ni mouvement : l'ancienne image est masquée,
990
- la nouvelle, vivante, s'affiche telle quelle. Présents d'un seul côté (une page sans coque,
991
- une page prête sans H), ils apparaissent ou s'effacent en fondu court. */
992
- ::view-transition-group(hgdc-product-rail),
950
+ /* Le H : identique des deux côtés, à la même phase, calé sur l'horloge. Ni fondu ni mouvement :
951
+ l'ancienne image est masquée, la nouvelle, vivante, s'affiche telle quelle. Présent d'un seul côté
952
+ (une page prête sans H), il apparaît ou s'efface en fondu court. */
993
953
  ::view-transition-group(hgdc-loader),
994
- ::view-transition-new(hgdc-product-rail),
995
954
  ::view-transition-new(hgdc-loader) {
996
955
  animation: none;
997
956
  }
998
- ::view-transition-old(hgdc-product-rail),
999
957
  ::view-transition-old(hgdc-loader) {
1000
958
  animation: none;
1001
959
  opacity: 0;
1002
960
  }
1003
- ::view-transition-old(hgdc-product-rail):only-child,
1004
961
  ::view-transition-old(hgdc-loader):only-child {
1005
962
  opacity: 1;
1006
963
  animation: hgdc-view-out var(--duration-fast) var(--ease-out) both;
1007
964
  }
1008
- ::view-transition-new(hgdc-product-rail):only-child,
1009
965
  ::view-transition-new(hgdc-loader):only-child {
1010
966
  animation: hgdc-view-in var(--duration-fast) var(--ease-out) both;
1011
967
  }
@@ -1,41 +0,0 @@
1
- import { type ReactNode } from "react";
2
- import { type HgdcCatalogProduct } from "./products.ts";
3
- /** Délai d'intention avant d'élargir la barre au survol : un passage de souris ne l'ouvre pas. */
4
- export declare const PRODUCT_RAIL_OPEN_DELAY = 150;
5
- /**
6
- * La place de l'organisation tant que le produit ne l'a pas (ses données arrivent) : la marque de
7
- * la dernière organisation affichée sous cette origine, au pixel près de celle d'`OrgSwitcher`
8
- * (initiales ou logo, posés sur `<html>` par `sidebarInitScript` avant la première peinture),
9
- * sinon un carré de squelette de la même taille. Rien ne bouge quand la vraie arrive.
10
- */
11
- export declare function OrgPlaceholder(): React.JSX.Element;
12
- /**
13
- * La barre des produits du cadre de chargement (`FamilyLoaderRoot`, hors de la coque) : la même
14
- * colonne que `ProductRail` repliée, `OrgPlaceholder` en haut, les mêmes pastilles, sans liens. Le
15
- * produit courant est marqué par le CSS (`<html data-hgdc-product>`, posé par
16
- * `sidebarInitScript`) : au rendu serveur, le kit ne connaît pas l'adresse.
17
- */
18
- export declare function ProductRailFrame(): React.JSX.Element;
19
- /**
20
- * La barre des produits de la famille, tout à gauche de la coque sur ordinateur : organisation en
21
- * haut, Account, puis les pastilles des produits. 56 px de large ; au survol (après un court délai
22
- * d'intention) ou au focus clavier, elle s'élargit et écrit le nom de chaque produit. Elle ne
23
- * recouvre jamais la page : `AppShell` plie la barre latérale du produit le temps du survol, la
24
- * barre prend exactement la place libérée (248 px) ; si la barre latérale est déjà pliée, elle
25
- * pousse la page (220 px). Un menu ouvert depuis la barre (sélecteur d'organisation) la garde
26
- * élargie ; tout revient quand la souris est sortie et que le menu est fermé.
27
- */
28
- export declare function ProductRail({ current, organization, accountHref, catalog, onExpandedChange, }: {
29
- /** Le mot du produit affiché (« host ») : marqué en teinte. */
30
- current?: string | undefined;
31
- /**
32
- * `OrgSwitcher`, rendu compact. Absent (`undefined` : les données du produit arrivent), sa
33
- * place est tenue par `OrgPlaceholder`. `null` : pas d'organisation, rien en haut.
34
- */
35
- organization?: ReactNode;
36
- /** Adresse d'Account (un Account local en développement). */
37
- accountHref?: string | undefined;
38
- catalog?: ReadonlyArray<HgdcCatalogProduct> | undefined;
39
- /** Prévenu à chaque ouverture et fermeture : `AppShell` y plie sa barre latérale. */
40
- onExpandedChange?: ((expanded: boolean) => void) | undefined;
41
- }): React.JSX.Element;
@@ -1,183 +0,0 @@
1
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
3
- import { ProductBadge } from "./product-badge.js";
4
- import { PRODUCT_PRERENDER_ATTRIBUTE } from "./product-switch.js";
5
- import { productRailEntries } from "./products.js";
6
- import { markProductSwitch, takeProductSwitch } from "./shell-memory.js";
7
- import { ShellPlacementContext, useLeaveOnClick } from "./shell-placement.js";
8
- import { cn } from "./cn.js";
9
- const useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
10
- const noSubscription = () => () => { };
11
- /** `location.origin` après l'hydratation, `undefined` au rendu serveur (liens absolus d'abord). */
12
- function usePageOrigin() {
13
- return useSyncExternalStore(noSubscription, () => window.location.origin, () => undefined);
14
- }
15
- /** Délai d'intention avant d'élargir la barre au survol : un passage de souris ne l'ouvre pas. */
16
- export const PRODUCT_RAIL_OPEN_DELAY = 150;
17
- /** Boîte d'une entrée de la barre : la même pour un lien et pour sa place dans le cadre de chargement. */
18
- const PRODUCT_ITEM = "flex h-10 items-center gap-3 rounded-xl px-2 text-shell-muted coarse:h-11";
19
- /** Colonne de la barre (56 px) et son contenu : les mêmes pour la barre et pour le cadre de chargement. */
20
- const RAIL_COLUMN = "hidden w-14 shrink-0 overflow-hidden lg:block";
21
- const RAIL_INNER = "group/rail flex h-full w-full flex-col gap-2 overflow-hidden bg-shell-rail px-2 py-3 text-shell-foreground";
22
- const RAIL_LIST = "grid min-h-0 gap-1 overflow-x-hidden overflow-y-auto";
23
- function ProductLabel({ entry }) {
24
- return (_jsxs(_Fragment, { children: [_jsx(ProductBadge, { product: entry.badge, className: "size-6" }), _jsxs("span", { className: "min-w-0 flex-1 truncate opacity-0 transition-opacity duration-(--duration-fast) ease-out group-data-expanded/rail:opacity-100 motion-reduce:transition-none", children: [_jsx("span", { className: "font-heading text-sm font-medium", children: entry.name }), entry.hint ? _jsx("span", { className: "sr-only", children: ", " }) : null, entry.hint ? _jsx("span", { className: "ml-1.5 text-xs text-shell-muted", children: entry.hint }) : null] })] }));
25
- }
26
- function ProductLink({ entry }) {
27
- // Chaque produit s'ouvre en document complet : la coque montre le H dès le clic.
28
- const leaveOnClick = useLeaveOnClick();
29
- return (_jsx("li", { children: _jsx("a", { href: entry.href, onClick: leaveOnClick, ...(entry.sameOrigin ? { [PRODUCT_PRERENDER_ATTRIBUTE]: "" } : {}), "aria-current": entry.current ? "page" : undefined, className: cn(PRODUCT_ITEM, "outline-none transition-colors duration-(--duration-fast) ease-out", "hover:bg-shell-hover hover:text-shell-foreground focus-visible:ring-2 focus-visible:ring-ring/50", "aria-[current=page]:bg-shell-active aria-[current=page]:text-shell-foreground"), children: _jsx(ProductLabel, { entry: entry }) }) }));
30
- }
31
- /**
32
- * La place de l'organisation tant que le produit ne l'a pas (ses données arrivent) : la marque de
33
- * la dernière organisation affichée sous cette origine, au pixel près de celle d'`OrgSwitcher`
34
- * (initiales ou logo, posés sur `<html>` par `sidebarInitScript` avant la première peinture),
35
- * sinon un carré de squelette de la même taille. Rien ne bouge quand la vraie arrive.
36
- */
37
- export function OrgPlaceholder() {
38
- return (_jsx("div", { "data-slot": "org-placeholder", "aria-hidden": true, className: "flex h-10 w-full shrink-0 items-center px-1 coarse:h-11", children: _jsx("span", { "data-slot": "org-placeholder-mark", className: "grid size-8 shrink-0 place-items-center rounded-lg text-xs font-semibold uppercase" }) }));
39
- }
40
- function RailDivider() {
41
- return _jsx("div", { "aria-hidden": true, className: "mx-2 h-px shrink-0 bg-foreground/8" });
42
- }
43
- /**
44
- * La barre des produits du cadre de chargement (`FamilyLoaderRoot`, hors de la coque) : la même
45
- * colonne que `ProductRail` repliée, `OrgPlaceholder` en haut, les mêmes pastilles, sans liens. Le
46
- * produit courant est marqué par le CSS (`<html data-hgdc-product>`, posé par
47
- * `sidebarInitScript`) : au rendu serveur, le kit ne connaît pas l'adresse.
48
- */
49
- export function ProductRailFrame() {
50
- const { products, account } = productRailEntries();
51
- return (_jsx("div", { "data-slot": "shell-frame-rail", className: RAIL_COLUMN, children: _jsxs("div", { className: RAIL_INNER, children: [_jsx(OrgPlaceholder, {}), _jsx(RailDivider, {}), _jsx("ul", { className: RAIL_LIST, children: [account, ...products].map((entry) => (_jsx("li", { children: _jsx("span", { "data-product": entry.badge, className: PRODUCT_ITEM, children: _jsx(ProductLabel, { entry: entry }) }) }, entry.badge))) })] }) }));
52
- }
53
- /**
54
- * La barre des produits de la famille, tout à gauche de la coque sur ordinateur : organisation en
55
- * haut, Account, puis les pastilles des produits. 56 px de large ; au survol (après un court délai
56
- * d'intention) ou au focus clavier, elle s'élargit et écrit le nom de chaque produit. Elle ne
57
- * recouvre jamais la page : `AppShell` plie la barre latérale du produit le temps du survol, la
58
- * barre prend exactement la place libérée (248 px) ; si la barre latérale est déjà pliée, elle
59
- * pousse la page (220 px). Un menu ouvert depuis la barre (sélecteur d'organisation) la garde
60
- * élargie ; tout revient quand la souris est sortie et que le menu est fermé.
61
- */
62
- export function ProductRail({ current, organization, accountHref, catalog, onExpandedChange, }) {
63
- // Trois raisons de rester élargie : le survol (après le délai d'intention), le focus clavier, et
64
- // un menu ouvert depuis la barre (sélecteur d'organisation ou tout autre) : la souris qui quitte
65
- // la barre pour aller dans le menu ne doit pas la replier sous elle.
66
- const [hovered, setHovered] = useState(false);
67
- const [focused, setFocused] = useState(false);
68
- const [menuOpen, setMenuOpen] = useState(false);
69
- const expanded = hovered || focused || menuOpen;
70
- // Avant la peinture : la barre latérale se plie dans la même image que la barre s'élargit, les
71
- // deux transitions partent ensemble et la page ne bouge pas.
72
- useIsomorphicLayoutEffect(() => {
73
- onExpandedChange?.(expanded);
74
- }, [expanded, onExpandedChange]);
75
- const ref = useRef(null);
76
- const pageOrigin = usePageOrigin();
77
- const { products, account } = productRailEntries({ current, accountHref, catalog, pageOrigin });
78
- // Écouteurs natifs : la barre est un repère `nav`, pas un contrôle ; c'est son survol, le focus
79
- // de ses liens et les menus qu'elle ouvre qui l'élargissent.
80
- useEffect(() => {
81
- const rail = ref.current;
82
- if (!rail)
83
- return;
84
- let timer;
85
- // Arrivée depuis la barre d'un autre produit, la souris restée dessus : la barre ne s'élargit
86
- // pas sous elle, il faut d'abord en sortir (ADR 0020).
87
- let arrivedUnderPointer = takeProductSwitch();
88
- const cancel = () => {
89
- window.clearTimeout(timer);
90
- timer = undefined;
91
- };
92
- const onEnter = (event) => {
93
- if (event.pointerType !== "mouse" || arrivedUnderPointer)
94
- return;
95
- cancel();
96
- timer = window.setTimeout(() => setHovered(true), PRODUCT_RAIL_OPEN_DELAY);
97
- };
98
- const onLeave = () => {
99
- arrivedUnderPointer = false;
100
- cancel();
101
- setHovered(false);
102
- };
103
- // Au clavier seulement : un clic de souris ne laisse pas la barre ouverte, ni le focus rendu au
104
- // déclencheur quand un menu ouvert à la souris se ferme. La dernière interaction fait foi.
105
- let keyboard = false;
106
- const onDocKey = () => {
107
- keyboard = true;
108
- };
109
- const onDocPointer = () => {
110
- keyboard = false;
111
- };
112
- const onFocusIn = (event) => {
113
- if (keyboard && event.target && focusVisible(event.target))
114
- setFocused(true);
115
- };
116
- const onFocusOut = (event) => {
117
- if (!(event.relatedTarget instanceof Node) || !rail.contains(event.relatedTarget))
118
- setFocused(false);
119
- };
120
- // Un produit choisi : la barre se replie comme un menu qui se ferme, pendant que l'autre produit
121
- // s'ouvre. La page d'arrivée la montre repliée : rien ne bouge au passage (ADR 0020).
122
- const onClick = (event) => {
123
- if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)
124
- return;
125
- if (!(event.target instanceof Element) || !event.target.closest("a[href]"))
126
- return;
127
- // Repliée d'un coup, sans transition (`data-hgdc-switching`) : l'image de départ de la
128
- // transition de vue montre déjà la barre telle que la page d'arrivée la peint.
129
- document.documentElement.dataset.hgdcSwitching = "";
130
- markProductSwitch();
131
- cancel();
132
- setHovered(false);
133
- setFocused(false);
134
- };
135
- const onKey = (event) => {
136
- if (event.key !== "Escape")
137
- return;
138
- cancel();
139
- setHovered(false);
140
- setFocused(false);
141
- };
142
- // Un menu ouvert depuis la barre : son déclencheur porte `aria-expanded="true"` (Base UI, Radix,
143
- // natif). Le menu lui-même vit dans un portail, hors de la barre : on suit donc le déclencheur.
144
- const syncMenu = () => setMenuOpen(rail.querySelector('[aria-expanded="true"]') !== null);
145
- const observer = new MutationObserver(syncMenu);
146
- observer.observe(rail, { subtree: true, attributes: true, attributeFilter: ["aria-expanded"], childList: true });
147
- syncMenu();
148
- document.addEventListener("keydown", onDocKey, true);
149
- document.addEventListener("pointerdown", onDocPointer, true);
150
- rail.addEventListener("pointerenter", onEnter);
151
- rail.addEventListener("pointerleave", onLeave);
152
- rail.addEventListener("focusin", onFocusIn);
153
- rail.addEventListener("focusout", onFocusOut);
154
- rail.addEventListener("keydown", onKey);
155
- rail.addEventListener("click", onClick);
156
- return () => {
157
- cancel();
158
- observer.disconnect();
159
- document.removeEventListener("keydown", onDocKey, true);
160
- document.removeEventListener("pointerdown", onDocPointer, true);
161
- rail.removeEventListener("pointerenter", onEnter);
162
- rail.removeEventListener("pointerleave", onLeave);
163
- rail.removeEventListener("focusin", onFocusIn);
164
- rail.removeEventListener("focusout", onFocusOut);
165
- rail.removeEventListener("keydown", onKey);
166
- rail.removeEventListener("click", onClick);
167
- };
168
- }, []);
169
- return (_jsx("div", { "data-slot": "product-rail", "data-expanded": expanded ? "" : undefined, className: cn(RAIL_COLUMN,
170
- // Même durée et même courbe que la barre latérale qui se plie : la somme des deux largeurs
171
- // reste constante (56 + 256 = 248 + 64), la page ne bouge pas.
172
- "transition-[width] duration-(--duration-base) ease-ios motion-reduce:transition-none", "data-expanded:w-62 folded:data-expanded:w-55"), children: _jsxs("nav", { ref: ref, "aria-label": "Produits", "data-expanded": expanded ? "" : undefined, className: RAIL_INNER, children: [organization === undefined ? (_jsxs(_Fragment, { children: [_jsx(OrgPlaceholder, {}), _jsx(RailDivider, {})] })) : organization ? (_jsxs(_Fragment, { children: [_jsx(ShellPlacementContext, { value: "product-rail", children: organization }), _jsx(RailDivider, {})] })) : null, _jsxs("ul", { className: RAIL_LIST, children: [_jsx(ProductLink, { entry: account }), products.map((entry) => (_jsx(ProductLink, { entry: entry }, entry.badge)))] })] }) }));
173
- }
174
- function focusVisible(target) {
175
- if (!(target instanceof Element))
176
- return false;
177
- try {
178
- return target.matches(":focus-visible");
179
- }
180
- catch {
181
- return true;
182
- }
183
- }