@sentropic/design-system-svelte 0.34.0 → 0.34.21

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.
Files changed (112) hide show
  1. package/dist/AppChrome.svelte +660 -0
  2. package/dist/AppChrome.svelte.d.ts +74 -0
  3. package/dist/AppChrome.svelte.d.ts.map +1 -0
  4. package/dist/AppChrome.test.d.ts +2 -0
  5. package/dist/AppChrome.test.d.ts.map +1 -0
  6. package/dist/AppChrome.test.js +122 -0
  7. package/dist/AppHeader.svelte +159 -1
  8. package/dist/AppHeader.svelte.d.ts +18 -1
  9. package/dist/AppHeader.svelte.d.ts.map +1 -1
  10. package/dist/ArcDiagramChart.svelte +380 -0
  11. package/dist/ArcDiagramChart.svelte.d.ts +43 -0
  12. package/dist/ArcDiagramChart.svelte.d.ts.map +1 -0
  13. package/dist/AreaRangeChart.svelte +487 -0
  14. package/dist/AreaRangeChart.svelte.d.ts +38 -0
  15. package/dist/AreaRangeChart.svelte.d.ts.map +1 -0
  16. package/dist/AreaSplineRangeChart.svelte +478 -0
  17. package/dist/AreaSplineRangeChart.svelte.d.ts +37 -0
  18. package/dist/AreaSplineRangeChart.svelte.d.ts.map +1 -0
  19. package/dist/BellCurveChart.svelte +487 -0
  20. package/dist/BellCurveChart.svelte.d.ts +40 -0
  21. package/dist/BellCurveChart.svelte.d.ts.map +1 -0
  22. package/dist/Calendar.svelte +11 -0
  23. package/dist/ChatThread.svelte +32 -1
  24. package/dist/ChatThread.svelte.d.ts +14 -0
  25. package/dist/ChatThread.svelte.d.ts.map +1 -1
  26. package/dist/ColumnPyramidChart.svelte +332 -0
  27. package/dist/ColumnPyramidChart.svelte.d.ts +35 -0
  28. package/dist/ColumnPyramidChart.svelte.d.ts.map +1 -0
  29. package/dist/ColumnRangeChart.svelte +432 -0
  30. package/dist/ColumnRangeChart.svelte.d.ts +42 -0
  31. package/dist/ColumnRangeChart.svelte.d.ts.map +1 -0
  32. package/dist/Combobox.svelte +3 -0
  33. package/dist/ConfigItemCard.svelte +303 -0
  34. package/dist/ConfigItemCard.svelte.d.ts +37 -0
  35. package/dist/ConfigItemCard.svelte.d.ts.map +1 -0
  36. package/dist/ContentSwitcher.svelte +1 -1
  37. package/dist/DatePicker.svelte +3 -0
  38. package/dist/DependencyWheelChart.svelte +413 -0
  39. package/dist/DependencyWheelChart.svelte.d.ts +42 -0
  40. package/dist/DependencyWheelChart.svelte.d.ts.map +1 -0
  41. package/dist/DumbbellChart.svelte +403 -0
  42. package/dist/DumbbellChart.svelte.d.ts +44 -0
  43. package/dist/DumbbellChart.svelte.d.ts.map +1 -0
  44. package/dist/ErrorBarChart.svelte +428 -0
  45. package/dist/ErrorBarChart.svelte.d.ts +40 -0
  46. package/dist/ErrorBarChart.svelte.d.ts.map +1 -0
  47. package/dist/FieldCard.svelte +220 -0
  48. package/dist/FieldCard.svelte.d.ts +28 -0
  49. package/dist/FieldCard.svelte.d.ts.map +1 -0
  50. package/dist/GanttChart.svelte +410 -0
  51. package/dist/GanttChart.svelte.d.ts +39 -0
  52. package/dist/GanttChart.svelte.d.ts.map +1 -0
  53. package/dist/HLCChart.svelte +330 -0
  54. package/dist/HLCChart.svelte.d.ts +32 -0
  55. package/dist/HLCChart.svelte.d.ts.map +1 -0
  56. package/dist/HeikinAshiChart.svelte +365 -0
  57. package/dist/HeikinAshiChart.svelte.d.ts +37 -0
  58. package/dist/HeikinAshiChart.svelte.d.ts.map +1 -0
  59. package/dist/HollowCandlestickChart.svelte +357 -0
  60. package/dist/HollowCandlestickChart.svelte.d.ts +34 -0
  61. package/dist/HollowCandlestickChart.svelte.d.ts.map +1 -0
  62. package/dist/Input.svelte +3 -0
  63. package/dist/ItemChart.svelte +389 -0
  64. package/dist/ItemChart.svelte.d.ts +67 -0
  65. package/dist/ItemChart.svelte.d.ts.map +1 -0
  66. package/dist/LollipopChart.svelte +1 -1
  67. package/dist/MultiSelect.svelte +3 -0
  68. package/dist/NumberInput.svelte +3 -0
  69. package/dist/OHLCChart.svelte +343 -0
  70. package/dist/OHLCChart.svelte.d.ts +33 -0
  71. package/dist/OHLCChart.svelte.d.ts.map +1 -0
  72. package/dist/OrganizationChart.svelte +284 -0
  73. package/dist/OrganizationChart.svelte.d.ts +19 -0
  74. package/dist/OrganizationChart.svelte.d.ts.map +1 -0
  75. package/dist/PasswordInput.svelte +3 -0
  76. package/dist/PolygonChart.svelte +189 -0
  77. package/dist/PolygonChart.svelte.d.ts +17 -0
  78. package/dist/PolygonChart.svelte.d.ts.map +1 -0
  79. package/dist/ScoreCard.svelte +172 -0
  80. package/dist/ScoreCard.svelte.d.ts +27 -0
  81. package/dist/ScoreCard.svelte.d.ts.map +1 -0
  82. package/dist/Search.svelte +7 -5
  83. package/dist/Select.svelte +3 -0
  84. package/dist/StreamgraphChart.svelte +283 -0
  85. package/dist/StreamgraphChart.svelte.d.ts +23 -0
  86. package/dist/StreamgraphChart.svelte.d.ts.map +1 -0
  87. package/dist/StreamingMessage.svelte +44 -2
  88. package/dist/StreamingMessage.svelte.d.ts +18 -1
  89. package/dist/StreamingMessage.svelte.d.ts.map +1 -1
  90. package/dist/TileMapChart.svelte +314 -0
  91. package/dist/TileMapChart.svelte.d.ts +45 -0
  92. package/dist/TileMapChart.svelte.d.ts.map +1 -0
  93. package/dist/TimePicker.svelte +3 -0
  94. package/dist/TimelineChart.svelte +362 -0
  95. package/dist/TimelineChart.svelte.d.ts +22 -0
  96. package/dist/TimelineChart.svelte.d.ts.map +1 -0
  97. package/dist/TreegraphChart.svelte +281 -0
  98. package/dist/TreegraphChart.svelte.d.ts +19 -0
  99. package/dist/TreegraphChart.svelte.d.ts.map +1 -0
  100. package/dist/VariablePieChart.svelte +313 -0
  101. package/dist/VariablePieChart.svelte.d.ts +52 -0
  102. package/dist/VariablePieChart.svelte.d.ts.map +1 -0
  103. package/dist/VennChart.svelte +348 -0
  104. package/dist/VennChart.svelte.d.ts +72 -0
  105. package/dist/VennChart.svelte.d.ts.map +1 -0
  106. package/dist/WordCloudChart.svelte +279 -0
  107. package/dist/WordCloudChart.svelte.d.ts +18 -0
  108. package/dist/WordCloudChart.svelte.d.ts.map +1 -0
  109. package/dist/index.d.ts +56 -0
  110. package/dist/index.d.ts.map +1 -1
  111. package/dist/index.js +28 -0
  112. package/package.json +5 -3
