@sentropic/design-system-svelte 0.34.50 → 0.34.51

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 (74) hide show
  1. package/README.md +62 -0
  2. package/dist/AnomalySwimLaneChart.svelte +10 -1
  3. package/dist/AnomalySwimLaneChart.svelte.d.ts +2 -0
  4. package/dist/AnomalySwimLaneChart.svelte.d.ts.map +1 -1
  5. package/dist/CalendarHeatmapChart.svelte +11 -1
  6. package/dist/CalendarHeatmapChart.svelte.d.ts +2 -0
  7. package/dist/CalendarHeatmapChart.svelte.d.ts.map +1 -1
  8. package/dist/Combobox.svelte +5 -1
  9. package/dist/Combobox.svelte.d.ts.map +1 -1
  10. package/dist/ContourChart.svelte +76 -13
  11. package/dist/ContourChart.svelte.d.ts +3 -1
  12. package/dist/ContourChart.svelte.d.ts.map +1 -1
  13. package/dist/Dashboard.svelte +155 -0
  14. package/dist/Dashboard.svelte.d.ts +21 -0
  15. package/dist/Dashboard.svelte.d.ts.map +1 -0
  16. package/dist/DashboardGrid.svelte +237 -0
  17. package/dist/DashboardGrid.svelte.d.ts +24 -0
  18. package/dist/DashboardGrid.svelte.d.ts.map +1 -0
  19. package/dist/DataTable.svelte +3 -1
  20. package/dist/DataTable.svelte.d.ts +1 -0
  21. package/dist/DataTable.svelte.d.ts.map +1 -1
  22. package/dist/DatePicker.svelte +33 -28
  23. package/dist/DatePicker.svelte.d.ts.map +1 -1
  24. package/dist/Density2DChart.svelte +10 -1
  25. package/dist/Density2DChart.svelte.d.ts +2 -0
  26. package/dist/Density2DChart.svelte.d.ts.map +1 -1
  27. package/dist/Dropdown.svelte +33 -9
  28. package/dist/Dropdown.svelte.d.ts.map +1 -1
  29. package/dist/EventFeedPanel.svelte +3 -3
  30. package/dist/EventFeedPanel.svelte.d.ts +1 -1
  31. package/dist/EventFeedPanel.svelte.d.ts.map +1 -1
  32. package/dist/FileUploader.svelte +7 -3
  33. package/dist/Footer.svelte +75 -11
  34. package/dist/Footer.svelte.d.ts +16 -6
  35. package/dist/Footer.svelte.d.ts.map +1 -1
  36. package/dist/ForceGraph.svelte +9 -3
  37. package/dist/ForceGraph.svelte.d.ts +4 -0
  38. package/dist/ForceGraph.svelte.d.ts.map +1 -1
  39. package/dist/HeatmapChart.svelte +39 -3
  40. package/dist/HeatmapChart.svelte.d.ts +4 -1
  41. package/dist/HeatmapChart.svelte.d.ts.map +1 -1
  42. package/dist/KanbanBoard.svelte +144 -0
  43. package/dist/KanbanBoard.svelte.d.ts +23 -0
  44. package/dist/KanbanBoard.svelte.d.ts.map +1 -0
  45. package/dist/ListReportPage.svelte +184 -0
  46. package/dist/ListReportPage.svelte.d.ts +46 -0
  47. package/dist/ListReportPage.svelte.d.ts.map +1 -0
  48. package/dist/MasterDetail.svelte +267 -0
  49. package/dist/MasterDetail.svelte.d.ts +35 -0
  50. package/dist/MasterDetail.svelte.d.ts.map +1 -0
  51. package/dist/ObjectPage.svelte +222 -0
  52. package/dist/ObjectPage.svelte.d.ts +46 -0
  53. package/dist/ObjectPage.svelte.d.ts.map +1 -0
  54. package/dist/OrderedList.svelte +7 -12
  55. package/dist/OrderedList.svelte.d.ts.map +1 -1
  56. package/dist/PointAndFigureChart.svelte +18 -11
  57. package/dist/PointAndFigureChart.svelte.d.ts +1 -1
  58. package/dist/PointAndFigureChart.svelte.d.ts.map +1 -1
  59. package/dist/RenkoChart.svelte +40 -13
  60. package/dist/RenkoChart.svelte.d.ts +1 -1
  61. package/dist/RenkoChart.svelte.d.ts.map +1 -1
  62. package/dist/VectorFieldChart.svelte +5 -5
  63. package/dist/VectorFieldChart.svelte.d.ts +1 -1
  64. package/dist/VectorFieldChart.svelte.d.ts.map +1 -1
  65. package/dist/WindBarbChart.svelte +5 -5
  66. package/dist/WindBarbChart.svelte.d.ts +1 -1
  67. package/dist/WindBarbChart.svelte.d.ts.map +1 -1
  68. package/dist/Wizard.svelte +125 -0
  69. package/dist/Wizard.svelte.d.ts +25 -0
  70. package/dist/Wizard.svelte.d.ts.map +1 -0
  71. package/dist/index.d.ts +24 -10
  72. package/dist/index.d.ts.map +1 -1
  73. package/dist/index.js +7 -0
  74. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # `@sentropic/design-system-svelte`
