@sentropic/design-system-svelte 0.9.0 → 0.10.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.
Files changed (67) hide show
  1. package/dist/AreaChart.svelte +6 -0
  2. package/dist/Button.svelte +48 -20
  3. package/dist/Card.svelte +6 -4
  4. package/dist/Checkbox.svelte +4 -0
  5. package/dist/Combobox.svelte +17 -6
  6. package/dist/DatePicker.svelte +17 -6
  7. package/dist/DonutChart.svelte +169 -0
  8. package/dist/DonutChart.svelte.d.ts +21 -0
  9. package/dist/DonutChart.svelte.d.ts.map +1 -0
  10. package/dist/Footer.svelte +124 -0
  11. package/dist/Footer.svelte.d.ts +21 -0
  12. package/dist/Footer.svelte.d.ts.map +1 -0
  13. package/dist/Highlight.svelte +63 -0
  14. package/dist/Highlight.svelte.d.ts +15 -0
  15. package/dist/Highlight.svelte.d.ts.map +1 -0
  16. package/dist/Input.svelte +34 -13
  17. package/dist/LanguageSelector.svelte +181 -0
  18. package/dist/LanguageSelector.svelte.d.ts +18 -0
  19. package/dist/LanguageSelector.svelte.d.ts.map +1 -0
  20. package/dist/Link.svelte +18 -10
  21. package/dist/MessageStatusBadge.svelte +11 -7
  22. package/dist/MessageStatusBadge.svelte.d.ts +2 -0
  23. package/dist/MessageStatusBadge.svelte.d.ts.map +1 -1
  24. package/dist/MultiSelect.svelte +17 -7
  25. package/dist/NumberInput.svelte +17 -6
  26. package/dist/OrderedList.svelte +103 -0
  27. package/dist/OrderedList.svelte.d.ts +17 -0
  28. package/dist/OrderedList.svelte.d.ts.map +1 -0
  29. package/dist/OverflowMenu.svelte +5 -2
  30. package/dist/PaginationNav.svelte +5 -2
  31. package/dist/PasswordInput.svelte +17 -6
  32. package/dist/Quote.svelte +72 -0
  33. package/dist/Quote.svelte.d.ts +16 -0
  34. package/dist/Quote.svelte.d.ts.map +1 -0
  35. package/dist/Radio.svelte +4 -0
  36. package/dist/ScatterPlot.svelte +179 -0
  37. package/dist/ScatterPlot.svelte.d.ts +21 -0
  38. package/dist/ScatterPlot.svelte.d.ts.map +1 -0
  39. package/dist/Search.svelte +17 -6
  40. package/dist/Select.svelte +21 -8
  41. package/dist/SkipLink.svelte +50 -0
  42. package/dist/SkipLink.svelte.d.ts +12 -0
  43. package/dist/SkipLink.svelte.d.ts.map +1 -0
  44. package/dist/StackedBarChart.svelte +190 -0
  45. package/dist/StackedBarChart.svelte.d.ts +22 -0
  46. package/dist/StackedBarChart.svelte.d.ts.map +1 -0
  47. package/dist/StreamingMessage.svelte +47 -0
  48. package/dist/StreamingMessage.svelte.d.ts +7 -0
  49. package/dist/StreamingMessage.svelte.d.ts.map +1 -1
  50. package/dist/Switch.svelte +5 -1
  51. package/dist/Tabs.svelte +21 -7
  52. package/dist/Textarea.svelte +36 -9
  53. package/dist/Tile.svelte +165 -0
  54. package/dist/Tile.svelte.d.ts +24 -0
  55. package/dist/Tile.svelte.d.ts.map +1 -0
  56. package/dist/Toggle.svelte +5 -1
  57. package/dist/Toggletip.svelte +5 -2
  58. package/dist/TreeView.svelte +213 -0
  59. package/dist/TreeView.svelte.d.ts +20 -0
  60. package/dist/TreeView.svelte.d.ts.map +1 -0
  61. package/dist/UnorderedList.svelte +4 -3
  62. package/dist/UnorderedList.svelte.d.ts +3 -2
  63. package/dist/UnorderedList.svelte.d.ts.map +1 -1
  64. package/dist/index.d.ts +19 -0
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +11 -0
  67. package/package.json +4 -2
@@ -387,6 +387,12 @@
387
387
  outline-offset: 1px;
388
388
  }
389
389
 