@@ -0,0 +1,74 @@
1
+ import type { Snippet } from "svelte";
2
+ /** Un lien de navigation principal du chrome. */
3
+ export interface AppChromeNavItem {
4
+ label: string;
5
+ href: string;
6
+ /** Marqué actif (souligné, aria-current=page). */
7
+ active?: boolean;
8
+ }
9
+ /** Une option du sélecteur de thème. */
10
+ export interface AppChromeThemeOption {
11
+ id: string;
12
+ label: string;
13
+ }
14
+ export type AppChromeColorMode = "light" | "dark" | "auto";
15
+ export type AppChromeLocale = "fr" | "en";
16
+ export interface AppChromeProps {
17
+ /** Nom de marque (défaut « Sentropic »). */
18
+ brandName?: string;
19
+ /** Sous-titre produit sous le nom (ex. « Design System », « dataviz »). */
20
+ productName?: string;
21
+ /** Source du logo carré (ex. `/SENT-logo-squared.svg`). */
22
+ logoSrc?: string;
23
+ /** Texte alternatif du logo (décoratif par défaut). */
24
+ logoAlt?: string;
25
+ /** Cible du lien de marque. Défaut `/`. */
26
+ brandHref?: string;
27
+ /** aria-label du lien de marque (sinon dérivé de brandName + productName). */
28
+ brandLabel?: string;
29
+ /** Liens de nav principaux (pills soulignées + état actif). */
30
+ nav?: AppChromeNavItem[];
31
+ /** aria-label de la nav principale. */
32
+ navLabel?: string;
33
+ /** Options de thème. Vide => le sélecteur est masqué. */
34
+ themes?: AppChromeThemeOption[];
35
+ /** Id du thème actif. */
36
+ theme?: string;
37
+ /** Callback de changement de thème. */
38
+ onThemeChange?: (id: string) => void;
39
+ /** aria-label du sélecteur de thème. */
40
+ themeLabel?: string;
41
+ /** Mode couleur actif. Undefined => le toggle est masqué. */
42
+ colorMode?: AppChromeColorMode;
43
+ /** Callback de changement (cycle light -> dark -> auto). */
44
+ onColorModeChange?: (mode: AppChromeColorMode) => void;
45
+ /** Libellés accessibles des 3 modes (light/dark/auto). */
46
+ colorModeLabels?: {
47
+ light: string;
48
+ dark: string;
49
+ auto: string;
50
+ };
51
+ /** Langue active. Undefined => le sélecteur est masqué. */
52
+ locale?: AppChromeLocale;
53
+ /** Callback de changement de langue. */
54
+ onLocaleChange?: (locale: AppChromeLocale) => void;
55
+ /** aria-label du sélecteur de langue. */
56
+ localeLabel?: string;
57
+ /** URL du dépôt. Undefined => le lien est masqué. */
58
+ githubHref?: string;
59
+ /** aria-label du lien GitHub. */
60
+ githubLabel?: string;
61
+ /** Zone identité à droite (IdentityMenu, bouton connexion, …). */
62
+ identity?: Snippet;
63
+ /** État ouvert du tiroir mobile (contrôlé). */
64
+ mobileMenuOpen?: boolean;
65
+ /** Callback de bascule du tiroir mobile. */
66
+ onMobileMenuToggle?: () => void;
67
+ /** aria-label du bouton burger. */
68
+ menuLabel?: string;
69
+ class?: string;
70
+ }
71
+ declare const AppChrome: import("svelte").Component<AppChromeProps, {}, "">;
72
+ type AppChrome = ReturnType<typeof AppChrome>;
73
+ export default AppChrome;
74
+ //# sourceMappingURL=AppChrome.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppChrome.svelte.d.ts","sourceRoot":"","sources":["../src/lib/AppChrome.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,iDAAiD;AACjD,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wCAAwC;AACxC,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAC3D,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,CAAC;AAE1C,MAAM,WAAW,cAAc;IAE7B,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB,+DAA+D;IAC/D,GAAG,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACzB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,yDAAyD;IACzD,MAAM,CAAC,EAAE,oBAAoB,EAAE,CAAC;IAChC,yBAAyB;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,aAAa,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,wCAAwC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACvD,0DAA0D;IAC1D,eAAe,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAGhE,2DAA2D;IAC3D,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,wCAAwC;IACxC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,qDAAqD;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAGnB,+CAA+C;IAC/C,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,4CAA4C;IAC5C,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqRH,QAAA,MAAM,SAAS,oDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=AppChrome.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppChrome.test.d.ts","sourceRoot":"","sources":["../src/lib/AppChrome.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,122 @@
1
+ import { fireEvent, render } from "@testing-library/svelte";
2
+ import { createRawSnippet } from "svelte";
3
+ import { describe, expect, it, vi } from "vitest";
4
+ import AppChrome from "./AppChrome.svelte";
5
+ const snippet = (html) => createRawSnippet(() => ({ render: () => `<span>${html}</span>` }));
6
+ const themes = [
7
+ { id: "sent-tech", label: "Sentropic" },
8
+ { id: "forge", label: "Forge" },
9
+ { id: "entropic", label: "Entropic" },
10
+ { id: "carbon", label: "Carbon" },
11
+ { id: "dsfr", label: "DSFR" },
12
+ { id: "airbus", label: "Airbus" },
13
+ ];
14
+ const nav = [
15
+ { label: "Vues", href: "/views", active: true },
16
+ { label: "Données", href: "/data" },
17
+ { label: "Réglages", href: "/settings" },
18
+ ];
19
+ describe("AppChrome — marque", () => {
20
+ it("renders the canonical brand block (logo + name + product)", () => {
21
+ const { container } = render(AppChrome, {
22
+ props: { brandName: "Sentropic", productName: "dataviz", logoSrc: "/SENT-logo-squared.svg", brandHref: "/home" },
23
+ });
24
+ const brand = container.querySelector("a.st-appChrome__brand");
25
+ expect(brand).toBeTruthy();
26
+ expect(brand.getAttribute("href")).toBe("/home");
27
+ expect(brand.getAttribute("aria-label")).toBe("Sentropic dataviz");
28
+ expect(container.querySelector("img.st-appChrome__brandMark").getAttribute("src")).toBe("/SENT-logo-squared.svg");
29
+ expect(container.querySelector(".st-appChrome__brandName")?.textContent).toBe("Sentropic");
30
+ expect(container.querySelector(".st-appChrome__brandProduct")?.textContent).toBe("dataviz");
31
+ });
32
+ it("defaults brandName to Sentropic", () => {
33
+ const { container } = render(AppChrome, { props: { productName: "dataviz" } });
34
+ expect(container.querySelector(".st-appChrome__brandName")?.textContent).toBe("Sentropic");
35
+ });
36
+ });
37
+ describe("AppChrome — navigation", () => {
38
+ it("renders nav links and marks the active one with aria-current", () => {
39
+ const { container } = render(AppChrome, { props: { nav } });
40
+ const links = Array.from(container.querySelectorAll(".st-appChrome__navLink"));
41
+ expect(links.map((a) => a.textContent?.trim())).toEqual(["Vues", "Données", "Réglages"]);
42
+ expect(links[0].getAttribute("aria-current")).toBe("page");
43
+ expect(links[1].getAttribute("aria-current")).toBeNull();
44
+ expect(links[0].classList.contains("st-appHeader__navLink")).toBe(true);
45
+ });
46
+ });
47
+ describe("AppChrome — contrôle thème", () => {
48
+ it("shows the active theme label and fires onThemeChange on selection", async () => {
49
+ const onThemeChange = vi.fn();
50
+ const { container } = render(AppChrome, { props: { themes, theme: "sent-tech", onThemeChange } });
51
+ const trigger = container.querySelector(".st-appChrome__themeWrap button");
52
+ expect(trigger.textContent).toContain("Sentropic");
53
+ await fireEvent.click(trigger);
54
+ expect(container.querySelector('[role="menu"]')).toBeTruthy();
55
+ const carbon = Array.from(container.querySelectorAll(".st-appChrome__menuItem")).find((b) => b.textContent?.includes("Carbon"));
56
+ await fireEvent.click(carbon);
57
+ expect(onThemeChange).toHaveBeenCalledWith("carbon");
58
+ });
59
+ it("hides the theme selector when themes is empty", () => {
60
+ const { container } = render(AppChrome);
61
+ expect(container.querySelector(".st-appChrome__themeWrap")).toBeNull();
62
+ });
63
+ });
64
+ describe("AppChrome — mode couleur", () => {
65
+ it("cycles light -> dark -> auto via onColorModeChange", async () => {
66
+ const onColorModeChange = vi.fn();
67
+ const { container, rerender } = render(AppChrome, { props: { colorMode: "light", onColorModeChange } });
68
+ await fireEvent.click(container.querySelector(".st-appChrome__iconControl"));
69
+ expect(onColorModeChange).toHaveBeenCalledWith("dark");
70
+ await rerender({ colorMode: "dark", onColorModeChange });
71
+ await fireEvent.click(container.querySelector(".st-appChrome__iconControl"));
72
+ expect(onColorModeChange).toHaveBeenLastCalledWith("auto");
73
+ await rerender({ colorMode: "auto", onColorModeChange });
74
+ await fireEvent.click(container.querySelector(".st-appChrome__iconControl"));
75
+ expect(onColorModeChange).toHaveBeenLastCalledWith("light");
76
+ });
77
+ it("hides the color-mode toggle when colorMode is undefined", () => {
78
+ const { container } = render(AppChrome, { props: { nav } });
79
+ expect(container.querySelector(".st-appChrome__iconControl")).toBeNull();
80
+ });
81
+ });
82
+ describe("AppChrome — langue", () => {
83
+ it("shows the active locale and fires onLocaleChange on selection", async () => {
84
+ const onLocaleChange = vi.fn();
85
+ const { container } = render(AppChrome, { props: { locale: "fr", onLocaleChange } });
86
+ const trigger = container.querySelector(".st-appChrome__localeWrap button");
87
+ expect(trigger.textContent).toContain("FR");
88
+ await fireEvent.click(trigger);
89
+ const en = Array.from(container.querySelectorAll(".st-appChrome__menuItem")).find((b) => b.textContent?.includes("English"));
90
+ await fireEvent.click(en);
91
+ expect(onLocaleChange).toHaveBeenCalledWith("en");
92
+ });
93
+ });
94
+ describe("AppChrome — github + identité", () => {
95
+ it("renders the github link with the provided href", () => {
96
+ const { container } = render(AppChrome, { props: { githubHref: "https://github.com/x/y" } });
97
+ const link = container.querySelector('.st-appChrome__utilityNav a[target="_blank"]');
98
+ expect(link.getAttribute("href")).toBe("https://github.com/x/y");
99
+ });
100
+ it("renders the identity snippet content", () => {
101
+ const { container } = render(AppChrome, { props: { identity: snippet("account-zone") } });
102
+ expect(container.querySelector(".st-appChrome__identity")?.textContent).toContain("account-zone");
103
+ });
104
+ });
105
+ describe("AppChrome — mobile burger + tiroir", () => {
106
+ it("wires the burger aria-controls to the drawer id and renders the drawer when open", () => {
107
+ const { container } = render(AppChrome, { props: { nav, mobileMenuOpen: true } });
108
+ const burger = container.querySelector(".st-appChrome__burgerTrigger");
109
+ const drawer = container.querySelector(".st-appChrome__drawer");
110
+ expect(drawer).toBeTruthy();
111
+ expect(burger.getAttribute("aria-expanded")).toBe("true");
112
+ expect(burger.getAttribute("aria-controls")).toBe(drawer.id);
113
+ expect(drawer.querySelectorAll(".st-appChrome__drawerLink").length).toBeGreaterThanOrEqual(3);
114
+ });
115
+ it("does not render the drawer when closed and toggles via callback", async () => {
116
+ const onMobileMenuToggle = vi.fn();
117
+ const { container } = render(AppChrome, { props: { nav, onMobileMenuToggle } });
118
+ expect(container.querySelector(".st-appChrome__drawer")).toBeNull();
119
+ await fireEvent.click(container.querySelector(".st-appChrome__burgerTrigger"));
120
+ expect(onMobileMenuToggle).toHaveBeenCalledTimes(1);
121
+ });
122
+ });
@@ -18,7 +18,24 @@
18
18
  * Auto-généré et stable si non fourni.