2
+
3
+ Svelte implementation of the Sentropic design-system component catalog. The package ships ESM JavaScript, TypeScript declarations, and the shared component CSS.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @sentropic/design-system-svelte @sentropic/design-system-themes
9
+ ```
10
+
11
+ Svelte is a peer dependency:
12
+
13
+ ```bash
14
+ npm install svelte
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ Import the package CSS once at the app or preview boundary, then render components inside `ThemeProvider`.
20
+
21
+ ```svelte
22
+ <script>
23
+ import { Button, Card, ThemeProvider } from "@sentropic/design-system-svelte";
24
+ import "@sentropic/design-system-svelte/styles.css";
25
+ </script>
26
+
27
+ <ThemeProvider>
28
+ <Card>
29
+ <strong>Release plan</strong>
30
+ <Button>Open plan</Button>
31
+ </Card>
32
+ </ThemeProvider>
33
+ ```
34
+
35
+ `ThemeProvider` defaults to the Sent Tech theme. Tenant themes can be supplied from `@sentropic/design-system-themes`, `@sentropic/design-system-theme-dsfr`, or `@sentropic/design-system-theme-carbon`.
36
+
37
+ ```svelte
38
+ <script>
39
+ import { ThemeProvider } from "@sentropic/design-system-svelte";
40
+ import { carbonTheme } from "@sentropic/design-system-theme-carbon";
41
+ </script>
42
+
43
+ <ThemeProvider theme={carbonTheme}>
44
+ <!-- your content -->
45
+ </ThemeProvider>
46
+ ```
47
+
48
+ ## Exports
49
+
50
+ - `@sentropic/design-system-svelte`: all public Svelte components and TypeScript props.
51
+ - `@sentropic/design-system-svelte/styles.css`: component CSS consumed by every theme.
52
+
53
+ The package includes declarations through `dist/index.d.ts` and marks `dist/styles.css` as a side effect so bundlers keep the stylesheet import.
54
+
55
+ ## Build
56
+
57
+ ```bash
58
+ npm --workspace @sentropic/design-system-svelte run build
59
+ npm --workspace @sentropic/design-system-svelte run test
60
+ ```
61
+
62
+ Publishing is handled by `.github/workflows/svelte-publish.yml` with a `svelte-v*` tag.
@@ -23,6 +23,8 @@
23
23
  | "category1" | "category2" | "category3" | "category4"
24
24
  | "category5" | "category6" | "category7" | "category8";
25
25
 
26
+ export type AnomalySwimLaneChartScale = "categorical" | "sequential";
27
+
26
28
  export type AnomalySwimLaneBucket = {
27
29
  at: number;
28
30
  score: number;
@@ -40,6 +42,7 @@
40
42
  type AnomalySwimLaneChartProps = {
41
43
  data: AnomalySwimLaneSeries[];
42
44
  max?: number;
45
+ scale?: AnomalySwimLaneChartScale;
43
46
  label?: string;
44
47
  width?: number;
45
48
  height?: number;
@@ -50,6 +53,7 @@
50
53
  let {
51
54
  data = [],
52
55
  max,
56
+ scale = "sequential",
53
57
  label,
54
58
  width,
55
59
  height = 300,
@@ -73,6 +77,10 @@
73
77
  return TONES[index];
74
78
  }
75
79
 
80
+ function normalizedScale(value: AnomalySwimLaneChartScale | undefined): AnomalySwimLaneChartScale {
81
+ return value === "categorical" ? "categorical" : "sequential";
82
+ }
83
+
76
84
  // Tronque une étiquette à la largeur de la marge gauche (approx. par char).
77
85
  function ellipsize(text: string, maxChars: number): string {
78
86
  if (text.length <= maxChars) return text;
@@ -87,6 +95,7 @@
87
95
  }
88
96
 
89
97
  let hoveredKey: string | null = $state(null);
98
+ const resolvedScale = $derived(normalizedScale(scale));
90
99
 
91
100
  const plotWidth = $derived(Math.max(resolvedWidth - MARGIN.left - MARGIN.right, 1));
92
101
  const plotHeight = $derived(Math.max(height - MARGIN.top - MARGIN.bottom, 1));
@@ -190,7 +199,7 @@
190
199
  return null;
191
200
  });
