@sentropic/design-system-svelte 0.34.20 → 0.34.22
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/dist/AppChrome.svelte +660 -0
- package/dist/AppChrome.svelte.d.ts +74 -0
- package/dist/AppChrome.svelte.d.ts.map +1 -0
- package/dist/AppChrome.test.d.ts +2 -0
- package/dist/AppChrome.test.d.ts.map +1 -0
- package/dist/AppChrome.test.js +122 -0
- package/dist/ConfigItemCard.svelte +303 -0
- package/dist/ConfigItemCard.svelte.d.ts +37 -0
- package/dist/ConfigItemCard.svelte.d.ts.map +1 -0
- package/dist/DataTable.svelte.d.ts +1 -1
- package/dist/ErrorSummary.svelte +75 -0
- package/dist/ErrorSummary.svelte.d.ts +17 -0
- package/dist/ErrorSummary.svelte.d.ts.map +1 -0
- package/dist/FieldCard.svelte +220 -0
- package/dist/FieldCard.svelte.d.ts +28 -0
- package/dist/FieldCard.svelte.d.ts.map +1 -0
- package/dist/ScoreCard.svelte +172 -0
- package/dist/ScoreCard.svelte.d.ts +27 -0
- package/dist/ScoreCard.svelte.d.ts.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/package.json +1 -1
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
|
|
4
|
+
/** Un lien de navigation principal du chrome. */
|
|
5
|
+
export interface AppChromeNavItem {
|
|
6
|
+
label: string;
|
|
7
|
+
href: string;
|
|
8
|
+
/** Marqué actif (souligné, aria-current=page). */
|
|
9
|
+
active?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Une option du sélecteur de thème. */
|
|
13
|
+
export interface AppChromeThemeOption {
|
|
14
|
+
id: string;
|
|
15
|
+
label: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type AppChromeColorMode = "light" | "dark" | "auto";
|
|
19
|
+
export type AppChromeLocale = "fr" | "en";
|
|
20
|
+
|
|
21
|
+
export interface AppChromeProps {
|
|
22
|
+
// ── Marque ──────────────────────────────────────────────────────────────
|
|
23
|
+
/** Nom de marque (défaut « Sentropic »). */
|
|
24
|
+
brandName?: string;
|
|
25
|
+
/** Sous-titre produit sous le nom (ex. « Design System », « dataviz »). */
|
|
26
|
+
productName?: string;
|
|
27
|
+
/** Source du logo carré (ex. `/SENT-logo-squared.svg`). */
|
|
28
|
+
logoSrc?: string;
|
|
29
|
+
/** Texte alternatif du logo (décoratif par défaut). */
|
|
30
|
+
logoAlt?: string;
|
|
31
|
+
/** Cible du lien de marque. Défaut `/`. */
|
|
32
|
+
brandHref?: string;
|
|
33
|
+
/** aria-label du lien de marque (sinon dérivé de brandName + productName). */
|
|
34
|
+
brandLabel?: string;
|
|
35
|
+
|
|
36
|
+
// ── Navigation ──────────────────────────────────────────────────────────
|
|
37
|
+
/** Liens de nav principaux (pills soulignées + état actif). */
|
|
38
|
+
nav?: AppChromeNavItem[];
|
|
39
|
+
/** aria-label de la nav principale. */
|
|
40
|
+
navLabel?: string;
|
|
41
|
+
|
|
42
|
+
// ── Contrôle thème (contrôlé) ─────────────────────────────────────────────
|
|
43
|
+
/** Options de thème. Vide => le sélecteur est masqué. */
|
|
44
|
+
themes?: AppChromeThemeOption[];
|
|
45
|
+
/** Id du thème actif. */
|
|
46
|
+
theme?: string;
|
|
47
|
+
/** Callback de changement de thème. */
|
|
48
|
+
onThemeChange?: (id: string) => void;
|
|
49
|
+
/** aria-label du sélecteur de thème. */
|
|
50
|
+
themeLabel?: string;
|
|
51
|
+
|
|
52
|
+
// ── Contrôle mode couleur (contrôlé) ───────────────────────────────────────
|
|
53
|
+
/** Mode couleur actif. Undefined => le toggle est masqué. */
|
|
54
|
+
colorMode?: AppChromeColorMode;
|
|
55
|
+
/** Callback de changement (cycle light -> dark -> auto). */
|
|
56
|
+
onColorModeChange?: (mode: AppChromeColorMode) => void;
|
|
57
|
+
/** Libellés accessibles des 3 modes (light/dark/auto). */
|
|
58
|
+
colorModeLabels?: { light: string; dark: string; auto: string };
|
|
59
|
+
|
|
60
|
+
// ── Contrôle langue (contrôlé) ─────────────────────────────────────────────
|
|
61
|
+
/** Langue active. Undefined => le sélecteur est masqué. */
|
|
62
|
+
locale?: AppChromeLocale;
|
|
63
|
+
/** Callback de changement de langue. */
|
|
64
|
+
onLocaleChange?: (locale: AppChromeLocale) => void;
|
|
65
|
+
/** aria-label du sélecteur de langue. */
|
|
66
|
+
localeLabel?: string;
|
|
67
|
+
|
|
68
|
+
// ── Lien GitHub ────────────────────────────────────────────────────────────
|
|
69
|
+
/** URL du dépôt. Undefined => le lien est masqué. */
|
|
70
|
+
githubHref?: string;
|
|
71
|
+
/** aria-label du lien GitHub. */
|
|
72
|
+
githubLabel?: string;
|
|
73
|
+
|
|
74
|
+
// ── Identité ───────────────────────────────────────────────────────────────
|
|
75
|
+
/** Zone identité à droite (IdentityMenu, bouton connexion, …). */
|
|
76
|
+
identity?: Snippet;
|
|
77
|
+
|
|
78
|
+
// ── Mobile ─────────────────────────────────────────────────────────────────
|
|
79
|
+
/** État ouvert du tiroir mobile (contrôlé). */
|
|
80
|
+
mobileMenuOpen?: boolean;
|
|
81
|
+
/** Callback de bascule du tiroir mobile. */
|
|
82
|
+
onMobileMenuToggle?: () => void;
|
|
83
|
+
/** aria-label du bouton burger. */
|
|
84
|
+
menuLabel?: string;
|
|
85
|
+
|
|
86
|
+
class?: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let appChromeIdCounter = 0;
|
|
90
|
+
function nextAppChromeId(): number {
|
|
91
|
+
return ++appChromeIdCounter;
|
|
92
|
+
}
|
|
93
|
+
</script>
|
|
94
|
+
|
|
95
|
+
<script lang="ts">
|
|
96
|
+
import { untrack } from "svelte";
|
|
97
|
+
import {
|
|
98
|
+
Boxes,
|
|
99
|
+
ChevronDown,
|
|
100
|
+
Github,
|
|
101
|
+
Globe,
|
|
102
|
+
Moon,
|
|
103
|
+
Palette,
|
|
104
|
+
Sun,
|
|
105
|
+
} from "@lucide/svelte";
|
|
106
|
+
import AppHeader from "./AppHeader.svelte";
|
|
107
|
+
|
|
108
|
+
let {
|
|
109
|
+
brandName = "Sentropic",
|
|
110
|
+
productName,
|
|
111
|
+
logoSrc,
|
|
112
|
+
logoAlt = "",
|
|
113
|
+
brandHref = "/",
|
|
114
|
+
brandLabel,
|
|
115
|
+
nav = [],
|
|
116
|
+
navLabel = "Primary",
|
|
117
|
+
themes = [],
|
|
118
|
+
theme,
|
|
119
|
+
onThemeChange,
|
|
120
|
+
themeLabel = "Change theme",
|
|
121
|
+
colorMode,
|
|
122
|
+
onColorModeChange,
|
|
123
|
+
colorModeLabels = { light: "Light mode", dark: "Dark mode", auto: "Auto mode" },
|
|
124
|
+
locale,
|
|
125
|
+
onLocaleChange,
|
|
126
|
+
localeLabel = "Change language",
|
|
127
|
+
githubHref,
|
|
128
|
+
githubLabel = "GitHub",
|
|
129
|
+
identity,
|
|
130
|
+
mobileMenuOpen = false,
|
|
131
|
+
onMobileMenuToggle,
|
|
132
|
+
menuLabel = "Menu",
|
|
133
|
+
class: className,
|
|
134
|
+
}: AppChromeProps = $props();
|
|
135
|
+
|
|
136
|
+
let isThemeOpen = $state(false);
|
|
137
|
+
let isLocaleOpen = $state(false);
|
|
138
|
+
|
|
139
|
+
const drawerId = untrack(() => `st-appChrome-drawer-${nextAppChromeId()}`);
|
|
140
|
+
|
|
141
|
+
const activeTheme = $derived(themes.find((t) => t.id === theme) ?? themes[0]);
|
|
142
|
+
const showThemeSelector = $derived(themes.length > 0);
|
|
143
|
+
const showColorMode = $derived(colorMode !== undefined);
|
|
144
|
+
const showLocaleSelector = $derived(locale !== undefined);
|
|
145
|
+
const showGithub = $derived(Boolean(githubHref));
|
|
146
|
+
|
|
147
|
+
function pickTheme(id: string) {
|
|
148
|
+
onThemeChange?.(id);
|
|
149
|
+
isThemeOpen = false;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function pickLocale(value: AppChromeLocale) {
|
|
153
|
+
onLocaleChange?.(value);
|
|
154
|
+
isLocaleOpen = false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function cycleColorMode() {
|
|
158
|
+
const next: AppChromeColorMode =
|
|
159
|
+
colorMode === "light" ? "dark" : colorMode === "dark" ? "auto" : "light";
|
|
160
|
+
onColorModeChange?.(next);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function colorModeAriaLabel(): string {
|
|
164
|
+
if (colorMode === "light") return colorModeLabels.dark;
|
|
165
|
+
if (colorMode === "dark") return colorModeLabels.auto;
|
|
166
|
+
return colorModeLabels.light;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const classes = () => ["st-appChrome", className].filter(Boolean).join(" ");
|
|
170
|
+
|
|
171
|
+
function closeMenus(target: EventTarget | null) {
|
|
172
|
+
const el = target as Element | null;
|
|
173
|
+
if (isThemeOpen && el && !el.closest(".st-appChrome__themeWrap")) isThemeOpen = false;
|
|
174
|
+
if (isLocaleOpen && el && !el.closest(".st-appChrome__localeWrap")) isLocaleOpen = false;
|
|
175
|
+
}
|
|
176
|
+
</script>
|
|
177
|
+
|
|
178
|
+
<svelte:window
|
|
179
|
+
onclick={(e) => closeMenus(e.target)}
|
|
180
|
+
onkeydown={(e) => {
|
|
181
|
+
if (e.key === "Escape") {
|
|
182
|
+
isThemeOpen = false;
|
|
183
|
+
isLocaleOpen = false;
|
|
184
|
+
}
|
|
185
|
+
}}
|
|
186
|
+
/>
|
|
187
|
+
|
|
188
|
+
<!-- ── Marque ─────────────────────────────────────────────────────────────── -->
|
|
189
|
+
{#snippet brand()}
|
|
190
|
+
<a class="st-appChrome__brand" href={brandHref} aria-label={brandLabel ?? ([brandName, productName].filter(Boolean).join(" ") || undefined)}>
|
|
191
|
+
{#if logoSrc}
|
|
192
|
+
<img class="st-appChrome__brandMark" src={logoSrc} alt={logoAlt} aria-hidden={logoAlt ? undefined : "true"} />
|
|
193
|
+
{/if}
|
|
194
|
+
{#if brandName || productName}
|
|
195
|
+
<span class="st-appChrome__brandCopy">
|
|
196
|
+
{#if brandName}<span class="st-appChrome__brandName">{brandName}</span>{/if}
|
|
197
|
+
{#if productName}<span class="st-appChrome__brandProduct">{productName}</span>{/if}
|
|
198
|
+
</span>
|
|
199
|
+
{/if}
|
|
200
|
+
</a>
|
|
201
|
+
{/snippet}
|
|
202
|
+
|
|
203
|
+
<!-- ── Nav principale ─────────────────────────────────────────────────────── -->
|
|
204
|
+
{#snippet navContent()}
|
|
205
|
+
{#each nav as item (item.href)}
|
|
206
|
+
<a
|
|
207
|
+
class="st-appChrome__navLink st-appHeader__navLink"
|
|
208
|
+
href={item.href}
|
|
209
|
+
aria-current={item.active ? "page" : undefined}
|
|
210
|
+
>
|
|
211
|
+
{item.label}
|
|
212
|
+
</a>
|
|
213
|
+
{/each}
|
|
214
|
+
{/snippet}
|
|
215
|
+
|
|
216
|
+
<!-- ── Sélecteur de thème ─────────────────────────────────────────────────── -->
|
|
217
|
+
{#snippet themeSelector()}
|
|
218
|
+
<div class="st-appChrome__themeWrap st-appChrome__menuWrap">
|
|
219
|
+
<button
|
|
220
|
+
type="button"
|
|
221
|
+
class="st-appChrome__control st-appHeader__control"
|
|
222
|
+
onclick={() => (isThemeOpen = !isThemeOpen)}
|
|
223
|
+
aria-expanded={isThemeOpen}
|
|
224
|
+
aria-haspopup="true"
|
|
225
|
+
aria-label={themeLabel}
|
|
226
|
+
>
|
|
227
|
+
<Palette size={14} aria-hidden="true" />
|
|
228
|
+
<span>{activeTheme?.label}</span>
|
|
229
|
+
<ChevronDown size={12} class="st-appChrome__chevron {isThemeOpen ? 'is-rotated' : ''}" aria-hidden="true" />
|
|
230
|
+
</button>
|
|
231
|
+
{#if isThemeOpen}
|
|
232
|
+
<div class="st-appChrome__menu" role="menu">
|
|
233
|
+
{#each themes as option (option.id)}
|
|
234
|
+
<button
|
|
235
|
+
type="button"
|
|
236
|
+
class="st-appChrome__menuItem"
|
|
237
|
+
class:is-active={theme === option.id}
|
|
238
|
+
role="menuitem"
|
|
239
|
+
onclick={() => pickTheme(option.id)}
|
|
240
|
+
>
|
|
241
|
+
<span class="st-appChrome__check" aria-hidden="true">{#if theme === option.id}✓{/if}</span>
|
|
242
|
+
<span>{option.label}</span>
|
|
243
|
+
</button>
|
|
244
|
+
{/each}
|
|
245
|
+
</div>
|
|
246
|
+
{/if}
|
|
247
|
+
</div>
|
|
248
|
+
{/snippet}
|
|
249
|
+
|
|
250
|
+
<!-- ── Toggle mode couleur ────────────────────────────────────────────────── -->
|
|
251
|
+
{#snippet colorModeToggle()}
|
|
252
|
+
<button
|
|
253
|
+
type="button"
|
|
254
|
+
class="st-appChrome__control st-appChrome__iconControl st-appHeader__control"
|
|
255
|
+
onclick={cycleColorMode}
|
|
256
|
+
aria-label={colorModeAriaLabel()}
|
|
257
|
+
>
|
|
258
|
+
{#if colorMode === "dark"}
|
|
259
|
+
<Moon size={16} strokeWidth={2} aria-hidden="true" />
|
|
260
|
+
{:else if colorMode === "light"}
|
|
261
|
+
<Sun size={16} strokeWidth={2} aria-hidden="true" />
|
|
262
|
+
{:else}
|
|
263
|
+
<Sun size={16} strokeWidth={1.5} aria-hidden="true" style="opacity:0.65" />
|
|
264
|
+
{/if}
|
|
265
|
+
</button>
|
|
266
|
+
{/snippet}
|
|
267
|
+
|
|
268
|
+
<!-- ── Sélecteur de langue ────────────────────────────────────────────────── -->
|
|
269
|
+
{#snippet localeSelector()}
|
|
270
|
+
<div class="st-appChrome__localeWrap st-appChrome__menuWrap">
|
|
271
|
+
<button
|
|
272
|
+
type="button"
|
|
273
|
+
class="st-appChrome__control st-appHeader__control"
|
|
274
|
+
onclick={() => (isLocaleOpen = !isLocaleOpen)}
|
|
275
|
+
aria-expanded={isLocaleOpen}
|
|
276
|
+
aria-haspopup="true"
|
|
277
|
+
aria-label={localeLabel}
|
|
278
|
+
>
|
|
279
|
+
<Globe size={14} aria-hidden="true" />
|
|
280
|
+
<span>{locale?.toUpperCase()}</span>
|
|
281
|
+
<ChevronDown size={12} class="st-appChrome__chevron {isLocaleOpen ? 'is-rotated' : ''}" aria-hidden="true" />
|
|
282
|
+
</button>
|
|
283
|
+
{#if isLocaleOpen}
|
|
284
|
+
<div class="st-appChrome__menu" role="menu">
|
|
285
|
+
{#each (["fr", "en"] as AppChromeLocale[]) as value (value)}
|
|
286
|
+
<button
|
|
287
|
+
type="button"
|
|
288
|
+
class="st-appChrome__menuItem"
|
|
289
|
+
class:is-active={locale === value}
|
|
290
|
+
role="menuitem"
|
|
291
|
+
onclick={() => pickLocale(value)}
|
|
292
|
+
>
|
|
293
|
+
<span class="st-appChrome__check" aria-hidden="true">{#if locale === value}✓{/if}</span>
|
|
294
|
+
<span>{value === "fr" ? "Français" : "English"}</span>
|
|
295
|
+
</button>
|
|
296
|
+
{/each}
|
|
297
|
+
</div>
|
|
298
|
+
{/if}
|
|
299
|
+
</div>
|
|
300
|
+
{/snippet}
|
|
301
|
+
|
|
302
|
+
<!-- ── Lien GitHub ────────────────────────────────────────────────────────── -->
|
|
303
|
+
{#snippet githubLink()}
|
|
304
|
+
<a
|
|
305
|
+
class="st-appChrome__control st-appChrome__iconControl st-appHeader__control"
|
|
306
|
+
href={githubHref}
|
|
307
|
+
rel="noreferrer"
|
|
308
|
+
target="_blank"
|
|
309
|
+
aria-label={githubLabel}
|
|
310
|
+
>
|
|
311
|
+
<Github size={16} strokeWidth={2.1} aria-hidden="true" />
|
|
312
|
+
</a>
|
|
313
|
+
{/snippet}
|
|
314
|
+
|
|
315
|
+
<!-- ── Zone de contrôles utilitaires (droite) ─────────────────────────────── -->
|
|
316
|
+
{#snippet utilityNav()}
|
|
317
|
+
<div class="st-appChrome__utilityNav">
|
|
318
|
+
{#if showThemeSelector}{@render themeSelector()}{/if}
|
|
319
|
+
{#if showColorMode}{@render colorModeToggle()}{/if}
|
|
320
|
+
{#if showLocaleSelector}{@render localeSelector()}{/if}
|
|
321
|
+
{#if showGithub}{@render githubLink()}{/if}
|
|
322
|
+
{#if identity}<div class="st-appChrome__identity">{@render identity()}</div>{/if}
|
|
323
|
+
</div>
|
|
324
|
+
{/snippet}
|
|
325
|
+
|
|
326
|
+
<!-- ── Actions desktop (nav + contrôles dans une barre, burger mobile) ─────── -->
|
|
327
|
+
{#snippet actions()}
|
|
328
|
+
{@render utilityNav()}
|
|
329
|
+
<button
|
|
330
|
+
type="button"
|
|
331
|
+
class="st-appChrome__burgerTrigger"
|
|
332
|
+
onclick={onMobileMenuToggle}
|
|
333
|
+
aria-expanded={mobileMenuOpen}
|
|
334
|
+
aria-controls={drawerId}
|
|
335
|
+
aria-label={menuLabel}
|
|
336
|
+
>
|
|
337
|
+
<Boxes size={20} aria-hidden="true" />
|
|
338
|
+
</button>
|
|
339
|
+
{/snippet}
|
|
340
|
+
|
|
341
|
+
<div class={classes()}>
|
|
342
|
+
<AppHeader
|
|
343
|
+
class="st-appChrome__header"
|
|
344
|
+
brandHref={brandHref}
|
|
345
|
+
brandLabel={brandLabel}
|
|
346
|
+
logo={brand}
|
|
347
|
+
nav={navContent}
|
|
348
|
+
actions={actions}
|
|
349
|
+
/>
|
|
350
|
+
|
|
351
|
+
{#if mobileMenuOpen}
|
|
352
|
+
<nav id={drawerId} class="st-appChrome__drawer" aria-label={navLabel}>
|
|
353
|
+
<div class="st-appChrome__drawerSection">
|
|
354
|
+
{#each nav as item (item.href)}
|
|
355
|
+
<a
|
|
356
|
+
class="st-appChrome__drawerLink"
|
|
357
|
+
href={item.href}
|
|
358
|
+
aria-current={item.active ? "page" : undefined}
|
|
359
|
+
onclick={() => onMobileMenuToggle?.()}
|
|
360
|
+
>
|
|
361
|
+
{item.label}
|
|
362
|
+
</a>
|
|
363
|
+
{/each}
|
|
364
|
+
</div>
|
|
365
|
+
|
|
366
|
+
{#if showThemeSelector}
|
|
367
|
+
<div class="st-appChrome__drawerSection">
|
|
368
|
+
<span class="st-appChrome__drawerLabel">{themeLabel}</span>
|
|
369
|
+
<div class="st-appChrome__drawerSwitcher">
|
|
370
|
+
{#each themes as option (option.id)}
|
|
371
|
+
<button
|
|
372
|
+
type="button"
|
|
373
|
+
class="st-appChrome__drawerBtn"
|
|
374
|
+
class:is-active={theme === option.id}
|
|
375
|
+
onclick={() => { onThemeChange?.(option.id); onMobileMenuToggle?.(); }}
|
|
376
|
+
>
|
|
377
|
+
{option.label}
|
|
378
|
+
</button>
|
|
379
|
+
{/each}
|
|
380
|
+
</div>
|
|
381
|
+
</div>
|
|
382
|
+
{/if}
|
|
383
|
+
|
|
384
|
+
{#if showColorMode}
|
|
385
|
+
<div class="st-appChrome__drawerSection">
|
|
386
|
+
<span class="st-appChrome__drawerLabel">{colorModeLabels.light} / {colorModeLabels.dark}</span>
|
|
387
|
+
<div class="st-appChrome__drawerSwitcher">
|
|
388
|
+
{#each (["light", "dark", "auto"] as AppChromeColorMode[]) as mode (mode)}
|
|
389
|
+
<button
|
|
390
|
+
type="button"
|
|
391
|
+
class="st-appChrome__drawerBtn"
|
|
392
|
+
class:is-active={colorMode === mode}
|
|
393
|
+
onclick={() => onColorModeChange?.(mode)}
|
|
394
|
+
>
|
|
395
|
+
{mode === "light" ? colorModeLabels.light : mode === "dark" ? colorModeLabels.dark : colorModeLabels.auto}
|
|
396
|
+
</button>
|
|
397
|
+
{/each}
|
|
398
|
+
</div>
|
|
399
|
+
</div>
|
|
400
|
+
{/if}
|
|
401
|
+
|
|
402
|
+
{#if showLocaleSelector}
|
|
403
|
+
<div class="st-appChrome__drawerSection">
|
|
404
|
+
<span class="st-appChrome__drawerLabel">{localeLabel}</span>
|
|
405
|
+
<div class="st-appChrome__drawerSwitcher">
|
|
406
|
+
{#each (["fr", "en"] as AppChromeLocale[]) as value (value)}
|
|
407
|
+
<button
|
|
408
|
+
type="button"
|
|
409
|
+
class="st-appChrome__drawerBtn"
|
|
410
|
+
class:is-active={locale === value}
|
|
411
|
+
onclick={() => { onLocaleChange?.(value); onMobileMenuToggle?.(); }}
|
|
412
|
+
>
|
|
413
|
+
{value === "fr" ? "Français" : "English"}
|
|
414
|
+
</button>
|
|
415
|
+
{/each}
|
|
416
|
+
</div>
|
|
417
|
+
</div>
|
|
418
|
+
{/if}
|
|
419
|
+
|
|
420
|
+
{#if showGithub || identity}
|
|
421
|
+
<div class="st-appChrome__drawerSection">
|
|
422
|
+
{#if showGithub}
|
|
423
|
+
<a class="st-appChrome__drawerLink" href={githubHref} rel="noreferrer" target="_blank">{githubLabel}</a>
|
|
424
|
+
{/if}
|
|
425
|
+
{#if identity}<div class="st-appChrome__identity">{@render identity()}</div>{/if}
|
|
426
|
+
</div>
|
|
427
|
+
{/if}
|
|
428
|
+
</nav>
|
|
429
|
+
{/if}
|
|
430
|
+
</div>
|
|
431
|
+
|
|
432
|
+
<style>
|
|
433
|
+
/* Le markup, les classes et les tokens sont byte-identiques entre svelte/
|
|
434
|
+
react/vue : la source de vérité du CSS est le bloc publié (styles.css).
|
|
435
|
+
Ce <style> scoped ne fait que rendre la démo Svelte autonome. */
|
|
436
|
+
.st-appChrome {
|
|
437
|
+
width: 100%;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
:global(.st-appChrome__header .st-appHeader__bar) {
|
|
441
|
+
max-width: none;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.st-appChrome__brand {
|
|
445
|
+
align-items: center;
|
|
446
|
+
color: var(--st-semantic-text-primary);
|
|
447
|
+
display: inline-flex;
|
|
448
|
+
flex: 0 0 auto;
|
|
449
|
+
gap: var(--st-spacing-3, 0.75rem);
|
|
450
|
+
min-width: 0;
|
|
451
|
+
text-decoration: none;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.st-appChrome__brandMark {
|
|
455
|
+
aspect-ratio: 1;
|
|
456
|
+
display: inline-block;
|
|
457
|
+
flex: 0 0 auto;
|
|
458
|
+
height: 2rem;
|
|
459
|
+
object-fit: contain;
|
|
460
|
+
width: 2rem;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.st-appChrome__brandCopy {
|
|
464
|
+
display: grid;
|
|
465
|
+
gap: 0.08rem;
|
|
466
|
+
line-height: 1;
|
|
467
|
+
min-width: 0;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.st-appChrome__brandName {
|
|
471
|
+
color: var(--st-semantic-text-primary);
|
|
472
|
+
font-size: 1rem;
|
|
473
|
+
font-weight: 760;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.st-appChrome__brandProduct {
|
|
477
|
+
color: var(--st-semantic-text-secondary);
|
|
478
|
+
font-size: 0.75rem;
|
|
479
|
+
font-weight: 650;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.st-appChrome__utilityNav {
|
|
483
|
+
align-items: center;
|
|
484
|
+
display: flex;
|
|
485
|
+
gap: var(--st-spacing-2, 0.5rem);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.st-appChrome__menuWrap {
|
|
489
|
+
display: inline-block;
|
|
490
|
+
position: relative;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.st-appChrome__control {
|
|
494
|
+
font-size: 0.75rem;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
.st-appChrome__iconControl {
|
|
498
|
+
justify-content: center;
|
|
499
|
+
padding: 0;
|
|
500
|
+
width: 2.25rem;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
.st-appChrome__chevron {
|
|
504
|
+
transition: transform var(--st-motion-fast, 120ms) ease;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
:global(.st-appChrome__chevron.is-rotated) {
|
|
508
|
+
transform: rotate(180deg);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
.st-appChrome__menu {
|
|
512
|
+
background: var(--st-semantic-surface-raised, var(--st-semantic-surface-default));
|
|
513
|
+
border: 1px solid var(--st-semantic-border-subtle);
|
|
514
|
+
border-radius: var(--st-radius-sm, 0.375rem);
|
|
515
|
+
box-shadow: var(--st-shadow-medium, 0 8px 24px rgb(15 23 42 / 0.08));
|
|
516
|
+
display: flex;
|
|
517
|
+
flex-direction: column;
|
|
518
|
+
gap: 0.1rem;
|
|
519
|
+
min-width: 8.5rem;
|
|
520
|
+
padding: 0.25rem;
|
|
521
|
+
position: absolute;
|
|
522
|
+
right: 0;
|
|
523
|
+
top: calc(100% + 4px);
|
|
524
|
+
z-index: var(--st-zindex-overlay, 80);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
.st-appChrome__menuItem {
|
|
528
|
+
align-items: center;
|
|
529
|
+
background: transparent;
|
|
530
|
+
border: 0;
|
|
531
|
+
border-radius: var(--st-radius-xs, 0.25rem);
|
|
532
|
+
color: var(--st-semantic-text-secondary);
|
|
533
|
+
cursor: pointer;
|
|
534
|
+
display: flex;
|
|
535
|
+
font: inherit;
|
|
536
|
+
font-size: 0.82rem;
|
|
537
|
+
font-weight: 550;
|
|
538
|
+
gap: 0.45rem;
|
|
539
|
+
padding: 0.35rem 0.65rem;
|
|
540
|
+
text-align: left;
|
|
541
|
+
width: 100%;
|
|
542
|
+
transition: background-color var(--st-motion-fast, 120ms) ease,
|
|
543
|
+
color var(--st-motion-fast, 120ms) ease;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.st-appChrome__menuItem:hover {
|
|
547
|
+
background: var(--st-semantic-surface-subtle);
|
|
548
|
+
color: var(--st-semantic-text-primary);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
.st-appChrome__menuItem.is-active {
|
|
552
|
+
color: var(--st-semantic-text-link);
|
|
553
|
+
font-weight: 650;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.st-appChrome__check {
|
|
557
|
+
align-items: center;
|
|
558
|
+
display: inline-flex;
|
|
559
|
+
font-size: 0.75rem;
|
|
560
|
+
justify-content: center;
|
|
561
|
+
width: 0.75rem;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
.st-appChrome__burgerTrigger {
|
|
565
|
+
align-items: center;
|
|
566
|
+
background: transparent;
|
|
567
|
+
border: 0;
|
|
568
|
+
border-radius: var(--st-radius-xs, 0.25rem);
|
|
569
|
+
color: var(--st-semantic-text-secondary);
|
|
570
|
+
cursor: pointer;
|
|
571
|
+
display: none;
|
|
572
|
+
height: 2.25rem;
|
|
573
|
+
justify-content: center;
|
|
574
|
+
width: 2.25rem;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.st-appChrome__burgerTrigger:hover {
|
|
578
|
+
background: var(--st-semantic-surface-subtle);
|
|
579
|
+
color: var(--st-semantic-text-primary);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
.st-appChrome__drawer {
|
|
583
|
+
background: var(--st-semantic-surface-default);
|
|
584
|
+
border-bottom: 1px solid var(--st-semantic-border-subtle);
|
|
585
|
+
box-shadow: var(--st-shadow-medium, 0 10px 20px rgb(15 23 42 / 0.05));
|
|
586
|
+
display: none;
|
|
587
|
+
flex-direction: column;
|
|
588
|
+
gap: var(--st-spacing-5, 1.25rem);
|
|
589
|
+
padding: var(--st-spacing-5, 1.25rem);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
.st-appChrome__drawerSection {
|
|
593
|
+
display: flex;
|
|
594
|
+
flex-direction: column;
|
|
595
|
+
gap: var(--st-spacing-2, 0.5rem);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
.st-appChrome__drawerLabel {
|
|
599
|
+
color: var(--st-semantic-text-secondary);
|
|
600
|
+
font-size: 0.75rem;
|
|
601
|
+
font-weight: 700;
|
|
602
|
+
letter-spacing: 0.05em;
|
|
603
|
+
text-transform: uppercase;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
.st-appChrome__drawerLink {
|
|
607
|
+
align-items: center;
|
|
608
|
+
border-bottom: 1px solid var(--st-semantic-border-subtle);
|
|
609
|
+
color: var(--st-semantic-text-primary);
|
|
610
|
+
display: inline-flex;
|
|
611
|
+
font-size: 1rem;
|
|
612
|
+
font-weight: 650;
|
|
613
|
+
gap: 0.45rem;
|
|
614
|
+
padding: 0.45rem 0;
|
|
615
|
+
text-decoration: none;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
.st-appChrome__drawerLink[aria-current="page"],
|
|
619
|
+
.st-appChrome__drawerLink:hover {
|
|
620
|
+
color: var(--st-semantic-text-link);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
.st-appChrome__drawerSwitcher {
|
|
624
|
+
display: flex;
|
|
625
|
+
gap: var(--st-spacing-2, 0.5rem);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
.st-appChrome__drawerBtn {
|
|
629
|
+
background: var(--st-semantic-surface-subtle);
|
|
630
|
+
border: 1px solid var(--st-semantic-border-subtle);
|
|
631
|
+
border-radius: var(--st-radius-xs, 0.25rem);
|
|
632
|
+
color: var(--st-semantic-text-secondary);
|
|
633
|
+
cursor: pointer;
|
|
634
|
+
flex: 1;
|
|
635
|
+
font: inherit;
|
|
636
|
+
font-size: 0.75rem;
|
|
637
|
+
font-weight: 650;
|
|
638
|
+
padding: 0.45rem;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
.st-appChrome__drawerBtn.is-active {
|
|
642
|
+
background: var(--st-semantic-border-interactive);
|
|
643
|
+
border-color: var(--st-semantic-border-interactive);
|
|
644
|
+
color: var(--st-semantic-text-on-emphasis, var(--st-semantic-surface-default));
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
@media (max-width: 768px) {
|
|
648
|
+
.st-appChrome__utilityNav {
|
|
649
|
+
display: none;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
.st-appChrome__burgerTrigger {
|
|
653
|
+
display: inline-flex;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
.st-appChrome__drawer {
|
|
657
|
+
display: flex;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
</style>
|