19
19
  */
20
20
  drawerId?: string;
21
- /** Logo (décision actée : logo SENT + sous-titre). */
21
+ /**
22
+ * Marque structurée (décision actée : logo SENT + sous-titre). Rend le bloc
23
+ * canonique « logo carré + nom + sous-titre produit » sans dupliquer de CSS
24
+ * côté consommateur. Si le snippet `logo` est fourni, il a priorité (contrôle
25
+ * total) ; sinon ces props produisent le bloc marque par défaut.
26
+ */
27
+ brandName?: string;
28
+ /** Sous-titre produit affiché sous le nom (ex. « Design System », « dataviz »). */
29
+ productName?: string;
30
+ /** Source de l'image du logo carré (ex. `/SENT-logo-squared.svg`). */
31
+ logoSrc?: string;
32
+ /** Texte alternatif du logo (décoratif par défaut). */
33
+ logoAlt?: string;
34
+ /** Cible du lien de la marque. Défaut : `/`. */
35
+ brandHref?: string;
36
+ /** aria-label du lien de marque (sinon dérivé de `brandName` + `productName`). */
37
+ brandLabel?: string;
38
+ /** Logo (décision actée : logo SENT + sous-titre). Prioritaire sur `brandName`/`logoSrc`. */
22
39
  logo?: Snippet;
