@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
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { FamilyProduct } from "./family-svg.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Les modules de Basalte, un produit unique (organisation, projets, modules). Le mot technique
|
|
4
|
+
* (`host`) reste dans les adresses, les clés et le code ; l'interface n'affiche que le nom du module.
|
|
5
|
+
* Logique pure : la barre (`module-bar.tsx`) et son stockage (`module-bar-store.ts`) s'y appuient.
|
|
6
|
+
*/
|
|
7
|
+
export type ModuleWord = Extract<FamilyProduct, "host" | "mail" | "status" | "analytics" | "id" | "docs" | "iassistant">;
|
|
8
|
+
export type BasalteModule = {
|
|
9
|
+
word: ModuleWord;
|
|
10
|
+
/** Nom affiché : « Déploiements ». */
|
|
11
|
+
name: string;
|
|
12
|
+
/** Ce que le module apporte, en quelques mots : montré à côté d'un module masqué. */
|
|
13
|
+
purpose: string;
|
|
14
|
+
};
|
|
15
|
+
/** Tous les modules, dans leur ordre par défaut. */
|
|
16
|
+
export declare const BASALTE_MODULES: ReadonlyArray<BasalteModule>;
|
|
17
|
+
/** Les modules montrés tant que la personne n'a rien choisi. */
|
|
18
|
+
export declare const DEFAULT_MODULE_BAR: ReadonlyArray<ModuleWord>;
|
|
19
|
+
export declare function isModuleWord(value: unknown): value is ModuleWord;
|
|
20
|
+
export declare function moduleByWord(word: ModuleWord): BasalteModule;
|
|
21
|
+
/** Ordre retenu (stockage, réseau) : les mots connus, sans doublon. `null` si ce n'est pas une liste. */
|
|
22
|
+
export declare function parseModuleBar(value: unknown): Array<ModuleWord> | null;
|
|
23
|
+
/**
|
|
24
|
+
* La liste à montrer : l'ordre de la personne, plus le module ouvert s'il n'y est pas (on ne perd
|
|
25
|
+
* jamais de vue où l'on est).
|
|
26
|
+
*/
|
|
27
|
+
export declare function shownModules(order: ReadonlyArray<ModuleWord>, current?: string): Array<ModuleWord>;
|
|
28
|
+
/** Les modules absents de la liste, dans l'ordre par défaut. */
|
|
29
|
+
export declare function hiddenModules(shown: ReadonlyArray<ModuleWord>): Array<BasalteModule>;
|
|
30
|
+
/** Déplace l'entrée `from` à la place `to` ; hors limites, la liste reste telle quelle. */
|
|
31
|
+
export declare function moveModule<T>(list: ReadonlyArray<T>, from: number, to: number): Array<T>;
|
package/dist/modules.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** Tous les modules, dans leur ordre par défaut. */
|
|
2
|
+
export const BASALTE_MODULES = [
|
|
3
|
+
{ word: "host", name: "Déploiements", purpose: "Mettre un site en ligne" },
|
|
4
|
+
{ word: "mail", name: "E-mails", purpose: "Envoyer des e-mails" },
|
|
5
|
+
{ word: "status", name: "Surveillance", purpose: "Surveiller un site" },
|
|
6
|
+
{ word: "analytics", name: "Statistiques", purpose: "Compter les visites" },
|
|
7
|
+
{ word: "id", name: "Connexion", purpose: "Connecter tes utilisateurs" },
|
|
8
|
+
{ word: "docs", name: "Documentation", purpose: "Publier une documentation" },
|
|
9
|
+
{ word: "iassistant", name: "Assistant IA", purpose: "Répondre aux visiteurs" },
|
|
10
|
+
];
|
|
11
|
+
/** Les modules montrés tant que la personne n'a rien choisi. */
|
|
12
|
+
export const DEFAULT_MODULE_BAR = ["host", "mail", "status", "analytics"];
|
|
13
|
+
export function isModuleWord(value) {
|
|
14
|
+
return BASALTE_MODULES.some((item) => item.word === value);
|
|
15
|
+
}
|
|
16
|
+
export function moduleByWord(word) {
|
|
17
|
+
const found = BASALTE_MODULES.find((item) => item.word === word);
|
|
18
|
+
if (!found)
|
|
19
|
+
throw new Error(`@hgdc/ui/modules : module inconnu « ${word} »`);
|
|
20
|
+
return found;
|
|
21
|
+
}
|
|
22
|
+
/** Ordre retenu (stockage, réseau) : les mots connus, sans doublon. `null` si ce n'est pas une liste. */
|
|
23
|
+
export function parseModuleBar(value) {
|
|
24
|
+
if (!Array.isArray(value))
|
|
25
|
+
return null;
|
|
26
|
+
return [...new Set(value.filter(isModuleWord))];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* La liste à montrer : l'ordre de la personne, plus le module ouvert s'il n'y est pas (on ne perd
|
|
30
|
+
* jamais de vue où l'on est).
|
|
31
|
+
*/
|
|
32
|
+
export function shownModules(order, current) {
|
|
33
|
+
return isModuleWord(current) && !order.includes(current) ? [...order, current] : [...order];
|
|
34
|
+
}
|
|
35
|
+
/** Les modules absents de la liste, dans l'ordre par défaut. */
|
|
36
|
+
export function hiddenModules(shown) {
|
|
37
|
+
return BASALTE_MODULES.filter((item) => !shown.includes(item.word));
|
|
38
|
+
}
|
|
39
|
+
/** Déplace l'entrée `from` à la place `to` ; hors limites, la liste reste telle quelle. */
|
|
40
|
+
export function moveModule(list, from, to) {
|
|
41
|
+
if (from < 0 || from >= list.length || to < 0 || to >= list.length)
|
|
42
|
+
return [...list];
|
|
43
|
+
const next = [...list];
|
|
44
|
+
const [item] = next.splice(from, 1);
|
|
45
|
+
next.splice(to, 0, item);
|
|
46
|
+
return next;
|
|
47
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* La place de l'organisation tant que le produit ne l'a pas (ses données arrivent) : la marque de
|
|
3
|
+
* la dernière organisation affichée sous cette origine, au pixel près de celle d'`OrgSwitcher`
|
|
4
|
+
* (initiales ou logo, posés sur `<html>` par `sidebarInitScript` avant la première peinture),
|
|
5
|
+
* sinon un carré de squelette de la même taille. Rien ne bouge quand la vraie arrive.
|
|
6
|
+
*/
|
|
7
|
+
export declare function OrgPlaceholder(): React.JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* La place de l'organisation tant que le produit ne l'a pas (ses données arrivent) : la marque de
|
|
4
|
+
* la dernière organisation affichée sous cette origine, au pixel près de celle d'`OrgSwitcher`
|
|
5
|
+
* (initiales ou logo, posés sur `<html>` par `sidebarInitScript` avant la première peinture),
|
|
6
|
+
* sinon un carré de squelette de la même taille. Rien ne bouge quand la vraie arrive.
|
|
7
|
+
*/
|
|
8
|
+
export function OrgPlaceholder() {
|
|
9
|
+
return (_jsx("div", { "data-slot": "org-placeholder", "aria-hidden": true, className: "flex h-10 min-w-0 flex-1 items-center px-1.5 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" }) }));
|
|
10
|
+
}
|
package/dist/org-switcher.d.ts
CHANGED
|
@@ -31,10 +31,8 @@ export declare function OrgMark({ org, className, }: {
|
|
|
31
31
|
className?: string;
|
|
32
32
|
}): React.JSX.Element;
|
|
33
33
|
/**
|
|
34
|
-
* L'organisation active, et le menu pour en changer ou en créer une. En haut de la barre
|
|
35
|
-
*
|
|
36
|
-
* téléphone, ou en haut de la barre latérale quand le produit coupe la barre des produits.
|
|
37
|
-
* Présentationnel : le produit passe la liste, l'active et ce qu'il faut faire au choix (changer
|
|
34
|
+
* L'organisation active, et le menu pour en changer ou en créer une. En haut de la barre latérale
|
|
35
|
+
* d'`AppShell` (la marque seule en rail) et du tiroir sur téléphone. Présentationnel : le produit passe la liste, l'active et ce qu'il faut faire au choix (changer
|
|
38
36
|
* l'URL `/o/<slug>/…`), le kit n'appelle rien.
|
|
39
37
|
*/
|
|
40
38
|
export declare function OrgSwitcher({ organizations, activeId, onSelect, createHref, onCreate, className, }: {
|
package/dist/org-switcher.js
CHANGED
|
@@ -9,7 +9,6 @@ import { Badge } from "./badge.js";
|
|
|
9
9
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "./dropdown-menu.js";
|
|
10
10
|
import { AVATAR_TONE_CLASSES, initials, orgTone } from "./initials.js";
|
|
11
11
|
import { rememberShellOrganization } from "./shell-memory.js";
|
|
12
|
-
import { useShellPlacement } from "./shell-placement.js";
|
|
13
12
|
import { cn } from "./cn.js";
|
|
14
13
|
export const ORG_ROLE_LABEL = {
|
|
15
14
|
owner: "Propriétaire",
|
|
@@ -36,14 +35,11 @@ export function OrgMark({ org, className, }) {
|
|
|
36
35
|
return (_jsx("span", { "aria-hidden": true, className: cn("grid size-8 shrink-0 place-items-center rounded-lg text-xs font-semibold uppercase", AVATAR_TONE_CLASSES[orgTone(org)], className), children: initials(org.name) }));
|
|
37
36
|
}
|
|
38
37
|
/**
|
|
39
|
-
* L'organisation active, et le menu pour en changer ou en créer une. En haut de la barre
|
|
40
|
-
*
|
|
41
|
-
* téléphone, ou en haut de la barre latérale quand le produit coupe la barre des produits.
|
|
42
|
-
* Présentationnel : le produit passe la liste, l'active et ce qu'il faut faire au choix (changer
|
|
38
|
+
* L'organisation active, et le menu pour en changer ou en créer une. En haut de la barre latérale
|
|
39
|
+
* d'`AppShell` (la marque seule en rail) et du tiroir sur téléphone. Présentationnel : le produit passe la liste, l'active et ce qu'il faut faire au choix (changer
|
|
43
40
|
* l'URL `/o/<slug>/…`), le kit n'appelle rien.
|
|
44
41
|
*/
|
|
45
42
|
export function OrgSwitcher({ organizations, activeId, onSelect, createHref, onCreate, className, }) {
|
|
46
|
-
const placement = useShellPlacement();
|
|
47
43
|
const active = organizations.find((org) => org.id === activeId);
|
|
48
44
|
// Retenue pour la première image du prochain produit ouvert sous la même origine (ADR 0020).
|
|
49
45
|
const activeName = active?.name;
|
|
@@ -54,13 +50,5 @@ export function OrgSwitcher({ organizations, activeId, onSelect, createHref, onC
|
|
|
54
50
|
}, [activeId, activeName, activeLogo]);
|
|
55
51
|
if (!active)
|
|
56
52
|
return null;
|
|
57
|
-
|
|
58
|
-
const inRail = placement === "product-rail";
|
|
59
|
-
return (_jsxs(DropdownMenu, { children: [_jsxs(DropdownMenuTrigger, { render: _jsx("button", { type: "button", "aria-label": `Organisation ${active.name}, changer d'organisation`, title: active.name, className: cn("flex min-h-10 w-full min-w-0 items-center gap-2.5 rounded-xl px-1.5 py-1 text-left coarse:min-h-11 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", inRail
|
|
60
|
-
? "h-10 shrink-0 gap-3 px-1 py-0 hover:bg-shell-hover aria-expanded:bg-shell-hover coarse:h-11"
|
|
61
|
-
: "lg:hover:bg-shell-hover lg:aria-expanded:bg-shell-hover lg:rail:w-auto lg:rail:justify-center lg:rail:px-1", className) }), children: [_jsx(OrgMark, { org: active }), _jsxs("span", { className: cn("min-w-0 flex-1", inRail
|
|
62
|
-
? "opacity-0 transition-opacity duration-(--duration-fast) ease-out group-data-expanded/rail:opacity-100 motion-reduce:transition-none"
|
|
63
|
-
: "lg:rail:hidden"), children: [_jsx("span", { className: cn("block text-sm font-medium", inRail ? "truncate" : "line-clamp-2 break-words"), children: active.name }), active.role ? (_jsx("span", { className: "block truncate text-xs text-muted-foreground lg:text-shell-muted", children: ORG_ROLE_LABEL[active.role] })) : null] }), _jsx(HugeiconsIcon, { icon: UnfoldMoreIcon, "aria-hidden": true, className: cn("size-4 shrink-0 text-muted-foreground lg:text-shell-muted", inRail
|
|
64
|
-
? "opacity-0 transition-opacity duration-(--duration-fast) group-data-expanded/rail:opacity-100"
|
|
65
|
-
: "lg:rail:hidden") })] }), _jsxs(DropdownMenuContent, { align: "start", className: "w-64", children: [_jsxs(DropdownMenuGroup, { children: [_jsx(DropdownMenuLabel, { children: "Organisations" }), organizations.map((org) => (_jsxs(DropdownMenuItem, { onClick: () => onSelect(org.id), children: [_jsx(OrgMark, { org: org, className: "size-6 rounded-md text-[10px]" }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: org.name }), org.id === activeId ? (_jsx(HugeiconsIcon, { icon: Tick02Icon, "aria-label": "Organisation active", className: "size-4" })) : org.role ? (_jsx("span", { className: "text-xs font-normal text-muted-foreground", children: ORG_ROLE_LABEL[org.role] })) : null] }, org.id)))] }), createHref !== undefined ? (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(MenuPrimitive.LinkItem, { href: createHref, className: MENU_LINK_ITEM, children: [_jsx(HugeiconsIcon, { icon: PlusSignIcon, "aria-hidden": true }), "Cr\u00E9er une organisation"] })] })) : onCreate !== undefined ? (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onClick: onCreate, children: [_jsx(HugeiconsIcon, { icon: PlusSignIcon, "aria-hidden": true }), "Cr\u00E9er une organisation"] })] })) : null] })] }));
|
|
53
|
+
return (_jsxs(DropdownMenu, { children: [_jsxs(DropdownMenuTrigger, { render: _jsx("button", { type: "button", "aria-label": `Organisation ${active.name}, changer d'organisation`, title: active.name, className: cn("flex min-h-10 w-full min-w-0 items-center gap-2.5 rounded-xl px-1.5 py-1 text-left coarse:min-h-11 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:w-auto lg:rail:justify-center lg:rail:px-1", className) }), children: [_jsx(OrgMark, { org: active }), _jsxs("span", { className: "min-w-0 flex-1 lg:rail:hidden", children: [_jsx("span", { className: "line-clamp-2 block text-sm font-medium break-words", children: active.name }), active.role ? (_jsx("span", { className: "block truncate text-xs text-muted-foreground lg:text-shell-muted", children: ORG_ROLE_LABEL[active.role] })) : null] }), _jsx(HugeiconsIcon, { icon: UnfoldMoreIcon, "aria-hidden": true, className: "size-4 shrink-0 text-muted-foreground lg:text-shell-muted lg:rail:hidden" })] }), _jsxs(DropdownMenuContent, { align: "start", className: "w-64", children: [_jsxs(DropdownMenuGroup, { children: [_jsx(DropdownMenuLabel, { children: "Organisations" }), organizations.map((org) => (_jsxs(DropdownMenuItem, { onClick: () => onSelect(org.id), children: [_jsx(OrgMark, { org: org, className: "size-6 rounded-md text-[10px]" }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: org.name }), org.id === activeId ? (_jsx(HugeiconsIcon, { icon: Tick02Icon, "aria-label": "Organisation active", className: "size-4" })) : org.role ? (_jsx("span", { className: "text-xs font-normal text-muted-foreground", children: ORG_ROLE_LABEL[org.role] })) : null] }, org.id)))] }), createHref !== undefined ? (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(MenuPrimitive.LinkItem, { href: createHref, className: MENU_LINK_ITEM, children: [_jsx(HugeiconsIcon, { icon: PlusSignIcon, "aria-hidden": true }), "Cr\u00E9er une organisation"] })] })) : onCreate !== undefined ? (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onClick: onCreate, children: [_jsx(HugeiconsIcon, { icon: PlusSignIcon, "aria-hidden": true }), "Cr\u00E9er une organisation"] })] })) : null] })] }));
|
|
66
54
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { HGDC_ACCOUNT, HGDC_PRODUCTS } from "./products.js";
|
|
2
|
+
/** Nom affiché d'un produit de la famille (« E-mails » pour `mail`), Account compris. */
|
|
3
|
+
export function familyProductName(product) {
|
|
4
|
+
if (product === HGDC_ACCOUNT.badge)
|
|
5
|
+
return HGDC_ACCOUNT.name;
|
|
6
|
+
return HGDC_PRODUCTS.find((item) => item.badge === product)?.name ?? product;
|
|
7
|
+
}
|
package/dist/products.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export declare const PRODUCT_STAGE_LABEL: Readonly<Record<ProductStage, string>>
|
|
|
9
9
|
export type HgdcCatalogProduct = {
|
|
10
10
|
/** Le mot du produit, qui sert de sous-domaine : « Host ». */
|
|
11
11
|
word: string;
|
|
12
|
-
/** Nom
|
|
12
|
+
/** Nom affiché : celui du module de Basalte (« Déploiements » pour `host`), sinon le mot seul. */
|
|
13
13
|
name: string;
|
|
14
14
|
/** « host.hgdc.ch ». */
|
|
15
15
|
subdomain: string;
|
|
@@ -108,7 +108,7 @@ export declare function productMenuEntries(options?: {
|
|
|
108
108
|
*/
|
|
109
109
|
export declare function sameOriginPath(href: string, pageOrigin: string | undefined): string | null;
|
|
110
110
|
/** Une entrée de la barre des produits d'`AppShell`. */
|
|
111
|
-
export type
|
|
111
|
+
export type ShellProductEntry = {
|
|
112
112
|
name: string;
|
|
113
113
|
href: string;
|
|
114
114
|
badge: FamilyProduct;
|
|
@@ -132,14 +132,14 @@ export type ProductRailEntry = {
|
|
|
132
132
|
* (`PRODUCT_ORIGINS`, `app.hgdc.ch/<produit>`) reçoit un lien relatif et `sameOrigin` (ADR 0020),
|
|
133
133
|
* et le produit courant sa base (`/mail/`) plutôt que `/`.
|
|
134
134
|
*/
|
|
135
|
-
export declare function
|
|
135
|
+
export declare function shellProductEntries(options?: {
|
|
136
136
|
current?: string | undefined;
|
|
137
137
|
accountHref?: string | undefined;
|
|
138
138
|
catalog?: ReadonlyArray<HgdcCatalogProduct> | undefined;
|
|
139
139
|
pageOrigin?: string | undefined;
|
|
140
140
|
}): {
|
|
141
|
-
products: Array<
|
|
142
|
-
account:
|
|
141
|
+
products: Array<ShellProductEntry>;
|
|
142
|
+
account: ShellProductEntry;
|
|
143
143
|
};
|
|
144
144
|
/**
|
|
145
145
|
* Lien vers l'autre vue du même produit (ADR 0014) : depuis `app.hgdc.ch/<mot>`, « Ouvrir seul » vers
|
package/dist/products.js
CHANGED
|
@@ -48,7 +48,7 @@ export function productEntryHref(word, origins = PRODUCT_ORIGINS) {
|
|
|
48
48
|
*/
|
|
49
49
|
export const PRODUCT_ENTRY_REFRESH_QUERY = "refresh=background&next=/";
|
|
50
50
|
function product(word, pitch, icon, stage,
|
|
51
|
-
/** Nom affiché quand il diffère du mot de l'adresse (
|
|
51
|
+
/** Nom affiché quand il diffère du mot de l'adresse : le nom du module (« Déploiements » pour `host`). */
|
|
52
52
|
name = word) {
|
|
53
53
|
const badge = word.toLowerCase();
|
|
54
54
|
if (!isFamilyProduct(badge))
|
|
@@ -76,21 +76,21 @@ export const HGDC_ACCOUNT = { ...account, entryHref: account.href };
|
|
|
76
76
|
export const HGDC_MANAGER = product("Manager", "Tes organisations, leurs membres et leurs rôles.", UserGroupIcon, "live");
|
|
77
77
|
/** Les produits de la famille, dans leur ordre d'affichage. Account n'en fait pas partie (`HGDC_ACCOUNT`). */
|
|
78
78
|
export const HGDC_PRODUCTS = [
|
|
79
|
-
// Basalte est le
|
|
80
|
-
product("Host", "Ton site en ligne, déployé à chaque modification.", Rocket01Icon, "live", "
|
|
81
|
-
product("Mail", "Tes e-mails, newsletters et formulaires, envoyés depuis ton adresse.", Mail01Icon, "live"),
|
|
79
|
+
// Basalte est le produit ; chacun de ceux-ci y est un module, sous son nom de fonction. Adresses, pastille et clés gardent le mot.
|
|
80
|
+
product("Host", "Ton site en ligne, déployé à chaque modification.", Rocket01Icon, "live", "Déploiements"),
|
|
81
|
+
product("Mail", "Tes e-mails, newsletters et formulaires, envoyés depuis ton adresse.", Mail01Icon, "live", "E-mails"),
|
|
82
82
|
// Masqué temporairement le 2026-11-03 ; la console reste en ligne.
|
|
83
83
|
product("Portal", "Un espace partagé où ton client suit son projet, signe et paie.", Briefcase01Icon, "soon"),
|
|
84
|
-
product("Status", "La surveillance de tes sites et ta page de statut.", Activity01Icon, "live"),
|
|
85
|
-
product("Analytics", "Tes visiteurs mesurés, sans cookies ni bannière.", Analytics01Icon, "live"),
|
|
86
|
-
product("Docs", "Ta documentation en ligne, avec un assistant qui y répond.", BookOpen01Icon, "live"),
|
|
87
|
-
product("IAssistant", "Une bulle de chat IA sur ton site, qui répond à tes visiteurs.", BubbleChatIcon, "preview"),
|
|
84
|
+
product("Status", "La surveillance de tes sites et ta page de statut.", Activity01Icon, "live", "Surveillance"),
|
|
85
|
+
product("Analytics", "Tes visiteurs mesurés, sans cookies ni bannière.", Analytics01Icon, "live", "Statistiques"),
|
|
86
|
+
product("Docs", "Ta documentation en ligne, avec un assistant qui y répond.", BookOpen01Icon, "live", "Documentation"),
|
|
87
|
+
product("IAssistant", "Une bulle de chat IA sur ton site, qui répond à tes visiteurs.", BubbleChatIcon, "preview", "Assistant IA"),
|
|
88
88
|
id(),
|
|
89
89
|
HGDC_MANAGER,
|
|
90
90
|
];
|
|
91
91
|
/** ID, le compte de connexion ouvert aux applications tierces : pas d'entrée `/auth/hgdc`, sa racine. */
|
|
92
92
|
function id() {
|
|
93
|
-
const item = product("ID", "Ton compte pour te connecter à tes applications.", UserCircleIcon, "live");
|
|
93
|
+
const item = product("ID", "Ton compte pour te connecter à tes applications.", UserCircleIcon, "live", "Connexion");
|
|
94
94
|
return { ...item, entryHref: item.href };
|
|
95
95
|
}
|
|
96
96
|
/** Vrai si le produit répond déjà (en service ou en préversion). */
|
|
@@ -141,7 +141,7 @@ export function sameOriginPath(href, pageOrigin) {
|
|
|
141
141
|
* (`PRODUCT_ORIGINS`, `app.hgdc.ch/<produit>`) reçoit un lien relatif et `sameOrigin` (ADR 0020),
|
|
142
142
|
* et le produit courant sa base (`/mail/`) plutôt que `/`.
|
|
143
143
|
*/
|
|
144
|
-
export function
|
|
144
|
+
export function shellProductEntries(options = {}) {
|
|
145
145
|
const current = options.current?.toLowerCase();
|
|
146
146
|
const { pageOrigin } = options;
|
|
147
147
|
const entry = (item, href, product) => {
|
package/dist/shell-frame.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
/** Nom affiché d'un produit de la famille (« Mail »), Account compris. */
|
|
3
|
-
export declare function familyProductName(product: FamilyProduct): string;
|
|
1
|
+
export { familyProductName } from "./product-name.ts";
|
|
4
2
|
/** Haut de la barre latérale (marque du produit) : le même dans la coque et dans le cadre. */
|
|
5
3
|
export declare const SIDEBAR_HEAD = "mt-2 flex h-14 shrink-0 items-center gap-1 px-3";
|
|
6
4
|
export declare const SIDEBAR_BRAND = "flex min-w-0 flex-1 items-center gap-2.5 pl-2.25";
|
|
@@ -9,13 +7,12 @@ export declare const SIDEBAR_BRAND_NAME = "truncate font-heading text-[0.9375rem
|
|
|
9
7
|
export declare const SIDEBAR_RAIL_BOX = "grid h-9 w-10 shrink-0 place-items-center rounded-xl";
|
|
10
8
|
/**
|
|
11
9
|
* Le cadre de la coque, dessiné par le chargeur du document (`FamilyLoaderRoot`) quand une page
|
|
12
|
-
* attend hors de la coque :
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* prend la place sans saut.
|
|
10
|
+
* attend hors de la coque : organisation retenue, Manager, retour aux modules, nom du module,
|
|
11
|
+
* navigation en squelette à sa hauteur retenue, carte du contenu et sa barre du haut. Mêmes boîtes
|
|
12
|
+
* que la coque, au pixel : la transition de vue d'un module à l'autre n'a rien à faire bouger, et
|
|
13
|
+
* la coque qui monte ensuite prend la place sans saut.
|
|
17
14
|
*
|
|
18
15
|
* Le produit vient de l'adresse (`<html data-hgdc-product>`, `sidebarInitScript`) : le rendu
|
|
19
|
-
* serveur dessine
|
|
16
|
+
* serveur dessine tous les noms, le CSS montre le bon.
|
|
20
17
|
*/
|
|
21
18
|
export declare function ShellFrame(): React.JSX.Element;
|
package/dist/shell-frame.js
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { FAMILY_PRODUCTS } from "./family-svg.js";
|
|
3
|
+
import { OrgPlaceholder } from "./org-placeholder.js";
|
|
3
4
|
import { ProductBadge } from "./product-badge.js";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
/** Nom affiché d'un produit de la famille (« Mail »), Account compris. */
|
|
7
|
-
export function familyProductName(product) {
|
|
8
|
-
if (product === HGDC_ACCOUNT.badge)
|
|
9
|
-
return HGDC_ACCOUNT.name;
|
|
10
|
-
return HGDC_PRODUCTS.find((item) => item.badge === product)?.name ?? `HGDC ${product}`;
|
|
11
|
-
}
|
|
5
|
+
import { familyProductName } from "./product-name.js";
|
|
6
|
+
export { familyProductName } from "./product-name.js";
|
|
12
7
|
/** Haut de la barre latérale (marque du produit) : le même dans la coque et dans le cadre. */
|
|
13
8
|
export const SIDEBAR_HEAD = "mt-2 flex h-14 shrink-0 items-center gap-1 px-3";
|
|
14
9
|
export const SIDEBAR_BRAND = "flex min-w-0 flex-1 items-center gap-2.5 pl-2.25";
|
|
@@ -17,17 +12,18 @@ export const SIDEBAR_BRAND_NAME = "truncate font-heading text-[0.9375rem] font-s
|
|
|
17
12
|
export const SIDEBAR_RAIL_BOX = "grid h-9 w-10 shrink-0 place-items-center rounded-xl";
|
|
18
13
|
/** Lignes de squelette de la navigation, coupées à la hauteur retenue (`--hgdc-nav-height`). */
|
|
19
14
|
const SKELETON_ROWS = 12;
|
|
15
|
+
/** Une ligne de la barre sans lien : même boîte qu'une entrée. */
|
|
16
|
+
const FRAME_ROW = "flex h-9 shrink-0 items-center gap-2.5 px-3 text-sm font-medium text-shell-muted";
|
|
20
17
|
/**
|
|
21
18
|
* Le cadre de la coque, dessiné par le chargeur du document (`FamilyLoaderRoot`) quand une page
|
|
22
|
-
* attend hors de la coque :
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* prend la place sans saut.
|
|
19
|
+
* attend hors de la coque : organisation retenue, Manager, retour aux modules, nom du module,
|
|
20
|
+
* navigation en squelette à sa hauteur retenue, carte du contenu et sa barre du haut. Mêmes boîtes
|
|
21
|
+
* que la coque, au pixel : la transition de vue d'un module à l'autre n'a rien à faire bouger, et
|
|
22
|
+
* la coque qui monte ensuite prend la place sans saut.
|
|
27
23
|
*
|
|
28
24
|
* Le produit vient de l'adresse (`<html data-hgdc-product>`, `sidebarInitScript`) : le rendu
|
|
29
|
-
* serveur dessine
|
|
25
|
+
* serveur dessine tous les noms, le CSS montre le bon.
|
|
30
26
|
*/
|
|
31
27
|
export function ShellFrame() {
|
|
32
|
-
return (_jsxs("div", { "data-slot": "shell-frame", "aria-hidden": true, className: "hidden h-full lg:flex", children: [
|
|
28
|
+
return (_jsxs("div", { "data-slot": "shell-frame", "aria-hidden": true, className: "hidden h-full lg:flex", children: [_jsxs("div", { "data-slot": "shell-frame-sidebar", className: "flex w-64 shrink-0 flex-col overflow-hidden rail:w-16", children: [_jsx("div", { className: SIDEBAR_HEAD, children: _jsx(OrgPlaceholder, {}) }), _jsxs("div", { className: "px-3 pt-1", children: [_jsxs("div", { className: FRAME_ROW, children: [_jsx("span", { className: "size-[1.125rem] shrink-0 rounded-md bg-shell-hover" }), _jsx("span", { className: "rail:hidden", children: "Manager" })] }), _jsx("div", { className: "mx-2 mt-2 h-px bg-foreground/8" })] }), _jsxs("div", { className: "px-3 pt-2", children: [_jsxs("div", { className: `${FRAME_ROW} mb-2 rounded-xl bg-shell-hover text-shell-foreground`, children: [_jsx("span", { className: "size-4 shrink-0" }), _jsx("span", { className: "rail:hidden", children: "Retour aux modules" })] }), FAMILY_PRODUCTS.map((product) => (_jsx("div", { "data-product": product, className: "contents", children: _jsxs("div", { className: `${FRAME_ROW} font-semibold text-shell-foreground rail:hidden`, children: [_jsx(ProductBadge, { product: product, className: "size-[1.125rem] shrink-0" }), _jsx("span", { className: "truncate font-heading", children: familyProductName(product) })] }) }, product)))] }), _jsx("div", { className: "px-3 py-2", children: _jsx("div", { "data-slot": "shell-frame-nav", className: "grid gap-0.5 overflow-hidden", children: Array.from({ length: SKELETON_ROWS }, (_, index) => (_jsxs("div", { className: "flex h-9 shrink-0 items-center gap-2.5 px-3 rail:justify-center rail:px-0", children: [_jsx("span", { className: "size-[1.125rem] shrink-0 rounded-md bg-shell-hover" }), _jsx("span", { className: "h-2.5 w-24 rounded-full bg-shell-hover rail:hidden" })] }, index))) }) })] }), _jsx("div", { className: "min-w-0 flex-1 py-2 pr-2", children: _jsx("div", { className: "h-full overflow-hidden rounded-xl bg-background", children: _jsx("div", { "data-slot": "shell-frame-header", className: "min-h-14 border-b border-foreground/5 bg-background" }) }) })] }));
|
|
33
29
|
}
|
package/dist/shell-links.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import { type ProductView } from "./addresses.ts";
|
|
3
3
|
import type { FamilyProduct } from "./family-svg.ts";
|
|
4
|
+
import type { ShellMenuLinks } from "./shell-placement.ts";
|
|
4
5
|
/**
|
|
5
6
|
* Un accès commun de la barre : une adresse, ou un lien du routeur (`{ render: <Link to="/settings" /> }`,
|
|
6
7
|
* dont le `data-status="active"` marque la page ouverte), ou une adresse avec `active`.
|
|
@@ -10,7 +11,7 @@ export type ShellLink = string | {
|
|
|
10
11
|
render?: React.ReactElement;
|
|
11
12
|
active?: boolean;
|
|
12
13
|
};
|
|
13
|
-
/** Les trois accès
|
|
14
|
+
/** Les trois accès communs : au-dessus du compte ou dans son menu, selon la coque (`AppShell`). */
|
|
14
15
|
export type AppShellLinks = {
|
|
15
16
|
/** Documentation du produit. */
|
|
16
17
|
documentation?: ShellLink;
|
|
@@ -25,6 +26,8 @@ export declare function ShellCommonLinks({ links, statusPageUrl, otherView, }: {
|
|
|
25
26
|
/** Lien vers l'autre vue du produit (ADR 0014), en dernier ; il ne suffit pas à montrer la liste. */
|
|
26
27
|
otherView?: ReactNode;
|
|
27
28
|
}): React.JSX.Element | null;
|
|
29
|
+
/** L'autre vue, pour le menu du compte. `undefined` pour un produit qui n'en a pas. */
|
|
30
|
+
export declare function otherViewMenuLink(product: FamilyProduct, view: ProductView): ShellMenuLinks["otherView"];
|
|
28
31
|
/**
|
|
29
32
|
* « Ouvrir seul » (vers `<mot>.hgdc.ch`) ou « Ouvrir dans app.hgdc.ch », discret, sous les accès communs.
|
|
30
33
|
* Le chemin courant n'est lu qu'au clic : le lien rendu par le serveur mène à l'accueil.
|
package/dist/shell-links.js
CHANGED
|
@@ -30,6 +30,17 @@ export function ShellCommonLinks({ links, statusPageUrl, otherView, }) {
|
|
|
30
30
|
return null;
|
|
31
31
|
return (_jsx("nav", { "aria-label": "Acc\u00E8s communs", children: _jsxs("ul", { "data-slot": "app-shell-links", className: "grid gap-0.5", children: [items, otherView] }) }));
|
|
32
32
|
}
|
|
33
|
+
function otherViewClick(product, view, fallback) {
|
|
34
|
+
return (event) => {
|
|
35
|
+
const path = soloHref(`/${product}`, `${window.location.pathname}${window.location.search}`);
|
|
36
|
+
event.currentTarget.href = otherViewHref(product, view, path) ?? fallback;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** L'autre vue, pour le menu du compte. `undefined` pour un produit qui n'en a pas. */
|
|
40
|
+
export function otherViewMenuLink(product, view) {
|
|
41
|
+
const href = otherViewHref(product, view);
|
|
42
|
+
return href ? { label: otherViewLabel(view), href, onClick: otherViewClick(product, view, href) } : undefined;
|
|
43
|
+
}
|
|
33
44
|
/**
|
|
34
45
|
* « Ouvrir seul » (vers `<mot>.hgdc.ch`) ou « Ouvrir dans app.hgdc.ch », discret, sous les accès communs.
|
|
35
46
|
* Le chemin courant n'est lu qu'au clic : le lien rendu par le serveur mène à l'accueil.
|
|
@@ -38,9 +49,5 @@ export function OtherViewLink({ product, view, }) {
|
|
|
38
49
|
const href = otherViewHref(product, view);
|
|
39
50
|
if (!href)
|
|
40
51
|
return null;
|
|
41
|
-
|
|
42
|
-
const path = soloHref(`/${product}`, `${window.location.pathname}${window.location.search}`);
|
|
43
|
-
event.currentTarget.href = otherViewHref(product, view, path) ?? href;
|
|
44
|
-
};
|
|
45
|
-
return (_jsx(SidebarNavItem, { icon: LinkSquare02Icon, label: otherViewLabel(view), render: _jsx("a", { href: href, onClick: onClick }) }));
|
|
52
|
+
return (_jsx(SidebarNavItem, { icon: LinkSquare02Icon, label: otherViewLabel(view), render: _jsx("a", { href: href, onClick: otherViewClick(product, view, href) }) }));
|
|
46
53
|
}
|
package/dist/shell-memory.d.ts
CHANGED
|
@@ -4,10 +4,6 @@ export declare const SHELL_ORG_KEY = "hgdc-shell:org";
|
|
|
4
4
|
export declare const SHELL_NAV_KEY_PREFIX = "hgdc-shell:nav:";
|
|
5
5
|
/** Dernier produit ouvert (son mot), en `localStorage` : l'accueil d'`app.hgdc.ch` y renvoie. */
|
|
6
6
|
export declare const SHELL_LAST_PRODUCT_KEY = "hgdc-shell:last";
|
|
7
|
-
/** Passage d'un produit à l'autre (horodatage), en `sessionStorage`. */
|
|
8
|
-
export declare const SHELL_SWITCH_KEY = "hgdc-shell:switch";
|
|
9
|
-
/** Au-delà, un passage noté n'est plus celui qu'on vit (onglet laissé, retour bien plus tard). */
|
|
10
|
-
export declare const SHELL_SWITCH_MAX_AGE_MS = 10000;
|
|
11
7
|
export type ShellOrganization = {
|
|
12
8
|
initials: string;
|
|
13
9
|
logo?: string;
|
|
@@ -67,8 +63,4 @@ export declare function forgetShellOrganization(): void;
|
|
|
67
63
|
export declare function rememberShellNavHeight(product: string, height: number): void;
|
|
68
64
|
/** Retient le produit affiché (`AppShell` le fait), pour que l'accueil d'`app.hgdc.ch` y renvoie. */
|
|
69
65
|
export declare function rememberLastProduct(product: string): void;
|
|
70
|
-
/** Note le départ vers un autre produit (clic dans la barre des produits). */
|
|
71
|
-
export declare function markProductSwitch(now?: number): void;
|
|
72
|
-
/** Lit et efface la note : vrai si la page vient d'un passage depuis la barre, il y a peu. */
|
|
73
|
-
export declare function takeProductSwitch(now?: number): boolean;
|
|
74
66
|
export {};
|
package/dist/shell-memory.js
CHANGED
|
@@ -3,13 +3,11 @@ import { FAMILY_PRODUCTS } from "./family-svg.js";
|
|
|
3
3
|
import { PRODUCT_ORIGINS } from "./products.js";
|
|
4
4
|
/*
|
|
5
5
|
* Mémoire de la coque, partagée par les produits servis sous la même origine (`app.hgdc.ch/<produit>`,
|
|
6
|
-
* ADR 0020) : ce qu'il faut pour que la première image d'un produit ait déjà
|
|
7
|
-
*
|
|
6
|
+
* ADR 0020) : ce qu'il faut pour que la première image d'un produit ait déjà l'organisation
|
|
7
|
+
* et la barre latérale à sa taille, avant que ses données n'arrivent.
|
|
8
8
|
*
|
|
9
9
|
* - l'organisation affichée en dernier : ses initiales et son logo (rien d'autre, pas le nom) ;
|
|
10
10
|
* - la hauteur de la navigation de chaque produit, pour un squelette à la bonne taille ;
|
|
11
|
-
* - le passage en cours d'un produit à l'autre (session de l'onglet), pour que la barre d'arrivée
|
|
12
|
-
* ne s'élargisse pas sous une souris restée immobile.
|
|
13
11
|
*
|
|
14
12
|
* `shellBoot` applique la mémoire sur `<html>` avant la première peinture : il tourne en ligne
|
|
15
13
|
* dans le `<head>` (`sidebarInitScript`), donc il ne dépend de rien d'autre que de ses arguments.
|
|
@@ -20,10 +18,6 @@ export const SHELL_ORG_KEY = "hgdc-shell:org";
|
|
|
20
18
|
export const SHELL_NAV_KEY_PREFIX = "hgdc-shell:nav:";
|
|
21
19
|
/** Dernier produit ouvert (son mot), en `localStorage` : l'accueil d'`app.hgdc.ch` y renvoie. */
|
|
22
20
|
export const SHELL_LAST_PRODUCT_KEY = "hgdc-shell:last";
|
|
23
|
-
/** Passage d'un produit à l'autre (horodatage), en `sessionStorage`. */
|
|
24
|
-
export const SHELL_SWITCH_KEY = "hgdc-shell:switch";
|
|
25
|
-
/** Au-delà, un passage noté n'est plus celui qu'on vit (onglet laissé, retour bien plus tard). */
|
|
26
|
-
export const SHELL_SWITCH_MAX_AGE_MS = 10_000;
|
|
27
21
|
/**
|
|
28
22
|
* Applique la mémoire de la coque sur `<html>` : `data-sidebar="rail"`, `data-hgdc-product`
|
|
29
23
|
* (le produit de l'adresse), `data-hgdc-org` et les variables `--hgdc-org-initials`,
|
|
@@ -127,12 +121,12 @@ export function shellBootScript(config = SHELL_BOOT_CONFIG) {
|
|
|
127
121
|
function browser() {
|
|
128
122
|
return typeof window === "undefined" ? null : window;
|
|
129
123
|
}
|
|
130
|
-
function storageSet(
|
|
124
|
+
function storageSet(key, value) {
|
|
131
125
|
try {
|
|
132
126
|
if (value === null)
|
|
133
|
-
window
|
|
127
|
+
window.localStorage.removeItem(key);
|
|
134
128
|
else
|
|
135
|
-
window
|
|
129
|
+
window.localStorage.setItem(key, value);
|
|
136
130
|
}
|
|
137
131
|
catch {
|
|
138
132
|
// Stockage bloqué : la première image d'un autre produit montrera un squelette, rien de plus.
|
|
@@ -147,7 +141,7 @@ export function rememberShellOrganization(org) {
|
|
|
147
141
|
const win = browser();
|
|
148
142
|
if (!win)
|
|
149
143
|
return;
|
|
150
|
-
storageSet(
|
|
144
|
+
storageSet(SHELL_ORG_KEY, JSON.stringify(shellOrganization(org)));
|
|
151
145
|
shellBoot(SHELL_BOOT_CONFIG, win);
|
|
152
146
|
}
|
|
153
147
|
/** Oublie l'organisation (déconnexion) : la page suivante montre un squelette. */
|
|
@@ -155,39 +149,18 @@ export function forgetShellOrganization() {
|
|
|
155
149
|
const win = browser();
|
|
156
150
|
if (!win)
|
|
157
151
|
return;
|
|
158
|
-
storageSet(
|
|
152
|
+
storageSet(SHELL_ORG_KEY, null);
|
|
159
153
|
shellBoot(SHELL_BOOT_CONFIG, win);
|
|
160
154
|
}
|
|
161
155
|
/** Retient la hauteur de la navigation d'un produit (`AppShell` le fait après l'affichage). */
|
|
162
156
|
export function rememberShellNavHeight(product, height) {
|
|
163
157
|
if (!browser() || !(height > 0))
|
|
164
158
|
return;
|
|
165
|
-
storageSet(
|
|
159
|
+
storageSet(SHELL_NAV_KEY_PREFIX + product, String(Math.round(height)));
|
|
166
160
|
}
|
|
167
161
|
/** Retient le produit affiché (`AppShell` le fait), pour que l'accueil d'`app.hgdc.ch` y renvoie. */
|
|
168
162
|
export function rememberLastProduct(product) {
|
|
169
163
|
if (!browser())
|
|
170
164
|
return;
|
|
171
|
-
storageSet(
|
|
172
|
-
}
|
|
173
|
-
/** Note le départ vers un autre produit (clic dans la barre des produits). */
|
|
174
|
-
export function markProductSwitch(now = Date.now()) {
|
|
175
|
-
if (!browser())
|
|
176
|
-
return;
|
|
177
|
-
storageSet("sessionStorage", SHELL_SWITCH_KEY, String(now));
|
|
178
|
-
}
|
|
179
|
-
/** Lit et efface la note : vrai si la page vient d'un passage depuis la barre, il y a peu. */
|
|
180
|
-
export function takeProductSwitch(now = Date.now()) {
|
|
181
|
-
if (!browser())
|
|
182
|
-
return false;
|
|
183
|
-
let value = null;
|
|
184
|
-
try {
|
|
185
|
-
value = window.sessionStorage.getItem(SHELL_SWITCH_KEY);
|
|
186
|
-
window.sessionStorage.removeItem(SHELL_SWITCH_KEY);
|
|
187
|
-
}
|
|
188
|
-
catch {
|
|
189
|
-
return false;
|
|
190
|
-
}
|
|
191
|
-
const at = Number(value);
|
|
192
|
-
return value !== null && at > 0 && now - at >= 0 && now - at < SHELL_SWITCH_MAX_AGE_MS;
|
|
165
|
+
storageSet(SHELL_LAST_PRODUCT_KEY, product);
|
|
193
166
|
}
|
|
@@ -1,16 +1,22 @@
|
|
|
1
|
+
import type { ShellLink } from "./shell-links.tsx";
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
3
|
+
* Vrai quand la coque montre la barre de Basalte (Manager et modules) : le menu du compte ne répète
|
|
4
|
+
* pas les produits et reprend les accès d'aide (`ShellMenuLinksContext`).
|
|
4
5
|
*/
|
|
5
|
-
export
|
|
6
|
-
export declare
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
export declare const ModuleBarShownContext: React.Context<boolean>;
|
|
7
|
+
export declare function useModuleBarShown(): boolean;
|
|
8
|
+
/** Les accès que le menu du compte porte dans la barre de Basalte. */
|
|
9
|
+
export type ShellMenuLinks = {
|
|
10
|
+
documentation?: ShellLink | undefined;
|
|
11
|
+
status?: ShellLink | undefined;
|
|
12
|
+
otherView?: {
|
|
13
|
+
label: string;
|
|
14
|
+
href: string;
|
|
15
|
+
onClick: (event: React.MouseEvent<HTMLAnchorElement>) => void;
|
|
16
|
+
} | undefined;
|
|
17
|
+
};
|
|
18
|
+
export declare const ShellMenuLinksContext: React.Context<ShellMenuLinks>;
|
|
19
|
+
export declare function useShellMenuLinks(): ShellMenuLinks;
|
|
14
20
|
/**
|
|
15
21
|
* Prévient la coque qu'un lien vers un autre produit (ou une autre adresse servie en document
|
|
16
22
|
* complet) vient d'être suivi : elle couvre aussitôt la zone de contenu du H de chargement, le
|
package/dist/shell-placement.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { createContext, useContext } from "react";
|
|
2
|
-
export const ShellPlacementContext = createContext("sidebar");
|
|
3
|
-
export function useShellPlacement() {
|
|
4
|
-
return useContext(ShellPlacementContext);
|
|
5
|
-
}
|
|
6
2
|
/**
|
|
7
|
-
* Vrai quand la coque
|
|
8
|
-
*
|
|
3
|
+
* Vrai quand la coque montre la barre de Basalte (Manager et modules) : le menu du compte ne répète
|
|
4
|
+
* pas les produits et reprend les accès d'aide (`ShellMenuLinksContext`).
|
|
9
5
|
*/
|
|
10
|
-
export const
|
|
11
|
-
export function
|
|
12
|
-
return useContext(
|
|
6
|
+
export const ModuleBarShownContext = createContext(false);
|
|
7
|
+
export function useModuleBarShown() {
|
|
8
|
+
return useContext(ModuleBarShownContext);
|
|
9
|
+
}
|
|
10
|
+
export const ShellMenuLinksContext = createContext({});
|
|
11
|
+
export function useShellMenuLinks() {
|
|
12
|
+
return useContext(ShellMenuLinksContext);
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
15
|
* Prévient la coque qu'un lien vers un autre produit (ou une autre adresse servie en document
|
package/dist/sidebar-head.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
1
2
|
import type { FamilyProduct } from "./family-svg.ts";
|
|
2
3
|
/**
|
|
3
|
-
* Haut de la barre
|
|
4
|
+
* Haut de la barre de Basalte : l'organisation et le bouton « Réduire ». En rail, l'organisation
|
|
5
|
+
* se réduit à sa marque et le bouton « Déplier » passe dessous.
|
|
6
|
+
*/
|
|
7
|
+
export declare function ShellOrganizationHead({ organization }: {
|
|
8
|
+
organization: ReactNode;
|
|
9
|
+
}): React.JSX.Element;
|
|
10
|
+
/**
|
|
11
|
+
* Haut de la barre latérale d'une vue seule, sans barre de Basalte. Dépliée : la marque du produit, et le bouton « Réduire » à droite.
|
|
4
12
|
* En rail : la marque seule, qui devient le bouton « Déplier » au survol de la barre ou au focus,
|
|
5
13
|
* à la même place : le bouton ne descend jamais sous le logo. Les deux états sont dans la page et
|
|
6
14
|
* choisis par la variante CSS `rail:`, donc justes dès la première peinture.
|