390
+ @media (prefers-reduced-motion: reduce) {
391
+ .st-areaChart__dot {
392
+ transition: none;
393
+ }
394
+ }
395
+
390
396
  .st-areaChart__tooltip {
391
397
  background: var(--st-component-areaChart-tooltipBackground, var(--st-semantic-surface-inverse));
392
398
  border-radius: var(--st-radius-sm, 0.25rem);
@@ -32,38 +32,53 @@
32
32
  </button>
33
33
 
34
34
  <style>
35
+ /* Anatomy-driven: shape / density / typography / focus all read the themed
36
+ --st-component-button-anatomy-* tokens (rebuilt per theme via
37
+ createComponent). Fallbacks mirror the Sent Tech base = no regression. */
35
38
  .st-button {
36
- border: 1px solid transparent;
37
- border-radius: var(--st-component-button-radius, 0.375rem);
38
- cursor: pointer;
39
+ border-width: var(--st-component-button-anatomy-shape-borderWidth, 1px);
40
+ border-style: var(--st-component-button-anatomy-shape-borderStyle, solid);
41
+ border-color: transparent;
42
+ border-radius: var(--st-component-button-anatomy-shape-radius, 0.375rem);
43
+ cursor: var(--st-cursor-interactive, pointer);
39
44
  display: inline-flex;
40
45
  align-items: center;
41
46
  justify-content: center;
42
- gap: var(--st-spacing-2, 0.5rem);
43
- font: inherit;
44
- font-weight: 600;
47
+ gap: var(--st-component-button-anatomy-icon-gap, var(--st-spacing-2, 0.5rem));
48
+ font-family: var(--st-component-button-anatomy-typography-family, inherit);
49
+ font-weight: var(--st-component-button-anatomy-typography-weight, 600);
50
+ line-height: var(--st-component-button-anatomy-typography-lineHeight, 1.2);
51
+ letter-spacing: var(--st-component-button-anatomy-typography-letterSpacing, 0);
52
+ text-transform: var(--st-component-button-anatomy-typography-textTransform, none);
45
53
  transition:
46
54
  background var(--st-motion-fast, 120ms) var(--st-motion-easing, ease),
47
55
  border-color var(--st-motion-fast, 120ms) var(--st-motion-easing, ease),
48
- color var(--st-motion-fast, 120ms) var(--st-motion-easing, ease);
56
+ color var(--st-motion-fast, 120ms) var(--st-motion-easing, ease),
57
+ box-shadow var(--st-motion-fast, 120ms) var(--st-motion-easing, ease);
49
58
  }
50
59
 
51
60
  .st-button--sm {
52
- min-height: 2rem;
53
- padding: 0 0.75rem;
54
- font-size: 0.875rem;
61
+ min-height: var(--st-component-button-anatomy-density-sm-controlHeight, 2rem);
62
+ min-width: var(--st-component-button-anatomy-density-sm-minWidth, 2rem);
63
+ padding: var(--st-component-button-anatomy-density-sm-paddingBlock, 0)
64
+ var(--st-component-button-anatomy-density-sm-paddingInline, 0.75rem);
65
+ font-size: var(--st-component-button-anatomy-density-sm-fontSize, 0.875rem);
55
66
  }
56
67
 
57
68
  .st-button--md {
58
- min-height: 2.5rem;
59
- padding: 0 1rem;
60
- font-size: 0.9375rem;
69
+ min-height: var(--st-component-button-anatomy-density-md-controlHeight, 2.5rem);
70
+ min-width: var(--st-component-button-anatomy-density-md-minWidth, 2.5rem);
71
+ padding: var(--st-component-button-anatomy-density-md-paddingBlock, 0)
72
+ var(--st-component-button-anatomy-density-md-paddingInline, 1rem);
73
+ font-size: var(--st-component-button-anatomy-density-md-fontSize, var(--st-component-button-anatomy-typography-size, 0.9375rem));
61
74
  }
62
75
 
63
76
  .st-button--lg {
64
- min-height: 3rem;
65
- padding: 0 1.25rem;
66
- font-size: 1rem;
77
+ min-height: var(--st-component-button-anatomy-density-lg-controlHeight, 3rem);
78
+ min-width: var(--st-component-button-anatomy-density-lg-minWidth, 3rem);
79
+ padding: var(--st-component-button-anatomy-density-lg-paddingBlock, 0)
80
+ var(--st-component-button-anatomy-density-lg-paddingInline, 1.25rem);
81
+ font-size: var(--st-component-button-anatomy-density-lg-fontSize, 1rem);
67
82
  }
68
83
 
69
84
  .st-button--primary {
@@ -77,6 +92,16 @@
77
92
  border-color: var(--st-semantic-border-subtle);
78
93
  }
79
94
 
95
+ /* Anatomy v1.1.0: hover bg sourced from states.hover.bg (= primaryHover).
96
+ Retires the D1/C1 escape. Secondary uses its own semantic hover surface. */
97
+ .st-button--primary:not(:disabled):hover {
98
+ background: var(--st-component-button-anatomy-states-hover-bg, var(--st-semantic-action-primary));
99
+ }
100
+
101
+ .st-button--secondary:not(:disabled):hover {
102
+ background: var(--st-semantic-action-secondaryHover, var(--st-semantic-action-secondary));
103
+ }
104
+
80
105
  .st-button--ghost {
81
106
  background: transparent;
82
107
  color: var(--st-semantic-text-link);
@@ -88,12 +113,15 @@
88
113
  }
89
114
 
90
115
  .st-button:disabled {
91
- cursor: not-allowed;
92
- opacity: 0.55;
116
+ cursor: var(--st-cursor-disabled, not-allowed);
117
+ opacity: var(--st-component-button-anatomy-states-disabled-opacity, 0.55);
93
118
  }
94
119
 
120
+ /* Shared focus mixin: apply BOTH channels; the strategy (resolved in
121
+ createComponent) sets the live one and no-ops the other. */
95
122
  .st-button:focus-visible {
96
- outline: 2px solid var(--st-component-input-focusRing, var(--st-semantic-border-interactive));
97
- outline-offset: 2px;
123
+ outline: var(--st-component-button-anatomy-focus-outline, 2px solid var(--st-semantic-border-interactive));
124
+ outline-offset: var(--st-component-button-anatomy-focus-offset, 2px);
125
+ box-shadow: var(--st-component-button-anatomy-focus-boxShadow, none);
98
126
  }
99
127
  </style>
package/dist/Card.svelte CHANGED
@@ -21,15 +21,17 @@
21
21
  <style>
22
22
  .st-card {
23
23
  background: var(--st-component-card-background, var(--st-semantic-surface-raised));
24
- border: 1px solid var(--st-component-card-border, var(--st-semantic-border-subtle));
25
- border-radius: var(--st-component-card-radius, 0.5rem);
24
+ border-width: var(--st-component-card-anatomy-shape-borderWidth, 1px);
25
+ border-style: var(--st-component-card-anatomy-shape-borderStyle, solid);
26
+ border-color: var(--st-component-card-border, var(--st-semantic-border-subtle));
27
+ border-radius: var(--st-component-card-anatomy-shape-radius, 0.5rem);
26
28
  box-shadow: var(--st-component-card-shadow, 0 1px 2px rgb(15 23 42 / 0.08));
27
29
  color: var(--st-semantic-text-primary);
28
30
  padding: var(--st-spacing-4, 1rem);
29
31
  }
30
32
 
31
33
  .st-card--interactive {
32
- cursor: pointer;
34
+ cursor: var(--st-cursor-interactive, pointer);
33
35
  transition:
34
36
  box-shadow var(--st-motion-normal, 180ms) var(--st-motion-easing, ease),
35
37
  transform var(--st-motion-normal, 180ms) var(--st-motion-easing, ease);
@@ -37,6 +39,6 @@
37
39
 
38
40
  .st-card--interactive:hover {
39
41
  box-shadow: var(--st-shadow-medium, 0 8px 24px rgb(15 23 42 / 0.12));
40
- transform: translateY(-1px);
42
+ transform: var(--st-component-card-anatomy-states-hover-transform, translateY(-1px));
41
43
  }
42
44
  </style>
@@ -31,6 +31,10 @@
31
31
  }
32
32
 
33
33
  .st-choice__input {
34
+ /* Thématise la coche native (couleur checked) sans markup custom — additif,
35
+ a11y native préservée. Le visuel custom (forme/focus par stratégie) reste
36
+ un item dédié (cf. backlog). */
37
+ accent-color: var(--st-component-selection-checkedBackground, var(--st-semantic-action-primary));
34
38
  height: 1rem;
35
39
  margin: 0.125rem 0 0;
36
40
  width: 1rem;
@@ -233,8 +233,12 @@
233
233
  }
234
234
 
235
235
  .st-field__label {
236
- font-size: 0.875rem;
237
- font-weight: 600;
236
+ font-family: var(--st-component-field-labelTypography-family, inherit);
237
+ font-size: var(--st-component-field-labelTypography-size, 0.875rem);
238
+ font-weight: var(--st-component-field-labelTypography-weight, 600);
239
+ line-height: var(--st-component-field-labelTypography-lineHeight, 1.4);
240
+ letter-spacing: var(--st-component-field-labelTypography-letterSpacing, 0);
241
+ text-transform: var(--st-component-field-labelTypography-textTransform, none);
238
242
  }
239
243
 
240
244
  .st-field__help,
@@ -251,11 +255,15 @@
251
255
  color: var(--st-component-field-errorText, var(--st-semantic-feedback-error));
252
256
  }
