@hgdc/ui 0.29.0 → 0.30.1
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.
|
@@ -28,7 +28,7 @@ export declare function ProjectMark({ project, className, }: {
|
|
|
28
28
|
* pendant le chargement : le déclencheur montre déjà `activeName` (le segment de l'URL), le menu
|
|
29
29
|
* reste utilisable pour « Tous les projets » et « Nouveau projet ».
|
|
30
30
|
*/
|
|
31
|
-
export declare function ProjectSwitcher({ projects, activeId, activeName, onSelect, allHref, createHref, onCreate, renderLink, createLabel, className, }: {
|
|
31
|
+
export declare function ProjectSwitcher({ projects, activeId, activeName, onSelect, allHref, createHref, onCreate, renderLink, noun, createLabel, className, }: {
|
|
32
32
|
projects: ReadonlyArray<SwitcherProject> | undefined;
|
|
33
33
|
/** Le projet ouvert ; `null` ou absent : « Tous les projets ». */
|
|
34
34
|
activeId?: string | null;
|
|
@@ -43,6 +43,11 @@ export declare function ProjectSwitcher({ projects, activeId, activeName, onSele
|
|
|
43
43
|
onCreate?: () => void;
|
|
44
44
|
/** Rend un lien du routeur du produit ; par défaut un `<a href>`. */
|
|
45
45
|
renderLink?: (href: string) => ReactElement;
|
|
46
|
+
/** Ce que l'on choisit, nom masculin : `{ one: "site", many: "sites" }` pour Analytics. */
|
|
47
|
+
noun?: {
|
|
48
|
+
one: string;
|
|
49
|
+
many: string;
|
|
50
|
+
};
|
|
46
51
|
createLabel?: string;
|
|
47
52
|
className?: string;
|
|
48
53
|
}): React.JSX.Element;
|
package/dist/project-switcher.js
CHANGED
|
@@ -39,10 +39,12 @@ function SwitcherEntry({ href, onSelect, renderLink, children, }) {
|
|
|
39
39
|
* pendant le chargement : le déclencheur montre déjà `activeName` (le segment de l'URL), le menu
|
|
40
40
|
* reste utilisable pour « Tous les projets » et « Nouveau projet ».
|
|
41
41
|
*/
|
|
42
|
-
export function ProjectSwitcher({ projects, activeId, activeName, onSelect, allHref, createHref, onCreate, renderLink,
|
|
42
|
+
export function ProjectSwitcher({ projects, activeId, activeName, onSelect, allHref, createHref, onCreate, renderLink, noun = { one: "projet", many: "projets" }, createLabel = `Nouveau ${noun.one}`, className, }) {
|
|
43
|
+
const capitalized = (word) => word.charAt(0).toUpperCase() + word.slice(1);
|
|
44
|
+
const all = `Tous les ${noun.many}`;
|
|
43
45
|
const hasActive = activeId !== undefined && activeId !== null;
|
|
44
46
|
const current = hasActive ? projects?.find((p) => p.id === activeId) : undefined;
|
|
45
|
-
const label = hasActive ? (current?.name ?? activeName ?? "") :
|
|
47
|
+
const label = hasActive ? (current?.name ?? activeName ?? "") : all;
|
|
46
48
|
const canCreate = createHref !== undefined || onCreate !== undefined;
|
|
47
|
-
return (_jsxs(DropdownMenu, { children: [_jsxs(DropdownMenuTrigger, { render: _jsx("button", { type: "button", "aria-label": hasActive ?
|
|
49
|
+
return (_jsxs(DropdownMenu, { children: [_jsxs(DropdownMenuTrigger, { render: _jsx("button", { type: "button", "aria-label": hasActive ? `${capitalized(noun.one)} ${label}, changer de ${noun.one}` : `${all}, changer de ${noun.one}`, className: cn("flex h-10 max-w-full min-w-0 items-center gap-2 rounded-xl px-2.5 text-sm font-medium 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 coarse:h-11", className) }), children: [hasActive ? (current ? (_jsx(ProjectMark, { project: current })) : (_jsx("span", { "aria-hidden": true, className: "size-6 shrink-0 rounded-md bg-muted skeleton-pulse" }))) : (_jsx(HugeiconsIcon, { icon: DashboardSquare01Icon, "aria-hidden": true, className: "size-4.5 shrink-0 text-muted-foreground" })), _jsx("span", { className: "min-w-0 truncate", children: label }), _jsx(HugeiconsIcon, { icon: UnfoldMoreIcon, "aria-hidden": true, className: "size-4 shrink-0 text-muted-foreground" })] }), _jsxs(DropdownMenuContent, { align: "start", className: "w-72 max-w-[calc(100vw-2rem)]", children: [_jsxs(SwitcherEntry, { href: allHref, onSelect: () => onSelect?.(null), renderLink: renderLink, children: [_jsx(HugeiconsIcon, { icon: DashboardSquare01Icon, "aria-hidden": true, className: "size-4" }), _jsx("span", { className: "flex-1", children: all }), hasActive ? null : _jsx(HugeiconsIcon, { icon: Tick02Icon, "aria-label": "Vue active", className: "size-4" })] }), projects?.length ? (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuGroup, { children: [_jsx(DropdownMenuLabel, { children: capitalized(noun.many) }), projects.map((project) => (_jsxs(SwitcherEntry, { href: project.href, onSelect: () => onSelect?.(project.id), renderLink: renderLink, children: [_jsx(ProjectMark, { project: project }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: project.name }), project.id === activeId ? (_jsx(HugeiconsIcon, { icon: Tick02Icon, "aria-label": `${capitalized(noun.one)} actif`, className: "size-4" })) : null] }, project.id)))] })] })) : null, canCreate ? (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(SwitcherEntry, { href: createHref, onSelect: onCreate, renderLink: renderLink, children: [_jsx(HugeiconsIcon, { icon: PlusSignIcon, "aria-hidden": true, className: "size-4" }), createLabel] })] })) : null] })] }));
|
|
48
50
|
}
|
package/dist/time-chart.js
CHANGED
|
@@ -27,6 +27,13 @@ export function niceScale(max, ticks = 4) {
|
|
|
27
27
|
const step = nice * magnitude;
|
|
28
28
|
return { max: Math.ceil(max / step) * step, step };
|
|
29
29
|
}
|
|
30
|
+
/** Lignes lues par les lecteurs d'écran au plus : au-delà, un échantillon régulier, premier et dernier inclus. */
|
|
31
|
+
const SR_SAMPLE_SIZE = 60;
|
|
32
|
+
function sampleIndices(n) {
|
|
33
|
+
if (n <= SR_SAMPLE_SIZE)
|
|
34
|
+
return Array.from({ length: n }, (_, i) => i);
|
|
35
|
+
return Array.from({ length: SR_SAMPLE_SIZE }, (_, k) => Math.round((k * (n - 1)) / (SR_SAMPLE_SIZE - 1)));
|
|
36
|
+
}
|
|
30
37
|
/**
|
|
31
38
|
* Série temporelle en SVG, sans bibliothèque : lignes (aire sous la courbe pour une série seule)
|
|
32
39
|
* ou barres empilées. Le SVG s'étire à la largeur de son conteneur ; les traits gardent 2 px
|
|
@@ -88,5 +95,5 @@ export function TimeChart({ timestamps, series, kind = "line", height = 200, com
|
|
|
88
95
|
return v > 0 ? (_jsx("rect", { x: i + 0.15, y: base, width: 0.7, height: h, fill: color(si), opacity: (active === null || active === i ? 1 : 0.55) * (i === incompleteIndex ? 0.5 : 1) }, `${s.key}-${i}`)) : null;
|
|
89
96
|
});
|
|
90
97
|
})
|
|
91
|
-
: series.map((s, si) => (_jsxs("g", { children: [series.length === 1 ? (_jsx("path", { d: areaPath(s.values, scale.max), fill: color(si), opacity: 0.1 })) : null, _jsx("path", { d: linePath(incompleteIndex < 0 ? s.values : s.values.map((v, i) => (i === incompleteIndex ? null : v)), scale.max), fill: "none", stroke: color(si), strokeWidth: 2, strokeLinejoin: "round", strokeLinecap: "round", vectorEffect: "non-scaling-stroke" }), incompleteIndex >= 0 ? (_jsx("path", { "data-slot": "time-chart-incomplete", d: linePath(s.values.map((v, i) => (i >= incompleteIndex - 1 ? v : null)), scale.max), fill: "none", stroke: color(si), strokeWidth: 2, strokeDasharray: "3 4", strokeLinecap: "round", vectorEffect: "non-scaling-stroke" })) : null] }, s.key))), active !== null ? (_jsx("line", { x1: active + 0.5, x2: active + 0.5, y1: 0, y2: 100, vectorEffect: "non-scaling-stroke", className: "stroke-foreground/25", strokeWidth: 1 })) : null] }), active !== null && activeTime !== undefined ? (_jsxs("div", { "data-slot": "time-chart-tooltip", className: cn("pointer-events-none absolute top-0 z-10 grid min-w-36 gap-1 rounded-xl bg-popover px-3 py-2 text-xs shadow-lg shadow-black/10 [--popover:var(--floating)]", (active + 0.5) / n > 0.6 ? "-translate-x-[calc(100%+0.75rem)]" : "translate-x-3"), style: { left: `${((active + 0.5) / n) * 100}%` }, children: [_jsxs("p", { className: "font-medium", children: [formatTime(activeTime), active === incompleteIndex ? (_jsx("span", { className: "ml-1.5 font-normal text-muted-foreground", children: "En cours" })) : null] }), series.map((s, si) => (_jsxs("p", { className: "flex items-center gap-2", children: [_jsx("span", { "aria-hidden": true, className: "size-2 shrink-0 rounded-[2px]", style: { background: color(si) } }), _jsx("span", { className: "flex-1 text-muted-foreground", children: s.label }), _jsx("span", { className: "font-medium tabular-nums", children: s.values[active] === null ? "-" : formatValue(s.values[active] ?? 0) })] }, s.key)))] })) : null] }), xLabels.length > 0 ? (_jsx("div", { "aria-hidden": true, className: "relative h-4 text-[11px] text-muted-foreground tabular-nums", children: xLabels.map((i, k) => (_jsx("span", { className: cn("absolute", k === 0 ? "left-0" : k === xLabels.length - 1 ? "right-0" : "-translate-x-1/2"), style: k === 0 || k === xLabels.length - 1 ? undefined : { left: `${((i + 0.5) / n) * 100}%` }, children: timestamps[i] !== undefined ? formatTime(timestamps[i]) : null }, i))) })) : null] })] }),
|
|
98
|
+
: series.map((s, si) => (_jsxs("g", { children: [series.length === 1 ? (_jsx("path", { d: areaPath(s.values, scale.max), fill: color(si), opacity: 0.1 })) : null, _jsx("path", { d: linePath(incompleteIndex < 0 ? s.values : s.values.map((v, i) => (i === incompleteIndex ? null : v)), scale.max), fill: "none", stroke: color(si), strokeWidth: 2, strokeLinejoin: "round", strokeLinecap: "round", vectorEffect: "non-scaling-stroke" }), incompleteIndex >= 0 ? (_jsx("path", { "data-slot": "time-chart-incomplete", d: linePath(s.values.map((v, i) => (i >= incompleteIndex - 1 ? v : null)), scale.max), fill: "none", stroke: color(si), strokeWidth: 2, strokeDasharray: "3 4", strokeLinecap: "round", vectorEffect: "non-scaling-stroke" })) : null] }, s.key))), active !== null ? (_jsx("line", { x1: active + 0.5, x2: active + 0.5, y1: 0, y2: 100, vectorEffect: "non-scaling-stroke", className: "stroke-foreground/25", strokeWidth: 1 })) : null] }), active !== null && activeTime !== undefined ? (_jsxs("div", { "data-slot": "time-chart-tooltip", className: cn("pointer-events-none absolute top-0 z-10 grid min-w-36 gap-1 rounded-xl bg-popover px-3 py-2 text-xs shadow-lg shadow-black/10 [--popover:var(--floating)]", (active + 0.5) / n > 0.6 ? "-translate-x-[calc(100%+0.75rem)]" : "translate-x-3"), style: { left: `${((active + 0.5) / n) * 100}%` }, children: [_jsxs("p", { className: "font-medium", children: [formatTime(activeTime), active === incompleteIndex ? (_jsx("span", { className: "ml-1.5 font-normal text-muted-foreground", children: "En cours" })) : null] }), series.map((s, si) => (_jsxs("p", { className: "flex items-center gap-2", children: [_jsx("span", { "aria-hidden": true, className: "size-2 shrink-0 rounded-[2px]", style: { background: color(si) } }), _jsx("span", { className: "flex-1 text-muted-foreground", children: s.label }), _jsx("span", { className: "font-medium tabular-nums", children: s.values[active] === null ? "-" : formatValue(s.values[active] ?? 0) })] }, s.key)))] })) : null] }), xLabels.length > 0 ? (_jsx("div", { "aria-hidden": true, className: "relative h-4 text-[11px] text-muted-foreground tabular-nums", children: xLabels.map((i, k) => (_jsx("span", { className: cn("absolute", k === 0 ? "left-0" : k === xLabels.length - 1 ? "right-0" : "-translate-x-1/2"), style: k === 0 || k === xLabels.length - 1 ? undefined : { left: `${((i + 0.5) / n) * 100}%` }, children: timestamps[i] !== undefined ? formatTime(timestamps[i]) : null }, i))) })) : null] })] }), _jsx("div", { className: "sr-only", children: _jsxs("table", { children: [_jsx("caption", { children: n > SR_SAMPLE_SIZE ? `${label} (${n} mesures, échantillon de ${SR_SAMPLE_SIZE})` : label }), _jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { scope: "col", children: "Date" }), series.map((s) => (_jsx("th", { scope: "col", children: s.label }, s.key)))] }) }), _jsx("tbody", { children: sampleIndices(n).map((i) => (_jsxs("tr", { children: [_jsxs("th", { scope: "row", children: [formatTime(timestamps[i] ?? 0), i === incompleteIndex ? " (en cours)" : ""] }), series.map((s) => (_jsx("td", { children: s.values[i] === null ? "-" : formatValue(s.values[i] ?? 0) }, s.key)))] }, timestamps[i]))) })] }) })] }));
|
|
92
99
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hgdc/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.1",
|
|
4
4
|
"description": "Interface de la H Collection : jetons de la DA, thèmes, polices et composants.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"homepage": "https://github.com/hugogebel-boop/hgdc-kit/tree/main/packages/ui#readme",
|