23
40
  /** Liens de navigation (rendus dans le <nav> desktop). */
24
41
  nav?: Snippet;
@@ -47,6 +64,12 @@
47
64
  onMenuToggle,
48
65
  menuLabel = "Menu",
49
66
  drawerId,
67
+ brandName,
68
+ productName,
69
+ logoSrc,
70
+ logoAlt = "",
71
+ brandHref = "/",
72
+ brandLabel,
50
73
  logo,
51
74
  nav,
52
75
  actions,
@@ -54,6 +77,15 @@
54
77
  class: className,
55
78
  }: AppHeaderProps = $props();
56
79
 
80
+ // Marque par défaut : rendue ssi aucun snippet `logo` et qu'il y a au moins un
81
+ // nom / logo / produit. Calque le bloc marque canonique du site DS.
82
+ const hasDefaultBrand = $derived(
83
+ !logo && Boolean(brandName || productName || logoSrc),
84
+ );
85
+ const resolvedBrandLabel = $derived(
86
+ brandLabel ?? [brandName, productName].filter(Boolean).join(" "),
87
+ );
88
+
57
89
  // Id stable du tiroir : prop fournie sinon compteur module (SSR-safe, sans
58
90
  // crypto). Capturé une seule fois (untrack) : un id stable ne doit pas réagir.