253
257
 
258
+ /* Field box = resolved field anatomy (v1.2.0), same as Input. */
254
259
  .st-combobox {
255
260
  align-items: center;
256
- background: var(--st-component-control-background, var(--st-semantic-surface-default));
257
- border: 1px solid var(--st-component-control-border, var(--st-semantic-border-subtle));
258
- border-radius: var(--st-component-control-radius, 0.375rem);
261
+ background: var(--st-component-control-anatomy-field-fillBg, var(--st-component-control-background, var(--st-semantic-surface-default)));
262
+ border-top: var(--st-component-control-anatomy-field-borderTop, var(--st-component-control-anatomy-shape-borderWidth, 1px) var(--st-component-control-anatomy-shape-borderStyle, solid) var(--st-component-control-border, var(--st-semantic-border-subtle)));
263
+ border-right: var(--st-component-control-anatomy-field-borderRight, var(--st-component-control-anatomy-shape-borderWidth, 1px) var(--st-component-control-anatomy-shape-borderStyle, solid) var(--st-component-control-border, var(--st-semantic-border-subtle)));
264
+ border-bottom: var(--st-component-control-anatomy-field-borderBottom, var(--st-component-control-anatomy-shape-borderWidth, 1px) var(--st-component-control-anatomy-shape-borderStyle, solid) var(--st-component-control-border, var(--st-semantic-border-subtle)));
265
+ border-left: var(--st-component-control-anatomy-field-borderLeft, var(--st-component-control-anatomy-shape-borderWidth, 1px) var(--st-component-control-anatomy-shape-borderStyle, solid) var(--st-component-control-border, var(--st-semantic-border-subtle)));
266
+ border-radius: var(--st-component-control-anatomy-shape-radius, 0.375rem);
259
267
  color: var(--st-component-control-text, var(--st-semantic-text-primary));
260
268
  display: inline-flex;
261
269
  transition:
@@ -282,7 +290,10 @@
282
290
 
283
291
  .st-combobox:focus-within {
284
292
  border-color: var(--st-component-control-focusRing, var(--st-semantic-border-interactive));
285
- box-shadow: 0 0 0 2px var(--st-component-control-focusRing, var(--st-semantic-border-interactive));
293
+ outline: var(--st-component-control-anatomy-focus-outline, none);
294
+ outline-offset: var(--st-component-control-anatomy-focus-offset, 0);
295
+ box-shadow: var(--st-component-control-anatomy-focus-boxShadow,
296
+ 0 0 0 2px var(--st-component-control-focusRing, var(--st-semantic-border-interactive)));
286
297
  }
287
298
 
288
299
  .st-combobox:has([aria-invalid="true"]) {
@@ -419,8 +419,12 @@
419
419
  }
420
420
 
421
421
  .st-field__label {
422
- font-size: 0.875rem;
423
- font-weight: 600;
422
+ font-family: var(--st-component-field-labelTypography-family, inherit);
423
+ font-size: var(--st-component-field-labelTypography-size, 0.875rem);
424
+ font-weight: var(--st-component-field-labelTypography-weight, 600);
425
+ line-height: var(--st-component-field-labelTypography-lineHeight, 1.4);
426
+ letter-spacing: var(--st-component-field-labelTypography-letterSpacing, 0);
427
+ text-transform: var(--st-component-field-labelTypography-textTransform, none);
424
428
  }
425
429
 
426
430
  .st-field__help,
@@ -437,11 +441,15 @@
437
441
  color: var(--st-component-field-errorText, var(--st-semantic-feedback-error));
438
442
  }
