@hgdc/ui 0.63.0 → 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/README.md +3 -3
- package/dist/account-menu.d.ts +4 -4
- package/dist/account-menu.js +26 -8
- package/dist/app-shell.d.ts +13 -14
- package/dist/app-shell.js +23 -15
- package/dist/email.js +1 -1
- package/dist/family-svg.js +2 -2
- package/dist/module-bar-store.d.ts +9 -0
- package/dist/module-bar-store.js +56 -0
- package/dist/module-bar.d.ts +16 -0
- package/dist/module-bar.js +91 -0
- package/dist/modules.d.ts +31 -0
- package/dist/modules.js +47 -0
- package/dist/org-placeholder.d.ts +7 -0
- package/dist/org-placeholder.js +10 -0
- package/dist/org-switcher.d.ts +2 -4
- package/dist/org-switcher.js +3 -15
- package/dist/product-name.d.ts +3 -0
- package/dist/product-name.js +7 -0
- package/dist/products.d.ts +5 -5
- package/dist/products.js +10 -10
- package/dist/shell-frame.d.ts +6 -9
- package/dist/shell-frame.js +11 -15
- package/dist/shell-links.d.ts +4 -1
- package/dist/shell-links.js +12 -5
- package/dist/shell-memory.d.ts +0 -8
- package/dist/shell-memory.js +9 -36
- package/dist/shell-placement.d.ts +17 -11
- package/dist/shell-placement.js +9 -9
- package/dist/sidebar-head.d.ts +9 -1
- package/dist/sidebar-head.js +9 -1
- package/dist/sidebar-nav.d.ts +5 -1
- package/dist/sidebar-nav.js +5 -3
- package/dist/use-product-leave.js +0 -1
- package/email-icons/host.png +0 -0
- package/family/app/host/apple-touch-icon.png +0 -0
- package/family/app/host/favicon.ico +0 -0
- package/family/app/host/favicon.svg +1 -1
- package/family/app/host/icon-192.png +0 -0
- package/family/app/host/icon-512.png +0 -0
- package/family/app/host/icon-maskable-512.png +0 -0
- package/family/host-dark.svg +1 -1
- package/family/host.svg +1 -1
- package/family/tile/host.svg +1 -1
- package/package.json +1 -1
- package/styles.css +12 -56
- package/dist/product-rail.d.ts +0 -41
- package/dist/product-rail.js +0 -183
package/README.md
CHANGED
|
@@ -70,8 +70,8 @@ Entre deux produits servis sous la même origine, la coque fait comme une seule
|
|
|
70
70
|
|
|
71
71
|
Pendant le passage (0.39) :
|
|
72
72
|
|
|
73
|
-
- **Barre
|
|
74
|
-
- **Barre latérale** :
|
|
73
|
+
- **Barre latérale, organisation** : la page d'arrivée la peint identique dès sa première image, même quand ses données ne sont pas là : `OrgPlaceholder` tient la place de l'organisation (`organization` absent) avec la marque de la dernière organisation affichée sous l'origine (initiales ou logo, retenus par `OrgSwitcher`, oubliés à la déconnexion par `AccountMenu`), sinon un squelette de la même taille.
|
|
74
|
+
- **Barre latérale, contenu** : le nom du module et sa navigation passent en fondu de 200 ms. Une page qui attend hors de la coque (`PagePending` sous `FamilyLoaderRoot`) dessine la coque entière : organisation retenue, Manager, retour aux modules, nom du module de l'adresse, navigation en squelette à la hauteur retenue pour ce module, barre du haut.
|
|
75
75
|
- **H** : un seul nom de transition (`hgdc-loader`), sans fondu, calé sur l'horloge : celui de l'arrivée reprend à la même phase.
|
|
76
76
|
- **Contenu** : fondu de 150 ms.
|
|
77
77
|
|
|
@@ -112,7 +112,7 @@ Couleurs pleines ; pour éclaircir, mélanger au blanc `color-mix(in oklab, …)
|
|
|
112
112
|
- produit : `"build": "hgdc-brand sync mail && vite build"` (et en tête de `dev`), les six fichiers de `family/app/<mot>/` dans `public/`, listés dans le `.gitignore` ; `--email-as <fichier>` y ajoute l'icône d'e-mail du produit sous un ancien nom ;
|
|
113
113
|
- site hgdc.ch : `hgdc-brand emails public/email`, les `<mot>.png` servis à `EMAIL_ICON_BASE`.
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
Une seule barre latérale (ADR 0024) : organisation et bouton Réduire en haut, lien Manager, puis les onglets du module ouvert sous « Retour aux modules ». Le retour affiche la liste des modules, sans recharger la page ; « Personnaliser la barre » permet de les ranger, masquer et ajouter (`localStorage`, `hgdc-shell:modules`). Réglages reste au-dessus du compte ; Documentation, État du service et l'autre vue sont dans `AccountMenu`. `productRail={false}` ou une vue seule retire cette barre : marque du produit en haut, organisation dessous, les trois accès au-dessus du compte, « Produits » dans le menu du compte. `accountHref` pointe vers un Manager local. La barre se réduit en rail (`Ctrl+B`, mémorisé) : poser `sidebarInitScript` dans le `<head>`. Le kit ne connaît ni routeur ni authentification : liens par `render={<Link />}`, actions par callbacks. Rôles d'organisation : `owner`, `admin`, `member` (Propriétaire, Admin, Membre).
|
|
116
116
|
|
|
117
117
|
## Contenu et données
|
|
118
118
|
|
package/dist/account-menu.d.ts
CHANGED
|
@@ -27,11 +27,11 @@ export type AccountUser = {
|
|
|
27
27
|
* l'avatar seul, pour une barre du haut. Le produit passe les actions ; le thème passe par
|
|
28
28
|
* `ThemeProvider`.
|
|
29
29
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
30
|
+
* Dans la barre de Basalte, ce menu porte aussi Documentation, État du service et l'autre vue
|
|
31
|
+
* (`links` d'`AppShell`), et plus la liste des produits : la barre la montre. L'aide des raccourcis
|
|
32
|
+
* garde sa touche `?`, sans entrée de menu.
|
|
32
33
|
*
|
|
33
|
-
*
|
|
34
|
-
* sont déjà ; gardée sur téléphone) liste par défaut Account et les produits ouverts du catalogue
|
|
34
|
+
* Dans une vue seule, « Produits » liste Manager et les modules ouverts du catalogue
|
|
35
35
|
* (`productMenuEntries`, `@hgdc/ui/products`), avec `product` coché. `products` remplace cette
|
|
36
36
|
* liste ; `products={[]}` la retire.
|
|
37
37
|
*/
|
package/dist/account-menu.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import Activity01Icon from "@hugeicons/core-free-icons/Activity01Icon";
|
|
3
|
+
import BookOpen01Icon from "@hugeicons/core-free-icons/BookOpen01Icon";
|
|
2
4
|
import DashboardSquare01Icon from "@hugeicons/core-free-icons/DashboardSquare01Icon";
|
|
5
|
+
import LinkSquare02Icon from "@hugeicons/core-free-icons/LinkSquare02Icon";
|
|
3
6
|
import Logout01Icon from "@hugeicons/core-free-icons/Logout01Icon";
|
|
4
7
|
import SecurityLockIcon from "@hugeicons/core-free-icons/SecurityLockIcon";
|
|
5
8
|
import Tick02Icon from "@hugeicons/core-free-icons/Tick02Icon";
|
|
@@ -13,7 +16,7 @@ import { MENU_LINK_ITEM } from "./org-switcher.js";
|
|
|
13
16
|
import { ProductBadge } from "./product-badge.js";
|
|
14
17
|
import { productMenuEntries } from "./products.js";
|
|
15
18
|
import { forgetShellOrganization } from "./shell-memory.js";
|
|
16
|
-
import { useLeaveOnClick,
|
|
19
|
+
import { useLeaveOnClick, useModuleBarShown, useShellMenuLinks } from "./shell-placement.js";
|
|
17
20
|
import { ThemeSwitch } from "./theme-switch.js";
|
|
18
21
|
/** L'avatar du compte : la photo, sinon les initiales sur la teinte stable de l'adresse. */
|
|
19
22
|
function AccountAvatar({ user, size }) {
|
|
@@ -27,24 +30,39 @@ function AccountAvatar({ user, size }) {
|
|
|
27
30
|
* l'avatar seul, pour une barre du haut. Le produit passe les actions ; le thème passe par
|
|
28
31
|
* `ThemeProvider`.
|
|
29
32
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
33
|
+
* Dans la barre de Basalte, ce menu porte aussi Documentation, État du service et l'autre vue
|
|
34
|
+
* (`links` d'`AppShell`), et plus la liste des produits : la barre la montre. L'aide des raccourcis
|
|
35
|
+
* garde sa touche `?`, sans entrée de menu.
|
|
32
36
|
*
|
|
33
|
-
*
|
|
34
|
-
* sont déjà ; gardée sur téléphone) liste par défaut Account et les produits ouverts du catalogue
|
|
37
|
+
* Dans une vue seule, « Produits » liste Manager et les modules ouverts du catalogue
|
|
35
38
|
* (`productMenuEntries`, `@hgdc/ui/products`), avec `product` coché. `products` remplace cette
|
|
36
39
|
* liste ; `products={[]}` la retire.
|
|
37
40
|
*/
|
|
38
41
|
export function AccountMenu({ user, onProfile, onSecurity, onSignOut, products: productsProp, product: currentProduct, accountHref, children, variant = "row", }) {
|
|
39
|
-
const
|
|
40
|
-
|
|
42
|
+
const moduleBar = useModuleBarShown();
|
|
43
|
+
// La barre de Basalte montre déjà Manager et les modules, au téléphone comme à l'ordinateur.
|
|
44
|
+
const products = moduleBar ? [] : (productsProp ?? productMenuEntries({ current: currentProduct, accountHref }));
|
|
45
|
+
const menuLinks = useShellMenuLinks();
|
|
41
46
|
const leaveOnClick = useLeaveOnClick();
|
|
42
|
-
return (_jsxs(DropdownMenu, { children: [_jsxs(DropdownMenuTrigger, { render: variant === "row" ? (_jsx("button", { type: "button", "aria-label": `Compte de ${user.name}`, className: "flex w-full min-w-0 items-center gap-2.5 rounded-xl p-1.5 text-left outline-none transition-colors duration-(--duration-fast) ease-out hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring/50 aria-expanded:bg-muted lg:hover:bg-shell-hover lg:aria-expanded:bg-shell-hover lg:rail:justify-center" })) : (_jsx("button", { type: "button", "aria-label": `Compte de ${user.name}`, className: "touch-target grid size-9 place-items-center rounded-full outline-none focus-visible:ring-2 focus-visible:ring-ring/50" })), children: [_jsx(AccountAvatar, { user: user }), variant === "row" ? (_jsxs("span", { className: "min-w-0 flex-1 lg:rail:hidden", children: [_jsx("span", { className: "block truncate text-sm font-medium", children: user.name }), _jsx("span", { className: "block truncate text-xs text-muted-foreground lg:text-shell-muted", children: user.email })] })) : null] }), _jsxs(DropdownMenuContent, { align: variant === "row" ? "start" : "end", side: variant === "row" ? "top" : "bottom", className: "w-72", children: [_jsx(DropdownMenuGroup, { children: _jsxs(DropdownMenuLabel, { className: "flex items-center gap-2.5 py-2", children: [_jsx(AccountAvatar, { user: user }), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "block truncate text-sm font-medium text-foreground", children: user.name }), _jsx("span", { className: "block truncate", children: user.email })] })] }) }), _jsx(DropdownMenuSeparator, {}), onProfile ? (_jsxs(DropdownMenuItem, { onClick: onProfile, children: [_jsx(HugeiconsIcon, { icon: UserIcon, "aria-hidden": true }), "Profil"] })) : null, onSecurity ? (_jsxs(DropdownMenuItem, { onClick: onSecurity, children: [_jsx(HugeiconsIcon, { icon: SecurityLockIcon, "aria-hidden": true }), "S\u00E9curit\u00E9"] })) : null, _jsx(ThemeSwitch, {}), children ? (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), children] })) : null, products.length ? (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {
|
|
47
|
+
return (_jsxs(DropdownMenu, { children: [_jsxs(DropdownMenuTrigger, { render: variant === "row" ? (_jsx("button", { type: "button", "aria-label": `Compte de ${user.name}`, className: "flex w-full min-w-0 items-center gap-2.5 rounded-xl p-1.5 text-left outline-none transition-colors duration-(--duration-fast) ease-out hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring/50 aria-expanded:bg-muted lg:hover:bg-shell-hover lg:aria-expanded:bg-shell-hover lg:rail:justify-center" })) : (_jsx("button", { type: "button", "aria-label": `Compte de ${user.name}`, className: "touch-target grid size-9 place-items-center rounded-full outline-none focus-visible:ring-2 focus-visible:ring-ring/50" })), children: [_jsx(AccountAvatar, { user: user }), variant === "row" ? (_jsxs("span", { className: "min-w-0 flex-1 lg:rail:hidden", children: [_jsx("span", { className: "block truncate text-sm font-medium", children: user.name }), _jsx("span", { className: "block truncate text-xs text-muted-foreground lg:text-shell-muted", children: user.email })] })) : null] }), _jsxs(DropdownMenuContent, { align: variant === "row" ? "start" : "end", side: variant === "row" ? "top" : "bottom", className: "w-72", children: [_jsx(DropdownMenuGroup, { children: _jsxs(DropdownMenuLabel, { className: "flex items-center gap-2.5 py-2", children: [_jsx(AccountAvatar, { user: user }), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "block truncate text-sm font-medium text-foreground", children: user.name }), _jsx("span", { className: "block truncate", children: user.email })] })] }) }), _jsx(DropdownMenuSeparator, {}), onProfile ? (_jsxs(DropdownMenuItem, { onClick: onProfile, children: [_jsx(HugeiconsIcon, { icon: UserIcon, "aria-hidden": true }), "Profil"] })) : null, onSecurity ? (_jsxs(DropdownMenuItem, { onClick: onSecurity, children: [_jsx(HugeiconsIcon, { icon: SecurityLockIcon, "aria-hidden": true }), "S\u00E9curit\u00E9"] })) : null, _jsx(ThemeSwitch, {}), children ? (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), children] })) : null, _jsx(AccountMenuLinks, { links: menuLinks }), products.length ? (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuGroup, { children: [_jsx(DropdownMenuLabel, { children: "Produits" }), products.map((product) => product.current ? (_jsxs(DropdownMenuItem, { disabled: true, className: "data-disabled:opacity-100", children: [_jsx(ProductMenuIcon, { product: product }), _jsx("span", { className: "flex-1", children: product.name }), _jsx(HugeiconsIcon, { icon: Tick02Icon, "aria-label": "Produit ouvert" })] }, product.name)) : (_jsxs(MenuPrimitive.LinkItem, { href: product.href, onClick: leaveOnClick, className: MENU_LINK_ITEM, children: [_jsx(ProductMenuIcon, { product: product }), _jsx("span", { className: "flex-1", children: product.name }), product.hint ? (_jsx("span", { className: "text-xs font-normal text-muted-foreground", children: product.hint })) : null] }, product.name)))] })] })) : null, _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onClick: () => {
|
|
43
48
|
// La marque de l'organisation retenue pour la coque ne survit pas à la session.
|
|
44
49
|
forgetShellOrganization();
|
|
45
50
|
onSignOut();
|
|
46
51
|
}, children: [_jsx(HugeiconsIcon, { icon: Logout01Icon, "aria-hidden": true }), "Se d\u00E9connecter"] })] })] }));
|
|
47
52
|
}
|
|
53
|
+
/** Documentation, État du service et l'autre vue, dans le menu du compte de la barre de Basalte. */
|
|
54
|
+
function AccountMenuLinks({ links }) {
|
|
55
|
+
const documentation = links.documentation;
|
|
56
|
+
const status = links.status;
|
|
57
|
+
const { otherView } = links;
|
|
58
|
+
if (documentation === undefined && status === undefined && otherView === undefined)
|
|
59
|
+
return null;
|
|
60
|
+
return (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuGroup, { children: [documentation !== undefined ? (_jsx(ShellLinkItem, { link: documentation, icon: BookOpen01Icon, label: "Documentation" })) : null, status !== undefined ? _jsx(ShellLinkItem, { link: status, icon: Activity01Icon, label: "\u00C9tat du service" }) : null, otherView ? (_jsxs(MenuPrimitive.LinkItem, { href: otherView.href, onClick: otherView.onClick, className: MENU_LINK_ITEM, children: [_jsx(HugeiconsIcon, { icon: LinkSquare02Icon, "aria-hidden": true }), otherView.label] })) : null] })] }));
|
|
61
|
+
}
|
|
62
|
+
function ShellLinkItem({ link, icon, label, }) {
|
|
63
|
+
const props = typeof link === "string" ? { href: link } : link;
|
|
64
|
+
return (_jsxs(MenuPrimitive.LinkItem, { ...(props.href !== undefined ? { href: props.href } : {}), ...(props.render !== undefined ? { render: props.render } : {}), className: MENU_LINK_ITEM, children: [_jsx(HugeiconsIcon, { icon: icon, "aria-hidden": true }), label] }));
|
|
65
|
+
}
|
|
48
66
|
function ProductMenuIcon({ product }) {
|
|
49
67
|
if (product.badge)
|
|
50
68
|
return _jsx(ProductBadge, { product: product.badge });
|
package/dist/app-shell.d.ts
CHANGED
|
@@ -11,28 +11,27 @@ export { SidebarNavGroup } from "./sidebar-nav-group.tsx";
|
|
|
11
11
|
export type { AppShellLinks, ShellLink } from "./shell-links.tsx";
|
|
12
12
|
export type { AppShellAssistant } from "./shell-assistant.tsx";
|
|
13
13
|
/**
|
|
14
|
-
* La coque commune des consoles
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* produits restent dans le menu de l'avatar.
|
|
14
|
+
* La coque commune des consoles de Basalte. Sur ordinateur : la barre latérale (l'organisation,
|
|
15
|
+
* Manager, puis les onglets du module ouvert ou la liste des modules, le compte en bas), réductible
|
|
16
|
+
* en rail (`Ctrl+B`, mémorisé), et la page sur une carte. Sur téléphone : une barre en haut, la même
|
|
17
|
+
* navigation dans un tiroir qui monte du bas et se referme quand on choisit une page. Une vue seule
|
|
18
|
+
* (`<mot>.hgdc.ch`) garde la marque du produit à la place de la barre de Basalte.
|
|
20
19
|
*
|
|
21
20
|
* Le kit ne connaît ni le routeur ni l'authentification : le produit passe ses éléments
|
|
22
21
|
* (`OrgSwitcher`, `SidebarNav`, `AccountMenu`) et ses liens.
|
|
23
22
|
*/
|
|
24
23
|
export declare function AppShell({ product, organization, productRail: productRailOption, view: viewOption, accountHref, nav, links, account, sidebarFooter, header, onSearch, searchLabel, assistant, children, contentKey, navigating, mainClassName, statusPage, }: {
|
|
25
|
-
/** Le produit affiché (`"host"`) :
|
|
24
|
+
/** Le produit affiché (`"host"`) : son nom au-dessus de ses onglets, « vous êtes ici » dans la liste des modules. */
|
|
26
25
|
product: FamilyProduct;
|
|
27
26
|
/**
|
|
28
|
-
* `OrgSwitcher` : en haut de la barre
|
|
27
|
+
* `OrgSwitcher` : en haut de la barre latérale et du tiroir sur téléphone. Absent
|
|
29
28
|
* pendant que les données du produit arrivent : la barre lui garde sa place, avec la marque de
|
|
30
29
|
* la dernière organisation affichée (`OrgPlaceholder`, ADR 0020). `null` : aucune organisation.
|
|
31
30
|
*/
|
|
32
31
|
organization?: ReactNode;
|
|
33
32
|
/**
|
|
34
|
-
* `false` : pas de barre
|
|
35
|
-
*
|
|
33
|
+
* `false` : pas de barre de Basalte (un écran hors de la console, une démo) : la marque du produit
|
|
34
|
+
* en haut, l'organisation dessous. Absent : présente, ou, si `view` est donné, selon la vue
|
|
36
35
|
* et le réglage « Barre des produits » d'Account (ADR 0014), relu par la coque.
|
|
37
36
|
*/
|
|
38
37
|
productRail?: boolean;
|
|
@@ -42,13 +41,13 @@ export declare function AppShell({ product, organization, productRail: productRa
|
|
|
42
41
|
* Absent : un produit qui n'a qu'une vue, barre présente et pas de lien vers l'autre vue.
|
|
43
42
|
*/
|
|
44
43
|
view?: ProductView | undefined;
|
|
45
|
-
/** Adresse
|
|
44
|
+
/** Adresse de Manager dans la barre (un Manager local en développement). */
|
|
46
45
|
accountHref?: string | undefined;
|
|
47
46
|
/** `SidebarNav` et ses `SidebarNavItem`. */
|
|
48
47
|
nav: ReactNode;
|
|
49
48
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
49
|
+
* Réglages, juste au-dessus du compte ; Documentation et État du service (qui suit `statusPage` par
|
|
50
|
+
* défaut) dans le menu du compte. Dans une vue seule, les trois au-dessus du compte.
|
|
52
51
|
*/
|
|
53
52
|
links?: AppShellLinks;
|
|
54
53
|
/** `AccountMenu`, tout en bas de la barre latérale (et du tiroir sur téléphone). */
|
|
@@ -99,7 +98,7 @@ export declare function AppShell({ product, organization, productRail: productRa
|
|
|
99
98
|
/**
|
|
100
99
|
* Page de statut du produit, dont le bandeau d'état se pose en tête du contenu (ADR 0018) :
|
|
101
100
|
* `https://status.hgdc.ch/<produit>` par défaut, une autre adresse (prévisualisation sur une page
|
|
102
|
-
* de test), ou `false` pour ne rien lire. L'accès « État du service
|
|
101
|
+
* de test), ou `false` pour ne rien lire. L'accès « État du service » y mène.
|
|
103
102
|
*/
|
|
104
103
|
statusPage?: string | false | undefined;
|
|
105
104
|
}): React.JSX.Element;
|
package/dist/app-shell.js
CHANGED
|
@@ -8,17 +8,17 @@ import { FamilyLoaderHost, FamilyLoaderRootContext } from "./family-loader.js";
|
|
|
8
8
|
import { CONTENT_LOADING_LAYER, ContentLoadingContext, PagePending } from "./page-pending.js";
|
|
9
9
|
import { ProductBadge } from "./product-badge.js";
|
|
10
10
|
import { productBarShown, useProductBarPreference, useProductBarSync } from "./product-bar.js";
|
|
11
|
-
import {
|
|
11
|
+
import { ShellNavigation } from "./module-bar.js";
|
|
12
12
|
import { useProductSwitch } from "./product-switch.js";
|
|
13
13
|
import { cn } from "./cn.js";
|
|
14
14
|
import { ServiceStatus, statusPageUrlFor } from "./service-status.js";
|
|
15
15
|
import { AssistantButton, AssistantPanel, ASSISTANT_SIDE_BY_SIDE, useAssistantShortcut, } from "./shell-assistant.js";
|
|
16
16
|
import { ShellContext } from "./shell-context.js";
|
|
17
17
|
import { familyProductName } from "./shell-frame.js";
|
|
18
|
-
import { OtherViewLink, ShellCommonLinks } from "./shell-links.js";
|
|
18
|
+
import { OtherViewLink, otherViewMenuLink, ShellCommonLinks } from "./shell-links.js";
|
|
19
19
|
import { rememberLastProduct, rememberShellNavHeight } from "./shell-memory.js";
|
|
20
|
-
import {
|
|
21
|
-
import { SidebarHead } from "./sidebar-head.js";
|
|
20
|
+
import { ModuleBarShownContext, ShellLeaveContext, ShellMenuLinksContext, StatusPageContext, } from "./shell-placement.js";
|
|
21
|
+
import { ShellOrganizationHead, SidebarHead } from "./sidebar-head.js";
|
|
22
22
|
import { useToggleShortcut } from "./sidebar-rail.js";
|
|
23
23
|
import { useContentLoading, useHydrated, useIsomorphicLayoutEffect } from "./use-content-loading.js";
|
|
24
24
|
import { useProductLeave } from "./use-product-leave.js";
|
|
@@ -29,12 +29,11 @@ export { useAppShell } from "./shell-context.js";
|
|
|
29
29
|
export { SidebarNav, SidebarNavBack, SidebarNavItem } from "./sidebar-nav.js";
|
|
30
30
|
export { SidebarNavGroup } from "./sidebar-nav-group.js";
|
|
31
31
|
/**
|
|
32
|
-
* La coque commune des consoles
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* produits restent dans le menu de l'avatar.
|
|
32
|
+
* La coque commune des consoles de Basalte. Sur ordinateur : la barre latérale (l'organisation,
|
|
33
|
+
* Manager, puis les onglets du module ouvert ou la liste des modules, le compte en bas), réductible
|
|
34
|
+
* en rail (`Ctrl+B`, mémorisé), et la page sur une carte. Sur téléphone : une barre en haut, la même
|
|
35
|
+
* navigation dans un tiroir qui monte du bas et se referme quand on choisit une page. Une vue seule
|
|
36
|
+
* (`<mot>.hgdc.ch`) garde la marque du produit à la place de la barre de Basalte.
|
|
38
37
|
*
|
|
39
38
|
* Le kit ne connaît ni le routeur ni l'authentification : le produit passe ses éléments
|
|
40
39
|
* (`OrgSwitcher`, `SidebarNav`, `AccountMenu`) et ses liens.
|
|
@@ -45,8 +44,7 @@ export function AppShell({ product, organization, productRail: productRailOption
|
|
|
45
44
|
// Relu chez Account seulement par un produit passé aux deux vues (il donne `view`, ADR 0014).
|
|
46
45
|
useProductBarSync(productRailOption === undefined && viewOption !== undefined);
|
|
47
46
|
const productRail = productRailOption ?? (viewOption === undefined || productBarShown(viewOption, preference));
|
|
48
|
-
|
|
49
|
-
const [railPeek, setRailPeek] = useState(false);
|
|
47
|
+
const [navLevel, setNavLevel] = useState("tabs");
|
|
50
48
|
useToggleShortcut();
|
|
51
49
|
// Relecture des organisations après un passage depuis la barre, préchargement des autres produits (ADR 0016).
|
|
52
50
|
useProductSwitch(productRail);
|
|
@@ -77,7 +75,7 @@ export function AppShell({ product, organization, productRail: productRailOption
|
|
|
77
75
|
}, [product]);
|
|
78
76
|
const navHeight = useRef(0);
|
|
79
77
|
useEffect(() => {
|
|
80
|
-
const list = sidebarRef.current?.querySelector(":scope > nav > ul");
|
|
78
|
+
const list = sidebarRef.current?.querySelector(":scope > nav > ul, [data-slot=shell-navigation] > nav:not([data-slot]) > ul");
|
|
81
79
|
if (!(list instanceof HTMLElement) || list.offsetHeight === navHeight.current)
|
|
82
80
|
return;
|
|
83
81
|
navHeight.current = list.offsetHeight;
|
|
@@ -100,6 +98,16 @@ export function AppShell({ product, organization, productRail: productRailOption
|
|
|
100
98
|
const assistantPanelId = useId();
|
|
101
99
|
const assistantButtonRef = useRef(null);
|
|
102
100
|
useAssistantShortcut(assistant);
|
|
103
|
-
const
|
|
104
|
-
|
|
101
|
+
const otherView = viewOption === undefined ? null : _jsx(OtherViewLink, { product: product, view: viewOption });
|
|
102
|
+
// Dans la barre de Basalte, seul Réglages reste au-dessus du compte : le reste passe dans son menu.
|
|
103
|
+
const footer = (_jsxs(_Fragment, { children: [account !== undefined ? sidebarFooter : null, _jsx(ShellCommonLinks, { links: productRail ? { ...(links?.settings ? { settings: links.settings } : {}), status: false } : (links ?? {}), statusPageUrl: statusPageUrl, otherView: productRail ? null : otherView }), account ?? sidebarFooter] }));
|
|
104
|
+
const menuLinks = productRail
|
|
105
|
+
? {
|
|
106
|
+
documentation: links?.documentation,
|
|
107
|
+
status: links?.status === false ? undefined : (links?.status ?? statusPageUrl ?? undefined),
|
|
108
|
+
otherView: viewOption === undefined ? undefined : otherViewMenuLink(product, viewOption),
|
|
109
|
+
}
|
|
110
|
+
: {};
|
|
111
|
+
const navigation = productRail ? (_jsx(ShellNavigation, { product: product, nav: nav, accountHref: accountHref, level: navLevel, onLevelChange: setNavLevel })) : (nav);
|
|
112
|
+
return (_jsx(ShellContext.Provider, { value: { closeMobileNav: () => setMenuOpen(false), product }, children: _jsx(ModuleBarShownContext, { value: productRail, children: _jsx(ShellMenuLinksContext, { value: menuLinks, children: _jsx(ShellLeaveContext, { value: leave, children: _jsx(StatusPageContext, { value: statusPageUrl, children: _jsxs("div", { "data-slot": "app-shell", "data-module-bar": productRail ? "" : undefined, "data-loading": loading ? "" : undefined, "data-hydrated": hydrated ? "" : undefined, "data-assistant": assistantOpen ? "" : undefined, className: "min-h-dvh lg:grid lg:h-dvh lg:grid-cols-[auto_minmax(0,1fr)_auto] lg:bg-shell", children: [_jsx("a", { href: "#contenu", className: "sr-only z-50 rounded-xl bg-background px-4 py-2 text-sm font-medium focus:not-sr-only focus:fixed focus:top-3 focus:left-3 focus:ring-2 focus:ring-ring", children: "Aller au contenu" }), _jsxs("aside", { ref: sidebarRef, "data-slot": "app-shell-sidebar", "aria-label": "Barre lat\u00E9rale", inert: pageBlocked || undefined, className: "group/sidebar hidden overflow-hidden text-shell-foreground transition-[width] duration-(--duration-base) ease-ios motion-reduce:transition-none lg:flex lg:w-64 lg:flex-col lg:rail:w-16", children: [productRail ? (_jsx(ShellOrganizationHead, { organization: organization })) : (_jsxs(_Fragment, { children: [_jsx(SidebarHead, { product: product }), organization ? _jsx("div", { className: "shrink-0 px-3 pb-1", children: organization }) : null] })), navigation, _jsx("div", { "data-slot": "app-shell-footer", className: "grid gap-0.5 px-3 pt-1 pb-3 empty:hidden", children: footer })] }), _jsx("div", { inert: pageBlocked || undefined, className: "flex min-h-dvh flex-col lg:min-h-0 lg:p-2 lg:pl-0", children: _jsxs("div", { ref: contentRef, className: "relative flex flex-1 flex-col lg:min-h-0 lg:overflow-y-auto lg:rounded-xl lg:bg-background lg:[scrollbar-gutter:stable]", children: [_jsxs("header", { "data-slot": "app-shell-header", className: "sticky top-[env(safe-area-inset-top,0px)] z-30 flex min-h-14 shrink-0 flex-wrap items-center gap-x-1 gap-y-1 border-b border-foreground/5 bg-background px-2 py-1.5 sm:px-4 lg:top-0", children: [_jsx("button", { type: "button", onClick: () => setMenuOpen(true), "aria-label": "Ouvrir le menu", className: "touch-target grid size-10 shrink-0 place-items-center rounded-xl text-muted-foreground outline-none hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/50 lg:hidden", children: _jsx(HugeiconsIcon, { icon: Menu01Icon, "aria-hidden": true, className: "size-5" }) }), _jsxs("div", { "data-slot": "app-shell-brand", className: "flex shrink-0 items-center gap-2 pr-1 lg:hidden", children: [_jsx(ProductBadge, { product: product, className: "size-6" }), _jsx("span", { className: "font-heading text-[0.9375rem] font-semibold", children: familyProductName(product) })] }), _jsx("div", { className: cn("flex min-w-0 items-center gap-2", header ? "flex-[1_1_7rem]" : "flex-1"), children: header }), onSearch ? (_jsx(CommandMenuButton, { onClick: onSearch, compact: "mobile", className: "shrink-0 lg:w-96 lg:max-w-none", ...(searchLabel !== undefined ? { label: searchLabel } : {}) })) : null, assistant ? (_jsx(AssistantButton, { assistant: assistant, panelId: assistantPanelId, buttonRef: assistantButtonRef })) : null] }), _jsx("div", { "data-slot": "app-shell-loading", "aria-hidden": !loading, className: CONTENT_LOADING_LAYER, children: loading ? (_jsx("span", { role: "status", className: "sr-only", children: "Chargement\u2026" })) : null }), _jsxs("main", { id: "contenu", tabIndex: -1, "aria-busy": loading ? true : undefined, inert: loading || undefined, className: cn("mx-auto w-full max-w-6xl flex-1 px-4 pt-6 pb-16 outline-none sm:px-6 lg:px-10 lg:pt-8", mainClassName), children: [_jsx(ServiceStatus, { pageUrl: statusPageUrl, className: "mb-6" }), _jsx(ContentLoadingContext, { value: registerPagePending, children: contentKey === undefined ? (children) : (_jsx(Suspense, { fallback: _jsx(PagePending, {}), children: children }, contentKey)) })] })] }) }), assistant && assistantMounted ? (_jsx(AssistantPanel, { assistant: assistant, panelId: assistantPanelId, wide: wide, openAtShellMount: assistantOpenAtMount, buttonRef: assistantButtonRef })) : null, hasRoot ? null : _jsx(FamilyLoaderHost, {}), _jsx(Drawer, { side: "bottom", open: menuOpen, onOpenChange: setMenuOpen, children: _jsxs(DrawerContent, { className: "lg:hidden", children: [_jsx(DrawerTitle, { className: "sr-only", children: "Menu" }), organization ? _jsx("div", { className: "px-3 pt-1", children: organization }) : null, navigation, _jsx("div", { className: "grid gap-0.5 border-t border-foreground/5 px-3 pt-2 pb-[max(env(safe-area-inset-bottom),0.75rem)] empty:hidden", children: footer })] }) })] }) }) }) }) }) }));
|
|
105
113
|
}
|
package/dist/email.js
CHANGED
|
@@ -81,7 +81,7 @@ export function EmailLayout({ product: ref, preview, children, name, iconSrc, ac
|
|
|
81
81
|
border: `1px solid ${l.border}`,
|
|
82
82
|
borderRadius: 16,
|
|
83
83
|
padding: "32px 28px",
|
|
84
|
-
}, children: children }), _jsxs(Section, { style: { padding: "20px 4px 0" }, children: [footer ? (_jsx(Text, { className: "hg-muted", style: { ...muted, margin: "0 0 12px" }, children: footer })) : null, _jsxs(Text, { className: "hg-muted", style: muted, children: [_jsx(Img, { src: emailIconUrl("mail"), width: 16, height: 16, alt: "", style: { display: "inline-block", verticalAlign: "middle", borderRadius: 8, marginRight: 6 } }), _jsxs("span", { style: { verticalAlign: "middle" }, children: ["Envoy\u00E9 avec", " ", _jsx(Link, { href: MAIL_HREF, className: "hg-muted", style: { color: l.muted, textDecoration: "underline" }, children: "
|
|
84
|
+
}, children: children }), _jsxs(Section, { style: { padding: "20px 4px 0" }, children: [footer ? (_jsx(Text, { className: "hg-muted", style: { ...muted, margin: "0 0 12px" }, children: footer })) : null, _jsxs(Text, { className: "hg-muted", style: muted, children: [_jsx(Img, { src: emailIconUrl("mail"), width: 16, height: 16, alt: "", style: { display: "inline-block", verticalAlign: "middle", borderRadius: 8, marginRight: 6 } }), _jsxs("span", { style: { verticalAlign: "middle" }, children: ["Envoy\u00E9 avec", " ", _jsx(Link, { href: MAIL_HREF, className: "hg-muted", style: { color: l.muted, textDecoration: "underline" }, children: "Basalte" })] })] }), _jsx(Text, { className: "hg-muted", style: { ...muted, margin: "4px 0 0" }, children: LEGAL })] })] }) }) })] }));
|
|
85
85
|
}
|
|
86
86
|
export function EmailHeading({ children }) {
|
|
87
87
|
return (_jsx(Heading, { as: "h1", className: "hg-ink hg-heading", style: { color: EMAIL_LIGHT.ink, fontSize: 22, fontWeight: 600, lineHeight: "28px", margin: "0 0 12px" }, children: children }));
|
package/dist/family-svg.js
CHANGED
|
@@ -22,7 +22,7 @@ export function isFamilyProduct(word) {
|
|
|
22
22
|
*/
|
|
23
23
|
export const PRODUCT_BADGE_SVG = {
|
|
24
24
|
account: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><svg x="0" y="0" width="64" height="64" viewBox="12 12.5 40 40"><defs><linearGradient id="g" x1="14" y1="14" x2="50" y2="50" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ff6a3d"/><stop offset="1" stop-color="#fc582e"/></linearGradient></defs><path d="M22 17H42A5 5 0 0 1 47 22V40A5 5 0 0 1 42 45H22A5 5 0 0 1 17 40V22A5 5 0 0 1 22 17Z" fill="url(#g)" fill-opacity="0.28" fill-rule="evenodd"/><path d="M17.5 21H46.5A5.5 5.5 0 0 1 52 26.5V42.5A5.5 5.5 0 0 1 46.5 48H17.5A5.5 5.5 0 0 1 12 42.5V26.5A5.5 5.5 0 0 1 17.5 21ZM20.5 30.9A3.5 3.5 0 1 0 27.5 30.9A3.5 3.5 0 1 0 20.5 30.9ZM17.6 40.6C17.6 37.6 20.5 35.6 24 35.6C27.5 35.6 30.4 37.6 30.4 40.6ZM35.5 30H44.5A1.5 1.5 0 0 1 46 31.5V31.5A1.5 1.5 0 0 1 44.5 33H35.5A1.5 1.5 0 0 1 34 31.5V31.5A1.5 1.5 0 0 1 35.5 30ZM35.5 36H40.5A1.5 1.5 0 0 1 42 37.5V37.5A1.5 1.5 0 0 1 40.5 39H35.5A1.5 1.5 0 0 1 34 37.5V37.5A1.5 1.5 0 0 1 35.5 36Z" fill="url(#g)" fill-rule="evenodd"/></svg></svg>',
|
|
25
|
-
host: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><svg x="0" y="0" width="64" height="64" viewBox="0 -36.5 384 384"><image width="384" height="311" href="data:image/png;base64,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"/></svg></svg>',
|
|
25
|
+
host: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><svg x="0" y="0" width="64" height="64" viewBox="16 16 32 32"><defs><linearGradient id="g" x1="14" y1="14" x2="50" y2="50" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#3b7bff"/><stop offset="1" stop-color="#2b63f2"/></linearGradient></defs><path d="M19.1 18.2H44.9A3.1 3.1 0 0 1 48 21.3V21.699999999999996A3.1 3.1 0 0 1 44.9 24.799999999999997H19.1A3.1 3.1 0 0 1 16 21.699999999999996V21.3A3.1 3.1 0 0 1 19.1 18.2ZM41.75 21.5A1.35 1.35 0 1 0 44.45 21.5A1.35 1.35 0 1 0 41.75 21.5Z" fill="url(#g)" fill-opacity="0.4" fill-rule="evenodd"/><path d="M19.4 26.3H44.6A3.4 3.4 0 0 1 48 29.7V30.9A3.4 3.4 0 0 1 44.6 34.3H19.4A3.4 3.4 0 0 1 16 30.9V29.7A3.4 3.4 0 0 1 19.4 26.3ZM41.550000000000004 30.3A1.55 1.55 0 1 0 44.65 30.3A1.55 1.55 0 1 0 41.550000000000004 30.3Z" fill="url(#g)" fill-opacity="0.7" fill-rule="evenodd"/><path d="M19.7 35.8H44.3A3.7 3.7 0 0 1 48 39.5V42.099999999999994A3.7 3.7 0 0 1 44.3 45.8H19.7A3.7 3.7 0 0 1 16 42.099999999999994V39.5A3.7 3.7 0 0 1 19.7 35.8ZM41.300000000000004 40.8A1.8 1.8 0 1 0 44.9 40.8A1.8 1.8 0 1 0 41.300000000000004 40.8ZM21.7 39.6H28.3A1.2 1.2 0 0 1 29.5 40.800000000000004V40.8A1.2 1.2 0 0 1 28.3 42H21.7A1.2 1.2 0 0 1 20.5 40.8V40.800000000000004A1.2 1.2 0 0 1 21.7 39.6Z" fill="url(#g)" fill-rule="evenodd"/></svg></svg>',
|
|
26
26
|
mail: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><svg x="0" y="0" width="64" height="64" viewBox="14 13 36 36"><defs><linearGradient id="g" x1="14" y1="14" x2="50" y2="50" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fa3a58"/><stop offset="1" stop-color="#ec3052"/></linearGradient></defs><path d="M17.5 25H46.5A3.5 3.5 0 0 1 50 28.5V45.5A3.5 3.5 0 0 1 46.5 49H17.5A3.5 3.5 0 0 1 14 45.5V28.5A3.5 3.5 0 0 1 17.5 25Z" fill="url(#g)" fill-opacity="0.25" fill-rule="evenodd"/><path d="M22.5 13H41.5A2.5 2.5 0 0 1 44 15.5V44.5A2.5 2.5 0 0 1 41.5 47H22.5A2.5 2.5 0 0 1 20 44.5V15.5A2.5 2.5 0 0 1 22.5 13ZM25.2 18H38.8A1.2 1.2 0 0 1 40 19.2V19.2A1.2 1.2 0 0 1 38.8 20.4H25.2A1.2 1.2 0 0 1 24 19.2V19.2A1.2 1.2 0 0 1 25.2 18ZM25.2 23H33.8A1.2 1.2 0 0 1 35 24.2V24.2A1.2 1.2 0 0 1 33.8 25.4H25.2A1.2 1.2 0 0 1 24 24.2V24.2A1.2 1.2 0 0 1 25.2 23Z" fill="url(#g)" fill-opacity="0.7" fill-rule="evenodd"/><path d="M14 29.5L32 40.5L50 29.5V45.5A3.5 3.5 0 0 1 46.5 49H17.5A3.5 3.5 0 0 1 14 45.5Z" fill="url(#g)" fill-rule="evenodd"/></svg></svg>',
|
|
27
27
|
portal: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><svg x="0" y="0" width="64" height="64" viewBox="13 13 38 38"><defs><linearGradient id="g" x1="14" y1="14" x2="50" y2="50" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#8b5cff"/><stop offset="1" stop-color="#7b4bf7"/></linearGradient></defs><path d="M21 13H43A3 3 0 0 1 46 16V47A3 3 0 0 1 43 50H21A3 3 0 0 1 18 47V16A3 3 0 0 1 21 13Z" fill="url(#g)" fill-opacity="0.28" fill-rule="evenodd"/><path d="M20.6 13.3L32.6 16.5A2 2 0 0 1 34 18.4V45.6A2 2 0 0 1 32.6 47.5L20.6 50.7A2 2 0 0 1 18 48.8V15.2A2 2 0 0 1 20.6 13.3ZM28.2 32A1.8 1.8 0 1 0 31.8 32A1.8 1.8 0 1 0 28.2 32Z" fill="url(#g)" fill-rule="evenodd"/></svg></svg>',
|
|
28
28
|
status: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><svg x="0" y="0" width="64" height="64" viewBox="14 14 36 36"><defs><linearGradient id="g" x1="14" y1="14" x2="50" y2="50" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#11ce8b"/><stop offset="1" stop-color="#09b077"/></linearGradient></defs><path d="M14 32A18 18 0 1 0 50 32A18 18 0 1 0 14 32Z" fill="url(#g)" fill-opacity="0.22" fill-rule="evenodd"/><path d="M19.5 32A12.5 12.5 0 1 0 44.5 32A12.5 12.5 0 1 0 19.5 32Z" fill="url(#g)" fill-opacity="0.45" fill-rule="evenodd"/><path d="M25 32A7 7 0 1 0 39 32A7 7 0 1 0 25 32Z" fill="url(#g)" fill-rule="evenodd"/></svg></svg>',
|
|
@@ -38,7 +38,7 @@ export const PRODUCT_BADGE_SVG = {
|
|
|
38
38
|
*/
|
|
39
39
|
export const EMAIL_ICON_VERSION = {
|
|
40
40
|
account: "3585e697",
|
|
41
|
-
host: "
|
|
41
|
+
host: "f7879b39",
|
|
42
42
|
mail: "c3a003ef",
|
|
43
43
|
portal: "493abbd5",
|
|
44
44
|
status: "90f70c05",
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ModuleWord } from "./modules.ts";
|
|
2
|
+
/**
|
|
3
|
+
* L'ordre et le choix des modules de la barre, propres à chaque personne. Sous `app.hgdc.ch`, toutes
|
|
4
|
+
* les consoles partagent la même origine, donc le même réglage.
|
|
5
|
+
*/
|
|
6
|
+
export declare const MODULE_BAR_KEY = "hgdc-shell:modules";
|
|
7
|
+
export declare function saveModuleBar(order: ReadonlyArray<ModuleWord>): void;
|
|
8
|
+
/** L'ordre retenu ; celui par défaut au rendu serveur. */
|
|
9
|
+
export declare function useModuleBar(): ReadonlyArray<ModuleWord>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { useSyncExternalStore } from "react";
|
|
2
|
+
import { DEFAULT_MODULE_BAR, parseModuleBar } from "./modules.js";
|
|
3
|
+
/**
|
|
4
|
+
* L'ordre et le choix des modules de la barre, propres à chaque personne. Sous `app.hgdc.ch`, toutes
|
|
5
|
+
* les consoles partagent la même origine, donc le même réglage.
|
|
6
|
+
*/
|
|
7
|
+
export const MODULE_BAR_KEY = "hgdc-shell:modules";
|
|
8
|
+
const EVENT = "hgdc-module-bar";
|
|
9
|
+
let cachedRaw;
|
|
10
|
+
let cachedOrder = DEFAULT_MODULE_BAR;
|
|
11
|
+
/** Même référence tant que le stockage ne change pas : `useSyncExternalStore` compare par identité. */
|
|
12
|
+
function read() {
|
|
13
|
+
let raw = null;
|
|
14
|
+
try {
|
|
15
|
+
raw = window.localStorage.getItem(MODULE_BAR_KEY);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return DEFAULT_MODULE_BAR;
|
|
19
|
+
}
|
|
20
|
+
if (raw === cachedRaw)
|
|
21
|
+
return cachedOrder;
|
|
22
|
+
cachedRaw = raw;
|
|
23
|
+
cachedOrder = decode(raw);
|
|
24
|
+
return cachedOrder;
|
|
25
|
+
}
|
|
26
|
+
function decode(raw) {
|
|
27
|
+
if (raw === null)
|
|
28
|
+
return DEFAULT_MODULE_BAR;
|
|
29
|
+
try {
|
|
30
|
+
return parseModuleBar(JSON.parse(raw)) ?? DEFAULT_MODULE_BAR;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return DEFAULT_MODULE_BAR;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function subscribe(onChange) {
|
|
37
|
+
window.addEventListener(EVENT, onChange);
|
|
38
|
+
window.addEventListener("storage", onChange);
|
|
39
|
+
return () => {
|
|
40
|
+
window.removeEventListener(EVENT, onChange);
|
|
41
|
+
window.removeEventListener("storage", onChange);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function saveModuleBar(order) {
|
|
45
|
+
try {
|
|
46
|
+
window.localStorage.setItem(MODULE_BAR_KEY, JSON.stringify(order));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Stockage bloqué : la barre reprend l'ordre par défaut au prochain chargement.
|
|
50
|
+
}
|
|
51
|
+
window.dispatchEvent(new Event(EVENT));
|
|
52
|
+
}
|
|
53
|
+
/** L'ordre retenu ; celui par défaut au rendu serveur. */
|
|
54
|
+
export function useModuleBar() {
|
|
55
|
+
return useSyncExternalStore(subscribe, read, () => DEFAULT_MODULE_BAR);
|
|
56
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import type { FamilyProduct } from "./family-svg.ts";
|
|
3
|
+
/** Ce que montre le milieu de la barre : les onglets du module ouvert, la liste des modules, ou son réglage. */
|
|
4
|
+
export type ShellNavLevel = "tabs" | "modules" | "edit";
|
|
5
|
+
/**
|
|
6
|
+
* Le milieu de la barre de Basalte : Manager, un trait, puis l'un des trois niveaux. Dans un module,
|
|
7
|
+
* ses onglets (`nav`) sous « Retour aux modules » ; un clic y affiche la liste sans rien recharger ;
|
|
8
|
+
* un clic sur un autre module ouvre sa console, préchargée au survol sous la même origine.
|
|
9
|
+
*/
|
|
10
|
+
export declare function ShellNavigation({ product, nav, accountHref, level, onLevelChange, }: {
|
|
11
|
+
product: FamilyProduct;
|
|
12
|
+
nav: ReactNode;
|
|
13
|
+
accountHref?: string | undefined;
|
|
14
|
+
level: ShellNavLevel;
|
|
15
|
+
onLevelChange: (level: ShellNavLevel) => void;
|
|
16
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import Add01Icon from "@hugeicons/core-free-icons/Add01Icon";
|
|
3
|
+
import ArrowDown01Icon from "@hugeicons/core-free-icons/ArrowDown01Icon";
|
|
4
|
+
import ArrowUp01Icon from "@hugeicons/core-free-icons/ArrowUp01Icon";
|
|
5
|
+
import DragDropVerticalIcon from "@hugeicons/core-free-icons/DragDropVerticalIcon";
|
|
6
|
+
import Edit02Icon from "@hugeicons/core-free-icons/Edit02Icon";
|
|
7
|
+
import MinusSignIcon from "@hugeicons/core-free-icons/MinusSignIcon";
|
|
8
|
+
import UserGroupIcon from "@hugeicons/core-free-icons/UserGroupIcon";
|
|
9
|
+
import { HugeiconsIcon } from "@hugeicons/react";
|
|
10
|
+
import { useState, useSyncExternalStore } from "react";
|
|
11
|
+
import { Button } from "./button.js";
|
|
12
|
+
import { cn } from "./cn.js";
|
|
13
|
+
import { saveModuleBar, useModuleBar } from "./module-bar-store.js";
|
|
14
|
+
import { hiddenModules, moduleByWord, moveModule, shownModules } from "./modules.js";
|
|
15
|
+
import { ProductBadge } from "./product-badge.js";
|
|
16
|
+
import { PRODUCT_PRERENDER_ATTRIBUTE } from "./product-switch.js";
|
|
17
|
+
import { shellProductEntries } from "./products.js";
|
|
18
|
+
import { familyProductName } from "./product-name.js";
|
|
19
|
+
import { useLeaveOnClick } from "./shell-placement.js";
|
|
20
|
+
import { SIDEBAR_ITEM, SidebarNavBack } from "./sidebar-nav.js";
|
|
21
|
+
import { useSidebarRail } from "./sidebar-rail.js";
|
|
22
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "./tooltip.js";
|
|
23
|
+
const noSubscription = () => () => { };
|
|
24
|
+
/** `location.origin` après l'hydratation, `undefined` au rendu serveur (liens absolus d'abord). */
|
|
25
|
+
function usePageOrigin() {
|
|
26
|
+
return useSyncExternalStore(noSubscription, () => window.location.origin, () => undefined);
|
|
27
|
+
}
|
|
28
|
+
const GROUP_LABEL = "px-3 pt-3 pb-1.5 text-xs font-medium text-muted-foreground lg:text-shell-muted lg:rail:sr-only";
|
|
29
|
+
function ShellEntryLink({ entry, icon, onOpenCurrent, }) {
|
|
30
|
+
const rail = useSidebarRail();
|
|
31
|
+
const leaveOnClick = useLeaveOnClick();
|
|
32
|
+
const onClick = (event) => {
|
|
33
|
+
const plain = event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey;
|
|
34
|
+
if (entry.current && onOpenCurrent && plain) {
|
|
35
|
+
event.preventDefault();
|
|
36
|
+
onOpenCurrent();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
leaveOnClick(event);
|
|
40
|
+
};
|
|
41
|
+
return (_jsx("li", { children: _jsxs(Tooltip, { disabled: !rail, children: [_jsxs(TooltipTrigger, { render: _jsx("a", { href: entry.href, onClick: onClick, ...(entry.sameOrigin ? { [PRODUCT_PRERENDER_ATTRIBUTE]: "" } : {}), "aria-current": entry.current ? "page" : undefined, className: SIDEBAR_ITEM }), children: [icon ? (_jsx(HugeiconsIcon, { icon: icon, strokeWidth: 1.8, "aria-hidden": true, className: "size-5 shrink-0 lg:size-[1.125rem]" })) : (_jsx(ProductBadge, { product: entry.badge, className: "size-5 shrink-0 lg:size-[1.125rem]" })), _jsx("span", { className: "min-w-0 flex-1 truncate lg:rail:sr-only", children: entry.name }), entry.hint ? _jsx("span", { className: "text-xs font-normal lg:rail:hidden", children: entry.hint }) : null] }), _jsx(TooltipContent, { side: "right", children: entry.name })] }) }));
|
|
42
|
+
}
|
|
43
|
+
function ModuleList({ entries, order, onOpenCurrent, onCustomize, }) {
|
|
44
|
+
const shown = order.flatMap((word) => {
|
|
45
|
+
const entry = entries.find((item) => item.badge === word);
|
|
46
|
+
return entry ? [entry] : [];
|
|
47
|
+
});
|
|
48
|
+
return (_jsxs("nav", { "data-slot": "shell-modules", "aria-label": "Modules", className: "min-h-0 flex-1 overflow-y-auto px-3 pb-2", children: [_jsx("p", { className: GROUP_LABEL, children: "Modules du projet" }), _jsx("ul", { className: "grid gap-0.5", children: shown.map((entry) => (_jsx(ShellEntryLink, { entry: entry, onOpenCurrent: onOpenCurrent }, entry.badge))) }), _jsxs("button", { type: "button", onClick: onCustomize, className: cn(SIDEBAR_ITEM, "mt-1 w-full text-left font-normal lg:rail:hidden"), children: [_jsx(HugeiconsIcon, { icon: Edit02Icon, strokeWidth: 1.8, "aria-hidden": true, className: "size-5 shrink-0 lg:size-[1.125rem]" }), _jsx("span", { className: "truncate", children: "Personnaliser la barre" })] })] }));
|
|
49
|
+
}
|
|
50
|
+
/** Petit bouton d'une ligne du réglage : 44 px au doigt (`touch-target`), 24 px à la souris. */
|
|
51
|
+
function RowButton({ icon, label, disabled, onClick, }) {
|
|
52
|
+
return (_jsx("button", { type: "button", "aria-label": label, disabled: disabled, onClick: onClick, className: "touch-target grid size-6 shrink-0 place-items-center rounded-lg text-muted-foreground outline-none transition-colors duration-(--duration-fast) ease-out hover:bg-muted hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-30 lg:text-shell-muted lg:hover:bg-shell-active lg:hover:text-shell-foreground", children: _jsx(HugeiconsIcon, { icon: icon, strokeWidth: 2, "aria-hidden": true, className: "size-3.5" }) }));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Le réglage de la barre : un brouillon montré tel quel, gardé seulement par « Valider ». Glisser à
|
|
56
|
+
* la souris ; les flèches servent au clavier et au doigt, le glisser natif n'y fonctionne pas.
|
|
57
|
+
*/
|
|
58
|
+
function ModuleBarEditor({ order, onSave, onCancel, }) {
|
|
59
|
+
const [draft, setDraft] = useState(order);
|
|
60
|
+
const [dragged, setDragged] = useState(null);
|
|
61
|
+
const hidden = hiddenModules(draft);
|
|
62
|
+
return (_jsxs("div", { "data-slot": "shell-modules-editor", role: "group", "aria-label": "Personnaliser la barre", className: "min-h-0 flex-1 overflow-y-auto px-3 pb-2", children: [_jsx("p", { className: GROUP_LABEL, children: "Dans la barre" }), _jsx("ul", { className: "grid gap-0.5", children: draft.map((word, index) => {
|
|
63
|
+
const { name } = moduleByWord(word);
|
|
64
|
+
return (_jsxs("li", { draggable: true, onDragStart: (event) => {
|
|
65
|
+
event.dataTransfer.effectAllowed = "move";
|
|
66
|
+
setDragged(index);
|
|
67
|
+
}, onDragOver: (event) => {
|
|
68
|
+
if (dragged !== null)
|
|
69
|
+
event.preventDefault();
|
|
70
|
+
}, onDrop: (event) => {
|
|
71
|
+
event.preventDefault();
|
|
72
|
+
if (dragged !== null)
|
|
73
|
+
setDraft(moveModule(draft, dragged, index));
|
|
74
|
+
setDragged(null);
|
|
75
|
+
}, onDragEnd: () => setDragged(null), className: "flex min-h-11 cursor-grab items-center gap-1 rounded-xl bg-muted/70 pr-1 pl-1.5 text-sm font-medium lg:min-h-9 lg:bg-shell-hover coarse:min-h-11", children: [_jsx(HugeiconsIcon, { icon: DragDropVerticalIcon, "aria-hidden": true, className: "size-3.5 shrink-0 text-muted-foreground lg:text-shell-muted" }), _jsx(ProductBadge, { product: word, className: "size-[1.125rem] shrink-0" }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: name }), _jsx(RowButton, { icon: ArrowUp01Icon, label: `Monter ${name}`, disabled: index === 0, onClick: () => setDraft(moveModule(draft, index, index - 1)) }), _jsx(RowButton, { icon: ArrowDown01Icon, label: `Descendre ${name}`, disabled: index === draft.length - 1, onClick: () => setDraft(moveModule(draft, index, index + 1)) }), _jsx(RowButton, { icon: MinusSignIcon, label: `Masquer ${name}`, onClick: () => setDraft(draft.filter((other) => other !== word)) })] }, word));
|
|
76
|
+
}) }), _jsx("p", { className: GROUP_LABEL, children: "Masqu\u00E9s" }), hidden.length ? (_jsx("ul", { className: "grid gap-0.5", children: hidden.map(({ word, name, purpose }) => (_jsxs("li", { className: "flex min-h-11 items-center gap-2.5 rounded-xl pr-1 pl-3 text-sm font-medium lg:min-h-9 coarse:min-h-11", children: [_jsx(ProductBadge, { product: word, className: "size-[1.125rem] shrink-0 opacity-70" }), _jsxs("span", { className: "min-w-0 flex-1 py-1", children: [_jsx("span", { className: "block truncate", children: name }), _jsx("span", { className: "block truncate text-xs font-normal text-muted-foreground lg:text-shell-muted", children: purpose })] }), _jsx(RowButton, { icon: Add01Icon, label: `Ajouter ${name}`, onClick: () => setDraft([...draft, word]) })] }, word))) })) : (_jsx("p", { className: "px-3 py-1 text-xs text-muted-foreground lg:text-shell-muted", children: "Tous les modules sont dans la barre." })), _jsxs("div", { className: "flex gap-2 px-1 pt-3", children: [_jsx(Button, { size: "sm", onClick: () => onSave(draft), children: "Valider" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: onCancel, children: "Annuler" })] })] }));
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Le milieu de la barre de Basalte : Manager, un trait, puis l'un des trois niveaux. Dans un module,
|
|
80
|
+
* ses onglets (`nav`) sous « Retour aux modules » ; un clic y affiche la liste sans rien recharger ;
|
|
81
|
+
* un clic sur un autre module ouvre sa console, préchargée au survol sous la même origine.
|
|
82
|
+
*/
|
|
83
|
+
export function ShellNavigation({ product, nav, accountHref, level, onLevelChange, }) {
|
|
84
|
+
const pageOrigin = usePageOrigin();
|
|
85
|
+
const order = useModuleBar();
|
|
86
|
+
const { products, account } = shellProductEntries({ current: product, accountHref, pageOrigin });
|
|
87
|
+
return (_jsxs("div", { "data-slot": "shell-navigation", className: "flex min-h-0 flex-1 flex-col", children: [_jsxs("div", { "data-slot": "shell-manager", className: "shrink-0 px-3 pt-1", children: [_jsx("ul", { children: _jsx(ShellEntryLink, { entry: account, icon: UserGroupIcon }) }), _jsx("div", { "aria-hidden": true, className: "mx-2 mt-2 h-px bg-foreground/8" })] }), level === "modules" ? (_jsx(ModuleList, { entries: products, order: shownModules(order, product), onOpenCurrent: () => onLevelChange("tabs"), onCustomize: () => onLevelChange("edit") })) : level === "edit" ? (_jsx(ModuleBarEditor, { order: order, onSave: (next) => {
|
|
88
|
+
saveModuleBar(next);
|
|
89
|
+
onLevelChange("modules");
|
|
90
|
+
}, onCancel: () => onLevelChange("modules") })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "shrink-0 px-3 pt-2", children: [_jsx("ul", { children: _jsx(SidebarNavBack, { label: "Retour aux modules", keepMenuOpen: true, render: _jsx("button", { type: "button", className: "w-full", onClick: () => onLevelChange("modules") }) }) }), _jsxs("div", { "data-slot": "shell-module-title", className: "flex min-h-9 items-center gap-2.5 px-3 text-sm font-semibold lg:text-shell-foreground lg:rail:hidden", children: [_jsx(ProductBadge, { product: product, className: "size-[1.125rem] shrink-0" }), _jsx("span", { className: "truncate font-heading", children: familyProductName(product) })] })] }), nav] }))] }));
|
|
91
|
+
}
|