59
91
  const resolvedDrawerId = untrack(
@@ -68,6 +100,18 @@
68
100
  <!-- Logo SENT à GAUCHE (+ sous-titre). -->
69
101
  {#if logo}
70
102
  <div class="st-appHeader__logo">{@render logo()}</div>
103
+ {:else if hasDefaultBrand}
104
+ <a class="st-appHeader__brand" href={brandHref} aria-label={resolvedBrandLabel || undefined}>
105
+ {#if logoSrc}
106
+ <img class="st-appHeader__brandMark" src={logoSrc} alt={logoAlt} aria-hidden={logoAlt ? undefined : "true"} />
107
+ {/if}
108
+ {#if brandName || productName}
109
+ <span class="st-appHeader__brandCopy">
110
+ {#if brandName}<span class="st-appHeader__brandName">{brandName}</span>{/if}
111
+ {#if productName}<span class="st-appHeader__brandProduct">{productName}</span>{/if}
112
+ </span>
113
+ {/if}
114
+ </a>
71
115
  {/if}
72
116
 
73
117
  <!-- Nav desktop (masquée en mode compact). -->
@@ -185,6 +229,120 @@
185
229
  flex: 0 0 auto;
186
230
  }
187
231
 
232
+ /* --- Marque canonique (logo carré + nom + sous-titre produit) --- */
233
+ .st-appHeader__brand {
234
+ align-items: center;
235
+ color: var(--st-semantic-text-primary);
236
+ display: inline-flex;
237
+ flex: 0 0 auto;
238
+ gap: var(--st-spacing-3, 0.75rem);
239
+ min-width: 0;
240
+ text-decoration: none;
241
+ }
242
+
243
+ .st-appHeader__brand:hover,
244
+ .st-appHeader__brand:focus-visible {
245
+ text-decoration: none;
246
+ }
247
+
248
+ .st-appHeader__brand:focus-visible {
249
+ border-radius: var(--st-radius-sm, 0.375rem);
250
+ box-shadow: 0 0 0 2px var(--st-semantic-border-interactive);
251
+ outline: none;
252
+ }
253
+
254
+ .st-appHeader__brandMark {
255
+ aspect-ratio: 1;
256
+ display: inline-block;
257
+ flex: 0 0 auto;
258
+ height: 2rem;
259
+ object-fit: contain;
260
+ width: 2rem;
261
+ }
262
+
263
+ .st-appHeader__brandCopy {
264
+ display: grid;
265
+ gap: 0.08rem;
266
+ line-height: 1;
267
+ min-width: 0;
268
+ }
269
+
270
+ .st-appHeader__brandName {
271
+ color: var(--st-semantic-text-primary);
272
+ font-size: 1rem;
273
+ font-weight: 760;
274
+ }
275
+
276
+ .st-appHeader__brandProduct {
277
+ color: var(--st-semantic-text-secondary);
278
+ font-size: 0.75rem;
279
+ font-weight: 650;
280
+ }
281
+
282
+ /* --- Lien de nav canonique (pill soulignée, état actif) ---
283
+ Classe utilitaire publiée : un consommateur l'applique sur ses <a> de nav
284
+ (ou via le composant Link DS) pour matcher le chrome du site DS sans
285
+ dupliquer de CSS d'application. */
286
+ :global(.st-appHeader__navLink) {
287
+ align-items: center;
288
+ border-bottom: 2px solid transparent;
289
+ border-radius: 0;
290
+ color: var(--st-semantic-text-secondary);
291
+ display: inline-flex;
292
+ font-size: 0.875rem;
293
+ gap: 0.35rem;
294
+ line-height: 1;
295
+ padding: 0.38rem 0.75rem;
296
+ text-decoration: none;
297
+ white-space: nowrap;
298
+ transition: color var(--st-motion-fast, 120ms) ease,
299
+ border-color var(--st-motion-fast, 120ms) ease;
300
+ }
301
+
302
+ :global(.st-appHeader__navLink:hover),
303
+ :global(.st-appHeader__navLink:focus-visible) {
304
+ color: var(--st-semantic-text-primary);
305
+ text-decoration: none;
306
+ }
307
+
308
+ :global(.st-appHeader__navLink[aria-current="page"]) {
309
+ border-bottom-color: var(--st-semantic-border-interactive);
310
+ color: var(--st-semantic-text-primary);
311
+ font-weight: 650;
312
+ }
313
+
314
+ /* --- Contrôle utilitaire canonique (pill : thème / langue / icône) --- */
315
+ :global(.st-appHeader__control) {
316
+ align-items: center;
317
+ background: var(--st-semantic-surface-default);
318
+ border: 1px solid var(--st-semantic-border-subtle);
319
+ border-radius: var(--st-radius-sm, 0.375rem);
320
+ color: var(--st-semantic-text-secondary);
321
+ cursor: pointer;
322
+ display: inline-flex;
323
+ font: inherit;
324
+ font-size: 0.75rem;
325
+ font-weight: 650;
326
+ gap: 0.35rem;
327
+ height: 2.25rem;
328
+ line-height: 1;
329
+ padding: 0 0.65rem;
330
+ text-decoration: none;
331
+ white-space: nowrap;
332
+ transition: background-color var(--st-motion-fast, 120ms) ease,
333
+ border-color var(--st-motion-fast, 120ms) ease,
334
+ color var(--st-motion-fast, 120ms) ease;
335
+ }
336
+
337
+ :global(.st-appHeader__control:hover),
338
+ :global(.st-appHeader__control:focus-visible),
339
+ :global(.st-appHeader__control[aria-expanded="true"]) {
340
+ background: var(--st-semantic-surface-subtle);
341
+ border-color: var(--st-semantic-border-interactive);
342
+ color: var(--st-semantic-text-primary);
343
+ outline: none;
344
+ }
345
+
188
346
  .st-appHeader__actions {
189
347
  align-items: center;
190
348
  display: flex;
@@ -16,7 +16,24 @@ export interface AppHeaderProps {
16
16
  * Auto-généré et stable si non fourni.
17
17
  */
18
18
  drawerId?: string;
19
- /** Logo (décision actée : logo SENT + sous-titre). */
19
+ /**
20
+ * Marque structurée (décision actée : logo SENT + sous-titre). Rend le bloc
21
+ * canonique « logo carré + nom + sous-titre produit » sans dupliquer de CSS
22
+ * côté consommateur. Si le snippet `logo` est fourni, il a priorité (contrôle
23
+ * total) ; sinon ces props produisent le bloc marque par défaut.
24
+ */
25
+ brandName?: string;
26
+ /** Sous-titre produit affiché sous le nom (ex. « Design System », « dataviz »). */
27
+ productName?: string;
28
+ /** Source de l'image du logo carré (ex. `/SENT-logo-squared.svg`). */
29
+ logoSrc?: string;
30
+ /** Texte alternatif du logo (décoratif par défaut). */
31
+ logoAlt?: string;
32
+ /** Cible du lien de la marque. Défaut : `/`. */
33
+ brandHref?: string;
34
+ /** aria-label du lien de marque (sinon dérivé de `brandName` + `productName`). */
35
+ brandLabel?: string;
36
+ /** Logo (décision actée : logo SENT + sous-titre). Prioritaire sur `brandName`/`logoSrc`. */
20
37
  logo?: Snippet;
21
38
  /** Liens de navigation (rendus dans le <nav> desktop). */
22
39
  nav?: Snippet;
@@ -1 +1 @@
1
- {"version":3,"file":"AppHeader.svelte.d.ts","sourceRoot":"","sources":["../src/lib/AppHeader.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gDAAgD;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,0DAA0D;IAC1D,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,oEAAoE;IACpE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wEAAwE;IACxE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAoFH,QAAA,MAAM,SAAS,oDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"AppHeader.svelte.d.ts","sourceRoot":"","sources":["../src/lib/AppHeader.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gDAAgD;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6FAA6F;IAC7F,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,0DAA0D;IAC1D,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,oEAAoE;IACpE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wEAAwE;IACxE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA+GH,QAAA,MAAM,SAAS,oDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}