439
443
 
444
+ /* Field box = resolved field anatomy (v1.2.0), same as Input. */
440
445
  .st-datepicker {
441
446
  align-items: stretch;
442
- background: var(--st-component-control-background, var(--st-semantic-surface-default));
443
- border: 1px solid var(--st-component-control-border, var(--st-semantic-border-subtle));
444
- border-radius: var(--st-component-control-radius, 0.375rem);
447
+ background: var(--st-component-control-anatomy-field-fillBg, var(--st-component-control-background, var(--st-semantic-surface-default)));
448
+ border-top: var(--st-component-control-anatomy-field-borderTop, var(--st-component-control-anatomy-shape-borderWidth, 1px) var(--st-component-control-anatomy-shape-borderStyle, solid) var(--st-component-control-border, var(--st-semantic-border-subtle)));
449
+ border-right: var(--st-component-control-anatomy-field-borderRight, var(--st-component-control-anatomy-shape-borderWidth, 1px) var(--st-component-control-anatomy-shape-borderStyle, solid) var(--st-component-control-border, var(--st-semantic-border-subtle)));
450
+ border-bottom: var(--st-component-control-anatomy-field-borderBottom, var(--st-component-control-anatomy-shape-borderWidth, 1px) var(--st-component-control-anatomy-shape-borderStyle, solid) var(--st-component-control-border, var(--st-semantic-border-subtle)));
451
+ border-left: var(--st-component-control-anatomy-field-borderLeft, var(--st-component-control-anatomy-shape-borderWidth, 1px) var(--st-component-control-anatomy-shape-borderStyle, solid) var(--st-component-control-border, var(--st-semantic-border-subtle)));
452
+ border-radius: var(--st-component-control-anatomy-shape-radius, 0.375rem);
445
453
  color: var(--st-component-control-text, var(--st-semantic-text-primary));
446
454
  display: inline-flex;
447
455
  overflow: hidden;
@@ -469,7 +477,10 @@
469
477
 
470
478
  .st-datepicker:focus-within {
471
479
  border-color: var(--st-component-control-focusRing, var(--st-semantic-border-interactive));
472
- box-shadow: 0 0 0 2px var(--st-component-control-focusRing, var(--st-semantic-border-interactive));
480
+ outline: var(--st-component-control-anatomy-focus-outline, none);
481
+ outline-offset: var(--st-component-control-anatomy-focus-offset, 0);
482
+ box-shadow: var(--st-component-control-anatomy-focus-boxShadow,
483
+ 0 0 0 2px var(--st-component-control-focusRing, var(--st-semantic-border-interactive)));
473
484
  }
474
485
 
475
486
  .st-datepicker:has([aria-invalid="true"]) {
@@ -0,0 +1,169 @@
1
+ <script lang="ts" module>
2
+ export type DonutChartTone =
3
+ | "category1" | "category2" | "category3" | "category4"
4
+ | "category5" | "category6" | "category7" | "category8";
5
+
6
+ export type DonutChartDatum = {
7
+ label: string;
8
+ value: number;
9
+ tone?: DonutChartTone;
10
+ };
11
+ </script>
12
+
13
+ <script lang="ts">
14
+ type DonutChartProps = {
15
+ data: DonutChartDatum[];
16
+ /** Diamètre du SVG. */
17
+ size?: number;
18
+ /** Épaisseur de l'anneau. */
19
+ thickness?: number;
20
+ /** Texte au centre (sinon le total). null pour masquer. */
21
+ centerLabel?: string | null;
22
+ label: string;
23
+ class?: string;
24
+ };
25
+
26
+ let {
27
+ data,
28
+ size = 220,
29
+ thickness = 34,
30
+ centerLabel,
31
+ label,
32
+ class: className
33
+ }: DonutChartProps = $props();
34
+
35
+ const TONES: DonutChartTone[] = [
36
+ "category1", "category2", "category3", "category4",
37
+ "category5", "category6", "category7", "category8"
38
+ ];
39
+
40
+ let hoveredIndex: number | null = $state(null);
41
+
42
+ const slices = $derived.by(() => {
43
+ const total = data.reduce((sum, d) => sum + Math.max(d.value, 0), 0);
44
+ if (total <= 0) return { total: 0, items: [] as Array<{ d: DonutChartDatum; path: string; tone: DonutChartTone; pct: number }> };
45
+ const cx = size / 2;
46
+ const cy = size / 2;
47
+ const rOuter = size / 2 - 2;
48
+ const rInner = Math.max(rOuter - thickness, 1);
49
+ const TWO_PI = Math.PI * 2;
50
+ let angle = -Math.PI / 2; // départ en haut
51
+ const polar = (r: number, a: number): [number, number] => [cx + r * Math.cos(a), cy + r * Math.sin(a)];
52
+ const items = data.map((d, i) => {
53
+ const frac = Math.max(d.value, 0) / total;
54
+ // epsilon pour éviter le cas exact 2π (slice à 100%) non rendu.
55
+ const span = Math.min(frac * TWO_PI, TWO_PI - 0.0001);
56
+ const a0 = angle;
57
+ const a1 = angle + span;
58
+ angle = a1;
59
+ const large = span > Math.PI ? 1 : 0;
60
+ const [x0o, y0o] = polar(rOuter, a0);
61
+ const [x1o, y1o] = polar(rOuter, a1);
62
+ const [x1i, y1i] = polar(rInner, a1);
63
+ const [x0i, y0i] = polar(rInner, a0);
64
+ const path = `M ${x0o} ${y0o} A ${rOuter} ${rOuter} 0 ${large} 1 ${x1o} ${y1o} L ${x1i} ${y1i} A ${rInner} ${rInner} 0 ${large} 0 ${x0i} ${y0i} Z`;
65
+ return { d, path, tone: d.tone ?? TONES[i % TONES.length], pct: frac * 100 };
66
+ });
67
+ return { total, items };
68
+ });
69
+
70
+ const classes = () => ["st-donutChart", className].filter(Boolean).join(" ");
71
+ const fmtPct = (p: number) => `${p.toFixed(p < 10 ? 1 : 0)}%`;
72
+ </script>
73
+
74
+ <div class={classes()} role="img" aria-label={label}>
75
+ <svg viewBox="0 0 {size} {size}" width="100%" height="100%" focusable="false" aria-hidden="true">
76
+ {#if slices.total > 0}
77
+ {#each slices.items as slice, i (slice.d.label)}
78
+ <path
79
+ class="st-donutChart__slice st-donutChart__slice--{slice.tone}"
80
+ class:st-donutChart__slice--dim={hoveredIndex !== null && hoveredIndex !== i}
81
+ d={slice.path}
82
+ tabindex="0"
83
+ role="img"
84
+ aria-label="{slice.d.label}: {slice.d.value} ({fmtPct(slice.pct)})"
85
+ onmouseenter={() => (hoveredIndex = i)}
86
+ onmouseleave={() => (hoveredIndex = null)}
87
+ onfocus={() => (hoveredIndex = i)}
88
+ onblur={() => (hoveredIndex = null)}
89
+ />
90
+ {/each}
91
+ {#if centerLabel !== null}
92
+ <text class="st-donutChart__center" x={size / 2} y={size / 2} text-anchor="middle" dominant-baseline="central">
93
+ {centerLabel ?? slices.total}
94
+ </text>
95
+ {/if}
96
+ {/if}
97
+ </svg>
98
+
99
+ {#if hoveredIndex !== null && slices.items[hoveredIndex]}
100
+ {@const s = slices.items[hoveredIndex]}
101
+ <div class="st-donutChart__tooltip" role="presentation">
102
+ <span class="st-donutChart__tooltipLabel">{s.d.label}</span>
103
+ <span class="st-donutChart__tooltipValue">{s.d.value} · {fmtPct(s.pct)}</span>
104
+ </div>
105
+ {/if}
106
+ </div>
107
+
108
+ <style>
109
+ .st-donutChart {
110
+ color: var(--st-semantic-text-secondary);
111
+ display: block;
112
+ font-family: inherit;
113
+ max-width: 100%;
114
+ position: relative;
115
+ }
116
+
117
+ .st-donutChart svg { display: block; overflow: visible; }
118
+
119
+ .st-donutChart__slice {
120
+ cursor: pointer;
121
+ stroke: var(--st-semantic-surface-default, #fff);
122
+ stroke-width: 1.5;
123
+ transition: opacity 120ms ease;
124
+ }
125
+
126
+ .st-donutChart__slice--dim { opacity: 0.4; }
127
+
128
+ .st-donutChart__slice:focus-visible {
129
+ outline: 2px solid var(--st-semantic-border-interactive);
130
+ outline-offset: 1px;
131
+ }
132
+
133
+ .st-donutChart__slice--category1 { fill: var(--st-semantic-data-category1); }
134
+ .st-donutChart__slice--category2 { fill: var(--st-semantic-data-category2); }
135
+ .st-donutChart__slice--category3 { fill: var(--st-semantic-data-category3); }
136
+ .st-donutChart__slice--category4 { fill: var(--st-semantic-data-category4); }
137
+ .st-donutChart__slice--category5 { fill: var(--st-semantic-data-category5); }
138
+ .st-donutChart__slice--category6 { fill: var(--st-semantic-data-category6); }
139
+ .st-donutChart__slice--category7 { fill: var(--st-semantic-data-category7); }
140
+ .st-donutChart__slice--category8 { fill: var(--st-semantic-data-category8); }
141
+
142
+ .st-donutChart__center {
143
+ fill: var(--st-semantic-text-primary);
144
+ font-size: 1.25rem;
145
+ font-weight: 650;
146
+ }
147
+
148
+ .st-donutChart__tooltip {
149
+ background: var(--st-semantic-surface-inverse);
150
+ border-radius: var(--st-radius-sm, 0.25rem);
151
+ color: var(--st-semantic-text-inverse);
152
+ display: inline-flex;
153
+ flex-direction: column;
154
+ font-size: 0.75rem;
155
+ gap: 0.125rem;
156
+ left: 50%;
157
+ line-height: 1.2;
158
+ padding: 0.375rem 0.5rem;
159
+ pointer-events: none;
160
+ position: absolute;
161
+ top: 50%;
162
+ transform: translate(-50%, -50%);
163
+ white-space: nowrap;
164
+ z-index: 1;
165
+ }
166
+
167
+ .st-donutChart__tooltipLabel { font-weight: 600; }
168
+ .st-donutChart__tooltipValue { opacity: 0.85; }
169
+ </style>
@@ -0,0 +1,21 @@
1
+ export type DonutChartTone = "category1" | "category2" | "category3" | "category4" | "category5" | "category6" | "category7" | "category8";
2
+ export type DonutChartDatum = {
3
+ label: string;
4
+ value: number;
5
+ tone?: DonutChartTone;
6
+ };
7
+ type DonutChartProps = {
8
+ data: DonutChartDatum[];
9
+ /** Diamètre du SVG. */
10
+ size?: number;
11
+ /** Épaisseur de l'anneau. */
12
+ thickness?: number;
13
+ /** Texte au centre (sinon le total). null pour masquer. */
14
+ centerLabel?: string | null;
15
+ label: string;
16
+ class?: string;
17
+ };
18
+ declare const DonutChart: import("svelte").Component<DonutChartProps, {}, "">;
19
+ type DonutChart = ReturnType<typeof DonutChart>;
20
+ export default DonutChart;
21
+ //# sourceMappingURL=DonutChart.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DonutChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/DonutChart.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,cAAc,GACtB,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,GACrD,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;AAE1D,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,uBAAuB;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAiFJ,QAAA,MAAM,UAAU,qDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -0,0 +1,124 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import type { HTMLAttributes } from "svelte/elements";
4
+
5
+ type FooterProps = Omit<HTMLAttributes<HTMLElement>, "class"> & {
6
+ /** Texte de copyright (bas de pied de page). */
7
+ copyright?: string;
8
+ /** aria-label de la région contentinfo. */
9
+ label?: string;
10
+ class?: string;
11
+ /** Marque / logo (zone de tête, à gauche). */
12
+ brand?: Snippet;
13
+ /** Colonnes de liens (zone de tête). */
14
+ columns?: Snippet;
15
+ /** Liens légaux (barre du bas, à droite du copyright). */
16
+ legal?: Snippet;
17
+ /** Contenu libre additionnel sous la barre du bas. */
18
+ children?: Snippet;
19
+ };
20
+
21
+ let {
22
+ copyright,
23
+ label = "Pied de page",
24
+ class: className,
25
+ brand,
26
+ columns,
27
+ legal,
28
+ children,
29
+ ...rest
30
+ }: FooterProps = $props();
31
+
32
+ const classes = () => ["st-footer", className].filter(Boolean).join(" ");
33
+ const hasTop = () => Boolean(brand || columns);
34
+ const hasBottom = () => Boolean(copyright || legal);
35
+ </script>
36
+
37
+ <footer {...rest} class={classes()} aria-label={label}>
38
+ {#if hasTop()}
39
+ <div class="st-footer__top">
40
+ {#if brand}
41
+ <div class="st-footer__brand">{@render brand()}</div>
42
+ {/if}
43
+ {#if columns}
44
+ <div class="st-footer__columns">{@render columns()}</div>
45
+ {/if}
46
+ </div>
47
+ {/if}
48
+
49
+ {#if hasBottom()}
50
+ <div class="st-footer__bottom">
51
+ {#if copyright}
52
+ <span class="st-footer__copyright">{copyright}</span>
53
+ {/if}
54
+ {#if legal}
55
+ <nav class="st-footer__legal" aria-label="Liens légaux">
56
+ {@render legal()}
57
+ </nav>
58
+ {/if}
59
+ </div>
60
+ {/if}
61
+
62
+ {#if children}
63
+ {@render children()}
64
+ {/if}
65
+ </footer>
66
+
67
+ <style>
68
+ .st-footer {
69
+ background: var(--st-component-footer-background, var(--st-semantic-surface-subtle));
70
+ border-top: 1px solid var(--st-component-footer-border, var(--st-semantic-border-subtle));
71
+ color: var(--st-component-footer-text, var(--st-semantic-text-secondary));
72
+ display: flex;
73
+ flex-direction: column;
74
+ gap: var(--st-spacing-4, 1rem);
75
+ padding: var(--st-spacing-8, 2rem) var(--st-spacing-4, 1rem);
76
+ width: 100%;
77
+ }
78
+
79
+ .st-footer__top {
80
+ display: flex;
81
+ flex-wrap: wrap;
82
+ gap: var(--st-spacing-8, 2rem);
83
+ align-items: flex-start;
84
+ justify-content: space-between;
85
+ }
86
+
87
+ .st-footer__brand {
88
+ align-items: center;
89
+ color: var(--st-semantic-text-primary);
90
+ display: inline-flex;
91
+ flex: 0 0 auto;
92
+ gap: var(--st-spacing-3, 0.75rem);
93
+ }
94
+
95
+ .st-footer__columns {
96
+ display: flex;
97
+ flex-wrap: wrap;
98
+ gap: var(--st-spacing-8, 2rem);
99
+ flex: 1 1 auto;
100
+ justify-content: flex-end;
101
+ }
102
+
103
+ .st-footer__bottom {
104
+ align-items: center;
105
+ border-top: 1px solid var(--st-semantic-border-subtle);
106
+ display: flex;
107
+ flex-wrap: wrap;
108
+ gap: var(--st-spacing-4, 1rem);
109
+ justify-content: space-between;
110
+ padding-top: var(--st-spacing-4, 1rem);
111
+ }
112
+
113
+ .st-footer__copyright {
114
+ color: var(--st-semantic-text-muted);
115
+ font-size: 0.8125rem;
116
+ }
117
+
118
+ .st-footer__legal {
119
+ display: flex;
120
+ flex-wrap: wrap;
121
+ gap: var(--st-spacing-4, 1rem);
122
+ font-size: 0.8125rem;
123
+ }
124
+ </style>
@@ -0,0 +1,21 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { HTMLAttributes } from "svelte/elements";
3
+ type FooterProps = Omit<HTMLAttributes<HTMLElement>, "class"> & {
4
+ /** Texte de copyright (bas de pied de page). */
5
+ copyright?: string;
6
+ /** aria-label de la région contentinfo. */
7
+ label?: string;
8
+ class?: string;
9
+ /** Marque / logo (zone de tête, à gauche). */
10
+ brand?: Snippet;
11
+ /** Colonnes de liens (zone de tête). */
12
+ columns?: Snippet;
13
+ /** Liens légaux (barre du bas, à droite du copyright). */
14
+ legal?: Snippet;
15
+ /** Contenu libre additionnel sous la barre du bas. */
16
+ children?: Snippet;
17
+ };
18
+ declare const Footer: import("svelte").Component<FooterProps, {}, "">;
19
+ type Footer = ReturnType<typeof Footer>;
20
+ export default Footer;
21
+ //# sourceMappingURL=Footer.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Footer.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Footer.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGpD,KAAK,WAAW,GAAG,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC,GAAG;IAC9D,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wCAAwC;IACxC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAwDJ,QAAA,MAAM,MAAM,iDAAwC,CAAC;AACrD,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC;AACxC,eAAe,MAAM,CAAC"}
@@ -0,0 +1,63 @@
1
+ <script lang="ts" module>
2
+ export type HighlightTone = "neutral" | "info" | "success" | "warning" | "error";
3
+ </script>
4
+
5
+ <script lang="ts">
6
+ import type { Snippet } from "svelte";
7
+ import type { HTMLAttributes } from "svelte/elements";
8
+
9
+ type HighlightProps = Omit<HTMLAttributes<HTMLElement>, "class" | "title"> & {
10
+ /** Tonalité de l'encart (couleur d'accent). */
11
+ tone?: HighlightTone;
12
+ /** Titre optionnel de la mise en avant. */
13
+ title?: string;
14
+ class?: string;
15
+ children?: Snippet;
16
+ };
17
+
18
+ let {
19
+ tone = "neutral",
20
+ title,
21
+ class: className,
22
+ children,
23
+ ...rest
24
+ }: HighlightProps = $props();
25
+
26
+ const classes = () => ["st-highlight", `st-highlight--${tone}`, className].filter(Boolean).join(" ");
27
+ </script>
28
+
29
+ <aside {...rest} class={classes()}>
30
+ {#if title}<p class="st-highlight__title">{title}</p>{/if}
31
+ {#if children}<div class="st-highlight__body">{@render children()}</div>{/if}
32
+ </aside>
33
+
34
+ <style>
35
+ /* Mise en avant / encart éditorial (DSFR "Mise en avant"). */
36
+ .st-highlight {
37
+ --st-highlight-accent: var(--st-semantic-action-primary);
38
+ background: var(--st-semantic-surface-subtle);
39
+ border-left: 4px solid var(--st-highlight-accent);
40
+ border-radius: var(--st-radius-md, 0.375rem);
41
+ color: var(--st-semantic-text-primary);
42
+ padding: var(--st-spacing-4, 1rem);
43
+ }
44
+
45
+ .st-highlight--info { --st-highlight-accent: var(--st-semantic-feedback-info); }
46
+ .st-highlight--success { --st-highlight-accent: var(--st-semantic-feedback-success); }
47
+ .st-highlight--warning { --st-highlight-accent: var(--st-semantic-feedback-warning); }
48
+ .st-highlight--error { --st-highlight-accent: var(--st-semantic-feedback-error); }
49
+
50
+ .st-highlight__title {
51
+ font-size: 0.9375rem;
52
+ font-weight: 650;
53
+ margin: 0 0 var(--st-spacing-2, 0.5rem);
54
+ }
55
+
56
+ .st-highlight__body {
57
+ color: var(--st-semantic-text-secondary);
58
+ font-size: 0.9375rem;
59
+ line-height: 1.55;
60
+ }
61
+
62
+ .st-highlight__body :global(p) { margin: 0; }
63
+ </style>