192
201
 
193
- const classes = () => ["st-anomalySwimLaneChart", className].filter(Boolean).join(" ");
202
+ const classes = () => ["st-anomalySwimLaneChart", `st-anomalySwimLaneChart--${resolvedScale}`, className].filter(Boolean).join(" ");
194
203
  </script>
195
204
 
196
205
  <div class={classes()}>
@@ -19,6 +19,7 @@
19
19
  * class string
20
20
  */
21
21
  export type AnomalySwimLaneTone = "category1" | "category2" | "category3" | "category4" | "category5" | "category6" | "category7" | "category8";
22
+ export type AnomalySwimLaneChartScale = "categorical" | "sequential";
22
23
  export type AnomalySwimLaneBucket = {
23
24
  at: number;
24
25
  score: number;
@@ -30,6 +31,7 @@ export type AnomalySwimLaneSeries = {
30
31
  type AnomalySwimLaneChartProps = {
31
32
  data: AnomalySwimLaneSeries[];
32
33
  max?: number;
34
+ scale?: AnomalySwimLaneChartScale;
33
35
  label?: string;
34
36
  width?: number;
35
37
  height?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"AnomalySwimLaneChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/AnomalySwimLaneChart.svelte.ts"],"names":[],"mappings":"AAGE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,mBAAmB,GAC3B,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,GACrD,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;AAE1D,MAAM,MAAM,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,qBAAqB,EAAE,CAAC;CAClC,CAAC;AAMF,KAAK,yBAAyB,GAAG;IAC/B,IAAI,EAAE,qBAAqB,EAAE,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA+MJ,QAAA,MAAM,oBAAoB,+DAAwC,CAAC;AACnE,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACpE,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"AnomalySwimLaneChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/AnomalySwimLaneChart.svelte.ts"],"names":[],"mappings":"AAGE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,MAAM,mBAAmB,GAC3B,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,GACrD,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;AAE1D,MAAM,MAAM,yBAAyB,GAAG,aAAa,GAAG,YAAY,CAAC;AAErE,MAAM,MAAM,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,qBAAqB,EAAE,CAAC;CAClC,CAAC;AAMF,KAAK,yBAAyB,GAAG;IAC/B,IAAI,EAAE,qBAAqB,EAAE,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,yBAAyB,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAqNJ,QAAA,MAAM,oBAAoB,+DAAwC,CAAC;AACnE,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACpE,eAAe,oBAAoB,CAAC"}
@@ -16,6 +16,8 @@
16
16
  date: string;
17
17
  value: number;
18
18
  };
19
+
20
+ export type CalendarHeatmapChartScale = "categorical" | "sequential";
19
21
  </script>
20
22
 
21
23
  <script lang="ts">
@@ -29,6 +31,7 @@
29
31
  type CalendarHeatmapChartProps = {
30
32
  data: CalendarHeatmapChartDatum[];
31
33
  label: string;
34
+ scale?: CalendarHeatmapChartScale;
32
35
  width?: number;
33
36
  height?: number;
34
37
  class?: string;
@@ -37,6 +40,7 @@
37
40
  let {
38
41
  data = [],
39
42
  label,
43
+ scale = "sequential",
40
44
  width = 480,
41
45
  height = 140,
42
46
  class: className
@@ -75,6 +79,12 @@
75
79
  return Math.round((tsB - tsA) / 86400000);
76
80
  }
77
81
 
82
+ function normalizedScale(value: CalendarHeatmapChartScale | undefined): CalendarHeatmapChartScale {
83
+ return value === "categorical" ? "categorical" : "sequential";
84
+ }
85
+
86
+ const resolvedScale = $derived(normalizedScale(scale));
87
+
78
88
  // Group data by week and day-of-week
79
89
  const grid = $derived.by(() => {
80
90
  if (data.length === 0) return { cells: [], weeks: 0, monthLabels: [] };
@@ -187,7 +197,7 @@
187
197
  hoveredDate = target.getAttribute("data-chart-date") ?? null;
188
198
  }
189
199
 
190
- const classes = () => ["st-calendarHeatmapChart", className].filter(Boolean).join(" ");
200
+ const classes = () => ["st-calendarHeatmapChart", `st-calendarHeatmapChart--${resolvedScale}`, className].filter(Boolean).join(" ");
191
201
  </script>
192
202
 
193
203
  <div class={classes()}>
@@ -15,9 +15,11 @@ export type CalendarHeatmapChartDatum = {
15
15
  date: string;
16
16
  value: number;
17
17
  };
18
+ export type CalendarHeatmapChartScale = "categorical" | "sequential";
18
19
  type CalendarHeatmapChartProps = {
19
20
  data: CalendarHeatmapChartDatum[];
20
21
  label: string;
22
+ scale?: CalendarHeatmapChartScale;
21
23
  width?: number;
22
24
  height?: number;
23
25
  class?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"CalendarHeatmapChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/CalendarHeatmapChart.svelte.ts"],"names":[],"mappings":"AAGE;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAMF,KAAK,yBAAyB,GAAG;IAC/B,IAAI,EAAE,yBAAyB,EAAE,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAgNJ,QAAA,MAAM,oBAAoB,+DAAwC,CAAC;AACnE,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACpE,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"CalendarHeatmapChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/CalendarHeatmapChart.svelte.ts"],"names":[],"mappings":"AAGE;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,aAAa,GAAG,YAAY,CAAC;AAMrE,KAAK,yBAAyB,GAAG;IAC/B,IAAI,EAAE,yBAAyB,EAAE,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,yBAAyB,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAuNJ,QAAA,MAAM,oBAAoB,+DAAwC,CAAC;AACnE,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACpE,eAAe,oBAAoB,CAAC"}
@@ -56,6 +56,8 @@
56
56
  let expanded = $state(false);
57
57
  let activeIndex = $state(-1);
58
58
 
59
+ const inputId = $derived((rest as { id?: string }).id ?? `st-combobox-${Math.random().toString(36).slice(2, 9)}`);
60
+
59
61
  const fieldClasses = () => ["st-field", className].filter(Boolean).join(" ");
60
62
  const groupClasses = () => ["st-combobox", `st-combobox--${size}`].join(" ");
61
63
  const isInvalid = () => invalid || Boolean(errorText);
@@ -93,6 +95,7 @@
93
95
 
94
96
  function clear() {
95
97
  value = "";
98
+ activeIndex = -1;
96
99
  onchange?.("");
97
100
  }
98
101
 
@@ -131,11 +134,12 @@
131
134
  </script>
132
135
 
133
136
  <div class={fieldClasses()}>
134
- <label class="st-field__control">
137
+ <label class="st-field__control" for={inputId}>
135
138
  {#if label}<span class="st-field__label">{label}</span>{/if}
136
139
  <span class={groupClasses()}>
137
140
  <input
138
141
  {...rest}
142
+ id={inputId}
139
143
  type="text"
140
144
  class="st-combobox__control"
141
145
  role="combobox"
@@ -1 +1 @@
1
- {"version":3,"file":"Combobox.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Combobox.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAGzD,KAAK,aAAa,GAAG,IAAI,CACvB,mBAAmB,EACnB,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,CACjD,GAAG;IACF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC,CAAC;AAsJJ,QAAA,MAAM,QAAQ,wDAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"Combobox.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Combobox.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAGzD,KAAK,aAAa,GAAG,IAAI,CACvB,mBAAmB,EACnB,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,CACjD,GAAG;IACF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC,CAAC;AAyJJ,QAAA,MAAM,QAAQ,wDAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
@@ -27,30 +27,34 @@
27
27
  | "category1" | "category2" | "category3" | "category4"
28
28
  | "category5" | "category6" | "category7" | "category8";
29
29
 
30
+ export type ContourChartScale = "categorical" | "sequential";
31
+
30
32
  export type ContourChartDatum = {
31
33
  x: number;
32
34
  y: number;
33
35
  /** Valeur scalaire de la cellule : pilote la bande de couleur. */
34
36
  value: number;
35
37
  };
36
- </script>
37
-
38
- <script lang="ts">
39
- import ChartDataList from "./ChartDataList.svelte";
40
38
 
41
- type ContourChartProps = {
39
+ export type ContourChartProps = {
42
40
  data: ContourChartDatum[];
43
41
  levels?: number;
42
+ scale?: ContourChartScale;
44
43
  label?: string;
45
44
  width?: number;
46
45
  height?: number;
47
46
  size?: number;
48
47
  class?: string;
49
48
  };
49
+ </script>
50
+
51
+ <script lang="ts">
52
+ import ChartDataList from "./ChartDataList.svelte";
50
53
 
51
54
  let {
52
55
  data = [],
53
56
  levels = 6,
57
+ scale = "sequential",
54
58
  label,
55
59
  width = 640,
56
60
  height = 320,
@@ -90,12 +94,17 @@
90
94
  return r0 + ((v - d0) * (r1 - r0)) / (d1 - d0);
91
95
  }
92
96
 
97
+ function normalizedScale(value: ContourChartScale | undefined): ContourChartScale {
98
+ return value === "categorical" ? "categorical" : "sequential";
99
+ }
100
+
93
101
  function fmt(v: number): string {
94
102
  if (Math.abs(v) >= 1000) return `${(v / 1000).toFixed(v % 1000 === 0 ? 0 : 1)}k`;
95
103
  return Number.isInteger(v) ? String(v) : v.toFixed(1);
96
104
  }
97
105
 
98
106
  let hoveredKey: string | null = $state(null);
107
+ const resolvedScale = $derived(normalizedScale(scale));
99
108
 
100
109
  // Points valides : coordonnées finies, valeur finie.
101
110
  const validData = $derived(
@@ -120,13 +129,17 @@
120
129
  return { min, max };
121
130
  });
122
131
 
132
+ function toneForBand(band: number): ContourChartTone {
133
+ const toneIndex = Math.max(0, Math.min(TONES.length - 1, Math.floor((band / Math.max(levelCount - 1, 1)) * (TONES.length - 1))));
134
+ return TONES[toneIndex];
135
+ }
136
+
123
137
  // Palier (0..levelCount-1) puis ton catégoriel : valeur normalisée 0..1 → bande.
124
138
  function bandOf(value: number): { band: number; tone: ContourChartTone } {
125
139
  const { min, max } = valueRange;
126
140
  const ratio = max > min ? (value - min) / (max - min) : 0;
127
141
  const band = Math.max(0, Math.min(levelCount - 1, Math.floor(ratio * levelCount)));
128
- const toneIndex = Math.max(0, Math.min(TONES.length - 1, Math.floor((band / Math.max(levelCount - 1, 1)) * (TONES.length - 1))));
129
- return { band, tone: TONES[toneIndex] };
142
+ return { band, tone: toneForBand(band) };
130
143
  }
131
144
 
132
145
  const scales = $derived.by(() => {
@@ -157,17 +170,22 @@
157
170
 
158
171
  // Une bande rectangulaire par cellule de grille, peinte selon sa value.
159
172
  const cells = $derived.by(() => {
160
- const { xMin, xMax, yMin, yMax, plotW, plotH } = scales;
173
+ const { xMin, xMax, yMin, yMax, plotW, plotH, xValues, yValues } = scales;
161
174
  const { dx, dy } = cellSpan;
175
+ const xIndexByValue = new Map(xValues.map((value, index) => [value, index]));
176
+ const yIndexByValue = new Map(yValues.map((value, index) => [value, index]));
162
177
  return validData.map((d, i) => {
163
178
  const left = MARGIN.left + scaleLinear(d.x - dx / 2, xMin, xMax, 0, plotW);
164
179
  const right = MARGIN.left + scaleLinear(d.x + dx / 2, xMin, xMax, 0, plotW);
165
180
  const top = MARGIN.top + scaleLinear(d.y + dy / 2, yMin, yMax, plotH, 0);
166
181
  const bottom = MARGIN.top + scaleLinear(d.y - dy / 2, yMin, yMax, plotH, 0);
167
- const { tone } = bandOf(d.value);
182
+ const { band, tone } = bandOf(d.value);
168
183
  return {
169
184
  key: `${i}`,
170
185
  datum: d,
186
+ band,
187
+ col: xIndexByValue.get(d.x) ?? 0,
188
+ row: yIndexByValue.get(d.y) ?? 0,
171
189
  x: Math.min(left, right),
172
190
  y: Math.min(top, bottom),
173
191
  width: Math.abs(right - left),
@@ -179,14 +197,41 @@
179
197
  });
180
198
  });
181
199
 
200
+ const contourSegments = $derived.by(() => {
201
+ const byGrid = new Map(cells.map((cell) => [`${cell.col}:${cell.row}`, cell]));
202
+ const segments = [];
203
+ for (const cell of cells) {
204
+ const right = byGrid.get(`${cell.col + 1}:${cell.row}`);
205
+ if (right && right.band !== cell.band) {
206
+ segments.push({
207
+ key: `${cell.key}:right`,
208
+ x1: cell.x + cell.width,
209
+ y1: cell.y,
210
+ x2: cell.x + cell.width,
211
+ y2: cell.y + cell.height
212
+ });
213
+ }
214
+ const upper = byGrid.get(`${cell.col}:${cell.row + 1}`);
215
+ if (upper && upper.band !== cell.band) {
216
+ segments.push({
217
+ key: `${cell.key}:top`,
218
+ x1: cell.x,
219
+ y1: cell.y,
220
+ x2: cell.x + cell.width,
221
+ y2: cell.y
222
+ });
223
+ }
224
+ }
225
+ return segments;
226
+ });
227
+
182
228
  const dataValueItems = $derived(
183
229
  validData.map((d) => `x ${d.x}, y ${d.y} · ${fmt(d.value)}`)
184
230
  );
185
231
 
186
232
  const legendItems = $derived(
187
233
  Array.from({ length: levelCount }, (_, band) => {
188
- const toneIndex = Math.max(0, Math.min(TONES.length - 1, Math.floor((band / Math.max(levelCount - 1, 1)) * (TONES.length - 1))));
189
- return { band, tone: TONES[toneIndex] };
234
+ return { band, tone: toneForBand(band) };
190
235
  })
191
236
  );
192
237
  const hasLegend = $derived(validData.length > 0);
@@ -205,7 +250,7 @@
205
250
  return cells.find((c) => c.key === hoveredKey) ?? null;
206
251
  });
207
252
 
208
- const classes = () => ["st-contourChart", className].filter(Boolean).join(" ");
253
+ const classes = () => ["st-contourChart", `st-contourChart--${resolvedScale}`, className].filter(Boolean).join(" ");
209
254
  </script>
210
255
 
211
256
  <div class={classes()}>
@@ -237,6 +282,16 @@
237
282
  />
238
283
  {/each}
239
284
 
285
+ {#each contourSegments as segment (segment.key)}
286
+ <line
287
+ class="st-contourChart__isoline"
288
+ x1={segment.x1}
289
+ y1={segment.y1}
290
+ x2={segment.x2}
291
+ y2={segment.y2}
292
+ />
293
+ {/each}
294
+
240
295
  <!-- gridlines + ticks Y -->
241
296
  {#each scales.yTicks as t (t)}
242
297
  {@const y = MARGIN.top + scaleLinear(t, scales.yMin, scales.yMax, scales.plotH, 0)}
@@ -319,7 +374,7 @@
319
374
 
320
375
  .st-contourChart__cell {
321
376
  cursor: pointer;
322
- stroke: var(--st-semantic-surface-default, Canvas);
377
+ stroke: var(--st-semantic-surface-default);
323
378
  stroke-width: 0.5;
324
379
  transition: opacity 120ms ease;
325
380
  }
@@ -328,6 +383,14 @@
328
383
  opacity: 0.35;
329
384
  }
330
385
 
386
+ .st-contourChart__isoline {
387
+ pointer-events: none;
388
+ stroke: var(--st-semantic-border-strong);
389
+ stroke-linecap: round;
390
+ stroke-linejoin: round;
391
+ stroke-width: 1.5;
392
+ }
393
+
331
394
  .st-contourChart__cell--category1 { fill: var(--st-semantic-data-category1); }
332
395
  .st-contourChart__cell--category2 { fill: var(--st-semantic-data-category2); }
333
396
  .st-contourChart__cell--category3 { fill: var(--st-semantic-data-category3); }
@@ -23,15 +23,17 @@
23
23
  * class string
24
24
  */
25
25
  export type ContourChartTone = "category1" | "category2" | "category3" | "category4" | "category5" | "category6" | "category7" | "category8";
26
+ export type ContourChartScale = "categorical" | "sequential";
26
27
  export type ContourChartDatum = {
27
28
  x: number;
28
29
  y: number;
29
30
  /** Valeur scalaire de la cellule : pilote la bande de couleur. */
30
31
  value: number;
31
32
  };
32
- type ContourChartProps = {
33
+ export type ContourChartProps = {
33
34
  data: ContourChartDatum[];
34
35
  levels?: number;
36
+ scale?: ContourChartScale;
35
37
  label?: string;
36
38
  width?: number;
37
39
  height?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"ContourChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/ContourChart.svelte.ts"],"names":[],"mappings":"AAGE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,gBAAgB,GACxB,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,GACrD,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;AAE1D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAMF,KAAK,iBAAiB,GAAG;IACvB,IAAI,EAAE,iBAAiB,EAAE,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA4NJ,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"ContourChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/ContourChart.svelte.ts"],"names":[],"mappings":"AAGE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,MAAM,gBAAgB,GACxB,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,GACrD,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;AAE1D,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG,YAAY,CAAC;AAE7D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,iBAAiB,EAAE,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA6QJ,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -0,0 +1,155 @@
1
+ <script lang="ts" module>
2
+ import type { Snippet } from "svelte";
3
+ import type { SideNavItem } from "./SideNav.svelte";
4
+
5
+ export interface DashboardNavItem extends SideNavItem {}
6
+
7
+ export interface DashboardKpi {
8
+ label: string;
9
+ value: string;
10
+ unit?: string;
11
+ trend?: "up" | "down" | "flat";
12
+ }
13
+
14
+ export type DashboardProps = {
15
+ appTitle: string;
16
+ navItems?: DashboardNavItem[];
17
+ pageTitle: string;
18
+ kpis?: DashboardKpi[];
19
+ children?: Snippet;
20
+ };
21
+ </script>
22
+
23
+ <script lang="ts">
24
+ import SideNav from "./SideNav.svelte";
25
+
26
+ let {
27
+ appTitle,
28
+ navItems = [],
29
+ pageTitle,
30
+ kpis = [],
31
+ children,
32
+ }: DashboardProps = $props();
33
+ </script>
34
+
35
+ <div class="st-dash">
36
+ <header class="st-dash__header">
37
+ <span class="st-dash__appTitle">{appTitle}</span>
38
+ </header>
39
+ <div class="st-dash__body">
40
+ {#if navItems.length > 0}
41
+ <aside class="st-dash__aside">
42
+ <SideNav items={navItems} />
43
+ </aside>
44
+ {/if}
45
+ <main class="st-dash__main">
46
+ <h1 class="st-dash__pageTitle">{pageTitle}</h1>
47
+ {#if kpis.length > 0}
48
+ <div class="st-dash__kpi-row">
49
+ {#each kpis as kpi}
50
+ <div class="st-dash__kpi">
51
+ <span class="st-dash__kpiLabel">{kpi.label}</span>
52
+ <span class="st-dash__kpiValue">
53
+ {kpi.value}{#if kpi.unit}<span class="st-dash__kpiUnit"> {kpi.unit}</span>{/if}
54
+ </span>
55
+ {#if kpi.trend}
56
+ <span class="st-dash__kpiTrend st-dash__kpiTrend--{kpi.trend}" aria-label="Tendance {kpi.trend}">
57
+ {#if kpi.trend === "up"}↑{:else if kpi.trend === "down"}↓{:else}→{/if}
58
+ </span>
59
+ {/if}
60
+ </div>
61
+ {/each}
62
+ </div>
63
+ {/if}
64
+ {#if children}
65
+ <div class="st-dash__content">
66
+ {@render children()}
67
+ </div>
68
+ {/if}
69
+ </main>
70
+ </div>
71
+ </div>
72
+
73
+ <style>
74
+ .st-dash {
75
+ display: grid;
76
+ grid-template-rows: auto 1fr;
77
+ min-block-size: 100vh;
78
+ background: var(--st-semantic-surface-default);
79
+ color: var(--st-semantic-text-primary);
80
+ }
81
+ .st-dash__header {
82
+ border-block-end: 1px solid var(--st-semantic-border-subtle);
83
+ padding: var(--st-spacing-3, 0.75rem) var(--st-spacing-6, 1.5rem);
84
+ display: flex;
85
+ align-items: center;
86
+ }
87
+ .st-dash__appTitle {
88
+ font-weight: 700;
89
+ font-size: 1rem;
90
+ }
91
+ .st-dash__body {
92
+ display: grid;
93
+ grid-template-columns: 220px 1fr;
94
+ overflow: hidden;
95
+ }
96
+ .st-dash__aside {
97
+ border-inline-end: 1px solid var(--st-semantic-border-subtle);
98
+ background: var(--st-semantic-surface-raised);
99
+ overflow-y: auto;
100
+ }
101
+ .st-dash__main {
102
+ display: flex;
103
+ flex-direction: column;
104
+ gap: var(--st-spacing-6, 1.5rem);
105
+ padding: var(--st-spacing-6, 1.5rem);
106
+ overflow-y: auto;
107
+ }
108
+ .st-dash__pageTitle {
109
+ font-size: 1.5rem;
110
+ font-weight: 700;
111
+ margin: 0;
112
+ }
113
+ .st-dash__kpi-row {
114
+ display: grid;
115
+ grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
116
+ gap: var(--st-spacing-4, 1rem);
117
+ }
118
+ .st-dash__kpi {
119
+ display: flex;
120
+ flex-direction: column;
121
+ gap: var(--st-spacing-1, 0.25rem);
122
+ padding: var(--st-spacing-4, 1rem) var(--st-spacing-5, 1.25rem);
123
+ background: var(--st-semantic-surface-raised);
124
+ border: 1px solid var(--st-semantic-border-subtle);
125
+ border-radius: var(--st-radius-md, 0.5rem);
126
+ }
127
+ .st-dash__kpiLabel {
128
+ font-size: 0.75rem;
129
+ color: var(--st-semantic-text-secondary);
130
+ text-transform: uppercase;
131
+ letter-spacing: 0.05em;
132
+ }
133
+ .st-dash__kpiValue {
134
+ font-size: 1.75rem;
135
+ font-weight: 700;
136
+ line-height: 1;
137
+ }
138
+ .st-dash__kpiUnit {
139
+ font-size: 0.875rem;
140
+ font-weight: 400;
141
+ color: var(--st-semantic-text-secondary);
142
+ }
143
+ .st-dash__kpiTrend {
144
+ font-size: 0.875rem;
145
+ font-weight: 600;
146
+ }
147
+ .st-dash__kpiTrend--up { color: var(--st-semantic-color-success, #16a34a); }
148
+ .st-dash__kpiTrend--down { color: var(--st-semantic-color-error, #dc2626); }
149
+ .st-dash__kpiTrend--flat { color: var(--st-semantic-text-secondary); }
150
+ .st-dash__content {
151
+ display: flex;
152
+ flex-direction: column;
153
+ gap: var(--st-spacing-4, 1rem);
154
+ }
155
+ </style>
@@ -0,0 +1,21 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { SideNavItem } from "./SideNav.svelte";
3
+ export interface DashboardNavItem extends SideNavItem {
4
+ }
5
+ export interface DashboardKpi {
6
+ label: string;
7
+ value: string;
8
+ unit?: string;
9
+ trend?: "up" | "down" | "flat";
10
+ }
11
+ export type DashboardProps = {
12
+ appTitle: string;
13
+ navItems?: DashboardNavItem[];
14
+ pageTitle: string;
15
+ kpis?: DashboardKpi[];
16
+ children?: Snippet;
17
+ };
18
+ declare const Dashboard: import("svelte").Component<DashboardProps, {}, "">;
19
+ type Dashboard = ReturnType<typeof Dashboard>;
20
+ export default Dashboard;
21
+ //# sourceMappingURL=Dashboard.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Dashboard.svelte.d.ts","sourceRoot":"","sources":["../src/lib/Dashboard.svelte.ts"],"names":[],"mappings":"AAGE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,gBAAiB,SAAQ,WAAW;CAAG;AAExD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;CAChC;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,YAAY,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AA6DJ,QAAA,MAAM,SAAS,oDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}