@sentropic/design-system-svelte 0.34.0 → 0.34.20

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 (98) hide show
  1. package/dist/AppHeader.svelte +159 -1
  2. package/dist/AppHeader.svelte.d.ts +18 -1
  3. package/dist/AppHeader.svelte.d.ts.map +1 -1
  4. package/dist/ArcDiagramChart.svelte +380 -0
  5. package/dist/ArcDiagramChart.svelte.d.ts +43 -0
  6. package/dist/ArcDiagramChart.svelte.d.ts.map +1 -0
  7. package/dist/AreaRangeChart.svelte +487 -0
  8. package/dist/AreaRangeChart.svelte.d.ts +38 -0
  9. package/dist/AreaRangeChart.svelte.d.ts.map +1 -0
  10. package/dist/AreaSplineRangeChart.svelte +478 -0
  11. package/dist/AreaSplineRangeChart.svelte.d.ts +37 -0
  12. package/dist/AreaSplineRangeChart.svelte.d.ts.map +1 -0
  13. package/dist/BellCurveChart.svelte +487 -0
  14. package/dist/BellCurveChart.svelte.d.ts +40 -0
  15. package/dist/BellCurveChart.svelte.d.ts.map +1 -0
  16. package/dist/Calendar.svelte +11 -0
  17. package/dist/ChatThread.svelte +32 -1
  18. package/dist/ChatThread.svelte.d.ts +14 -0
  19. package/dist/ChatThread.svelte.d.ts.map +1 -1
  20. package/dist/ColumnPyramidChart.svelte +332 -0
  21. package/dist/ColumnPyramidChart.svelte.d.ts +35 -0
  22. package/dist/ColumnPyramidChart.svelte.d.ts.map +1 -0
  23. package/dist/ColumnRangeChart.svelte +432 -0
  24. package/dist/ColumnRangeChart.svelte.d.ts +42 -0
  25. package/dist/ColumnRangeChart.svelte.d.ts.map +1 -0
  26. package/dist/Combobox.svelte +3 -0
  27. package/dist/ContentSwitcher.svelte +1 -1
  28. package/dist/DataTable.svelte.d.ts +1 -1
  29. package/dist/DatePicker.svelte +3 -0
  30. package/dist/DependencyWheelChart.svelte +413 -0
  31. package/dist/DependencyWheelChart.svelte.d.ts +42 -0
  32. package/dist/DependencyWheelChart.svelte.d.ts.map +1 -0
  33. package/dist/DumbbellChart.svelte +403 -0
  34. package/dist/DumbbellChart.svelte.d.ts +44 -0
  35. package/dist/DumbbellChart.svelte.d.ts.map +1 -0
  36. package/dist/ErrorBarChart.svelte +428 -0
  37. package/dist/ErrorBarChart.svelte.d.ts +40 -0
  38. package/dist/ErrorBarChart.svelte.d.ts.map +1 -0
  39. package/dist/GanttChart.svelte +410 -0
  40. package/dist/GanttChart.svelte.d.ts +39 -0
  41. package/dist/GanttChart.svelte.d.ts.map +1 -0
  42. package/dist/HLCChart.svelte +330 -0
  43. package/dist/HLCChart.svelte.d.ts +32 -0
  44. package/dist/HLCChart.svelte.d.ts.map +1 -0
  45. package/dist/HeikinAshiChart.svelte +365 -0
  46. package/dist/HeikinAshiChart.svelte.d.ts +37 -0
  47. package/dist/HeikinAshiChart.svelte.d.ts.map +1 -0
  48. package/dist/HollowCandlestickChart.svelte +357 -0
  49. package/dist/HollowCandlestickChart.svelte.d.ts +34 -0
  50. package/dist/HollowCandlestickChart.svelte.d.ts.map +1 -0
  51. package/dist/Input.svelte +3 -0
  52. package/dist/ItemChart.svelte +389 -0
  53. package/dist/ItemChart.svelte.d.ts +67 -0
  54. package/dist/ItemChart.svelte.d.ts.map +1 -0
  55. package/dist/LollipopChart.svelte +1 -1
  56. package/dist/MultiSelect.svelte +3 -0
  57. package/dist/NumberInput.svelte +3 -0
  58. package/dist/OHLCChart.svelte +343 -0
  59. package/dist/OHLCChart.svelte.d.ts +33 -0
  60. package/dist/OHLCChart.svelte.d.ts.map +1 -0
  61. package/dist/OrganizationChart.svelte +284 -0
  62. package/dist/OrganizationChart.svelte.d.ts +19 -0
  63. package/dist/OrganizationChart.svelte.d.ts.map +1 -0
  64. package/dist/PasswordInput.svelte +3 -0
  65. package/dist/PolygonChart.svelte +189 -0
  66. package/dist/PolygonChart.svelte.d.ts +17 -0
  67. package/dist/PolygonChart.svelte.d.ts.map +1 -0
  68. package/dist/Search.svelte +7 -5
  69. package/dist/Select.svelte +3 -0
  70. package/dist/StreamgraphChart.svelte +283 -0
  71. package/dist/StreamgraphChart.svelte.d.ts +23 -0
  72. package/dist/StreamgraphChart.svelte.d.ts.map +1 -0
  73. package/dist/StreamingMessage.svelte +44 -2
  74. package/dist/StreamingMessage.svelte.d.ts +18 -1
  75. package/dist/StreamingMessage.svelte.d.ts.map +1 -1
  76. package/dist/TileMapChart.svelte +314 -0
  77. package/dist/TileMapChart.svelte.d.ts +45 -0
  78. package/dist/TileMapChart.svelte.d.ts.map +1 -0
  79. package/dist/TimePicker.svelte +3 -0
  80. package/dist/TimelineChart.svelte +362 -0
  81. package/dist/TimelineChart.svelte.d.ts +22 -0
  82. package/dist/TimelineChart.svelte.d.ts.map +1 -0
  83. package/dist/TreegraphChart.svelte +281 -0
  84. package/dist/TreegraphChart.svelte.d.ts +19 -0
  85. package/dist/TreegraphChart.svelte.d.ts.map +1 -0
  86. package/dist/VariablePieChart.svelte +313 -0
  87. package/dist/VariablePieChart.svelte.d.ts +52 -0
  88. package/dist/VariablePieChart.svelte.d.ts.map +1 -0
  89. package/dist/VennChart.svelte +348 -0
  90. package/dist/VennChart.svelte.d.ts +72 -0
  91. package/dist/VennChart.svelte.d.ts.map +1 -0
  92. package/dist/WordCloudChart.svelte +279 -0
  93. package/dist/WordCloudChart.svelte.d.ts +18 -0
  94. package/dist/WordCloudChart.svelte.d.ts.map +1 -0
  95. package/dist/index.d.ts +48 -0
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +24 -0
  98. package/package.json +5 -3
@@ -0,0 +1,189 @@
1
+ <script lang="ts" module>
2
+ export type PolygonChartTone =
3
+ | "category1" | "category2" | "category3" | "category4"
4
+ | "category5" | "category6" | "category7" | "category8";
5
+
6
+ export type PolygonChartPoint = {
7
+ x: number;
8
+ y: number;
9
+ };
10
+ </script>
11
+
12
+ <script lang="ts">
13
+ import ChartDataList from "./ChartDataList.svelte";
14
+
15
+ type PolygonChartProps = {
16
+ data: PolygonChartPoint[];
17
+ label: string;
18
+ tone?: PolygonChartTone;
19
+ width?: number;
20
+ height?: number;
21
+ class?: string;
22
+ };
23
+
24
+ let {
25
+ data,
26
+ label,
27
+ tone = "category1",
28
+ width = 480,
29
+ height = 360,
30
+ class: className
31
+ }: PolygonChartProps = $props();
32
+
33
+ const MARGIN = { top: 14, right: 18, bottom: 36, left: 48 };
34
+
35
+ function niceTicks(min: number, max: number, target = 5): number[] {
36
+ if (!Number.isFinite(min) || !Number.isFinite(max) || min === max) {
37
+ return [Number.isFinite(max) ? max : 0];
38
+ }
39
+ const range = max - min;
40
+ const rough = range / Math.max(target - 1, 1);
41
+ const pow = Math.pow(10, Math.floor(Math.log10(rough)));
42
+ const norm = rough / pow;
43
+ let step: number;
44
+ if (norm < 1.5) step = pow;
45
+ else if (norm < 3) step = 2 * pow;
46
+ else if (norm < 7) step = 5 * pow;
47
+ else step = 10 * pow;
48
+ const start = Math.floor(min / step) * step;
49
+ const end = Math.ceil(max / step) * step;
50
+ const ticks: number[] = [];
51
+ for (let v = start; v <= end + step / 2; v += step) ticks.push(Number(v.toFixed(10)));
52
+ return ticks;
53
+ }
54
+
55
+ function scaleLinear(v: number, d0: number, d1: number, r0: number, r1: number) {
56
+ if (d1 === d0) return r0;
57
+ return r0 + ((v - d0) * (r1 - r0)) / (d1 - d0);
58
+ }
59
+
60
+ function fmt(v: number): string {
61
+ if (Math.abs(v) >= 1000) return `${(v / 1000).toFixed(v % 1000 === 0 ? 0 : 1)}k`;
62
+ return Number.isInteger(v) ? String(v) : v.toFixed(1);
63
+ }
64
+
65
+ let hoveredIndex: number | null = $state(null);
66
+
67
+ // Non-finite coordinates are dropped before anything else.
68
+ const validData = $derived(
69
+ (data ?? []).filter((d) => Number.isFinite(d.x) && Number.isFinite(d.y))
70
+ );
71
+
72
+ const scales = $derived.by(() => {
73
+ const xs = validData.map((d) => d.x);
74
+ const ys = validData.map((d) => d.y);
75
+ const xTicks = niceTicks(Math.min(...xs), Math.max(...xs));
76
+ const yTicks = niceTicks(Math.min(...ys), Math.max(...ys));
77
+ const plotW = Math.max(width - MARGIN.left - MARGIN.right, 1);
78
+ const plotH = Math.max(height - MARGIN.top - MARGIN.bottom, 1);
79
+ return {
80
+ xTicks, yTicks,
81
+ xMin: xTicks[0], xMax: xTicks[xTicks.length - 1],
82
+ yMin: yTicks[0], yMax: yTicks[yTicks.length - 1],
83
+ plotW, plotH
84
+ };
85
+ });
86
+
87
+ const points = $derived.by(() => {
88
+ const { xMin, xMax, yMin, yMax, plotW, plotH } = scales;
89
+ return validData.map((d) => ({
90
+ cx: MARGIN.left + scaleLinear(d.x, xMin, xMax, 0, plotW),
91
+ cy: MARGIN.top + scaleLinear(d.y, yMin, yMax, plotH, 0),
92
+ datum: d
93
+ }));
94
+ });
95
+
96
+ const polygonPoints = $derived(points.map((p) => `${p.cx},${p.cy}`).join(" "));
97
+
98
+ const dataValueItems = $derived(validData.map((d) => `x ${d.x}, y ${d.y}`));
99
+
100
+ function handleVisualPointerMove(event: PointerEvent) {
101
+ const target = event.target;
102
+ if (!(target instanceof Element)) {
103
+ hoveredIndex = null;
104
+ return;
105
+ }
106
+ const index = Number(target.getAttribute("data-chart-index"));
107
+ hoveredIndex = Number.isInteger(index) ? index : null;
108
+ }
109
+
110
+ const classes = () => ["st-polygonChart", className].filter(Boolean).join(" ");
111
+ </script>
112
+
113
+ <div class={classes()}>
114
+ <div
115
+ class="st-polygonChart__visual"
116
+ role="img"
117
+ aria-label={label}
118
+ onpointermove={handleVisualPointerMove}
119
+ onpointerleave={() => (hoveredIndex = null)}
120
+ >
121
+ <svg viewBox="0 0 {width} {height}" preserveAspectRatio="xMidYMid meet" width="100%" height="100%" focusable="false" aria-hidden="true">
122
+ <!-- gridlines + ticks Y -->
123
+ {#each scales.yTicks as t (t)}
124
+ {@const y = MARGIN.top + scaleLinear(t, scales.yMin, scales.yMax, scales.plotH, 0)}
125
+ <line class="st-polygonChart__grid" x1={MARGIN.left} x2={width - MARGIN.right} y1={y} y2={y} />
126
+ <text class="st-polygonChart__tick" x={MARGIN.left - 6} y={y} text-anchor="end" dominant-baseline="middle">{fmt(t)}</text>
127
+ {/each}
128
+ <!-- ticks X -->
129
+ {#each scales.xTicks as t (t)}
130
+ {@const x = MARGIN.left + scaleLinear(t, scales.xMin, scales.xMax, 0, scales.plotW)}
131
+ <text class="st-polygonChart__tick" x={x} y={height - MARGIN.bottom + 16} text-anchor="middle">{fmt(t)}</text>
132
+ {/each}
133
+
134
+ <!-- axes -->
135
+ <line class="st-polygonChart__axis" x1={MARGIN.left} x2={MARGIN.left} y1={MARGIN.top} y2={height - MARGIN.bottom} />
136
+ <line class="st-polygonChart__axis" x1={MARGIN.left} x2={width - MARGIN.right} y1={height - MARGIN.bottom} y2={height - MARGIN.bottom} />
137
+
138
+ <!-- closed filled polygon connecting the points in order -->
139
+ {#if points.length >= 2}
140
+ <polygon class="st-polygonChart__polygon st-polygonChart__polygon--{tone}" points={polygonPoints} />
141
+ {/if}
142
+
143
+ <!-- vertex markers -->
144
+ {#each points as p, i (i)}
145
+ <circle
146
+ class="st-polygonChart__vertex st-polygonChart__vertex--{tone}"
147
+ cx={p.cx}
148
+ cy={p.cy}
149
+ r="3.5"
150
+ data-chart-index={i}
151
+ />
152
+ {/each}
153
+ </svg>
154
+ </div>
155
+
156
+ <ChartDataList {label} items={dataValueItems} />
157
+
158
+ {#if hoveredIndex !== null && points[hoveredIndex]}
159
+ {@const p = points[hoveredIndex]}
160
+ <div class="st-polygonChart__tooltip" role="presentation" style="left: {(p.cx / width) * 100}%; top: {(p.cy / height) * 100}%">
161
+ <span class="st-polygonChart__tooltipValue">x {p.datum.x} · y {p.datum.y}</span>
162
+ </div>
163
+ {/if}
164
+ </div>
165
+
166
+ <style>
167
+ .st-polygonChart { color: var(--st-semantic-text-secondary); display: block; font-family: inherit; position: relative; width: 100%; }
168
+ .st-polygonChart svg, .st-polygonChart__visual { display: block; overflow: visible; }
169
+ .st-polygonChart__grid { stroke: var(--st-semantic-border-subtle); stroke-dasharray: 2 3; stroke-width: 1; opacity: 0.7; }
170
+ .st-polygonChart__axis { stroke: var(--st-semantic-border-subtle); stroke-width: 1; }
171
+ .st-polygonChart__tick { fill: var(--st-semantic-text-secondary); font-size: 0.6875rem; }
172
+ .st-polygonChart__polygon { fill: currentColor; fill-opacity: 0.18; stroke: currentColor; stroke-width: 2; stroke-linejoin: round; }
173
+ .st-polygonChart__vertex { fill: currentColor; }
174
+ .st-polygonChart__polygon--category1, .st-polygonChart__vertex--category1 { color: var(--st-semantic-data-category1); }
175
+ .st-polygonChart__polygon--category2, .st-polygonChart__vertex--category2 { color: var(--st-semantic-data-category2); }
176
+ .st-polygonChart__polygon--category3, .st-polygonChart__vertex--category3 { color: var(--st-semantic-data-category3); }
177
+ .st-polygonChart__polygon--category4, .st-polygonChart__vertex--category4 { color: var(--st-semantic-data-category4); }
178
+ .st-polygonChart__polygon--category5, .st-polygonChart__vertex--category5 { color: var(--st-semantic-data-category5); }
179
+ .st-polygonChart__polygon--category6, .st-polygonChart__vertex--category6 { color: var(--st-semantic-data-category6); }
180
+ .st-polygonChart__polygon--category7, .st-polygonChart__vertex--category7 { color: var(--st-semantic-data-category7); }
181
+ .st-polygonChart__polygon--category8, .st-polygonChart__vertex--category8 { color: var(--st-semantic-data-category8); }
182
+ .st-polygonChart__tooltip {
183
+ background: var(--st-semantic-surface-inverse); border-radius: var(--st-radius-sm, 0.25rem);
184
+ color: var(--st-semantic-text-inverse); display: inline-flex; flex-direction: column;
185
+ font-size: 0.75rem; gap: 0.125rem; line-height: 1.2; padding: 0.375rem 0.5rem;
186
+ pointer-events: none; position: absolute; transform: translate(-50%, calc(-100% - 8px)); white-space: nowrap; z-index: 1;
187
+ }
188
+ .st-polygonChart__tooltipValue { opacity: 0.85; }
189
+ </style>
@@ -0,0 +1,17 @@
1
+ export type PolygonChartTone = "category1" | "category2" | "category3" | "category4" | "category5" | "category6" | "category7" | "category8";
2
+ export type PolygonChartPoint = {
3
+ x: number;
4
+ y: number;
5
+ };
6
+ type PolygonChartProps = {
7
+ data: PolygonChartPoint[];
8
+ label: string;
9
+ tone?: PolygonChartTone;
10
+ width?: number;
11
+ height?: number;
12
+ class?: string;
13
+ };
14
+ declare const PolygonChart: import("svelte").Component<PolygonChartProps, {}, "">;
15
+ type PolygonChart = ReturnType<typeof PolygonChart>;
16
+ export default PolygonChart;
17
+ //# sourceMappingURL=PolygonChart.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PolygonChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/PolygonChart.svelte.ts"],"names":[],"mappings":"AAGE,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;CACX,CAAC;AAMF,KAAK,iBAAiB,GAAG;IACvB,IAAI,EAAE,iBAAiB,EAAE,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA6IJ,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -143,14 +143,17 @@
143
143
 
144
144
  .st-search--sm {
145
145
  min-height: var(--st-component-control-smHeight, 2rem);
146
+ font-size: 0.8125rem;
146
147
  }
147
148
 
148
149
  .st-search--md {
149
150
  min-height: var(--st-component-control-mdHeight, 2.5rem);
151
+ font-size: 0.875rem;
150
152
  }
151
153
 
152
154
  .st-search--lg {
153
155
  min-height: var(--st-component-control-lgHeight, 3rem);
156
+ font-size: 1rem;
154
157
  }
155
158
 
156
159
  .st-search:hover:not(:has(input:disabled)) {
@@ -210,18 +213,17 @@
210
213
  align-items: center;
211
214
  background: transparent;
212
215
  border: 0;
213
- border-radius: 50%;
214
- color: inherit;
216
+ border-radius: var(--st-component-control-anatomy-shape-radius, 0.375rem);
217
+ color: var(--st-semantic-text-secondary);
215
218
  cursor: pointer;
216
219
  display: inline-flex;
217
220
  flex: 0 0 auto;
218
221
  font: inherit;
219
- font-size: 1.125rem;
220
222
  height: 1.5rem;
221
223
  justify-content: center;
222
224
  line-height: 1;
223
- margin-inline-end: 0.375rem;
224
- padding: 0;
225
+ margin-inline-end: 0.25rem;
226
+ padding: 0 0.25rem;
225
227
  transition: background-color var(--st-motion-fast, 120ms) var(--st-motion-easing, ease);
226
228
  width: 1.5rem;
227
229
  }
@@ -144,14 +144,17 @@
144
144
 
145
145
  .st-select--sm {
146
146
  min-height: var(--st-component-control-smHeight, 2rem);
147
+ font-size: 0.8125rem;
147
148
  }
148
149
 
149
150
  .st-select--md {
150
151
  min-height: var(--st-component-control-mdHeight, 2.5rem);
152
+ font-size: 0.875rem;
151
153
  }
152
154
 
153
155
  .st-select--lg {
154
156
  min-height: var(--st-component-control-lgHeight, 3rem);
157
+ font-size: 1rem;
155
158
  }
156
159
 
157
160
  .st-select:focus-visible {
@@ -0,0 +1,283 @@
1
+ <script lang="ts" module>
2
+ export type StreamgraphChartTone =
3
+ | "category1" | "category2" | "category3" | "category4"
4
+ | "category5" | "category6" | "category7" | "category8";
5
+
6
+ // Une série pour un point d'abscisse : un libellé + sa valeur.
7
+ export type StreamgraphChartSeriesValue = {
8
+ label: string;
9
+ value: number;
10
+ tone?: StreamgraphChartTone;
11
+ };
12
+
13
+ // Un point sur l'axe X (catégorie / temps) avec ses séries empilées.
14
+ export type StreamgraphChartDatum = {
15
+ category: string;
16
+ values: StreamgraphChartSeriesValue[];
17
+ };
18
+ </script>
19
+
20
+ <script lang="ts">
21
+ import ChartDataList from "./ChartDataList.svelte";
22
+
23
+ type StreamgraphChartProps = {
24
+ data: StreamgraphChartDatum[];
25
+ width?: number;
26
+ height?: number;
27
+ label: string;
28
+ smooth?: boolean;
29
+ showLegend?: boolean;
30
+ class?: string;
31
+ };
32
+
33
+ let {
34
+ data = [],
35
+ width = 480,
36
+ height = 240,
37
+ label,
38
+ smooth = true,
39
+ showLegend = true,
40
+ class: className
41
+ }: StreamgraphChartProps = $props();
42
+
43
+ const MARGIN = { top: 12, right: 16, bottom: 32, left: 16 };
44
+ const TONES = ["category1","category2","category3","category4","category5","category6","category7","category8"] as const;
45
+
46
+ const scaleLinear = (v: number, d0: number, d1: number, r0: number, r1: number) =>
47
+ d1 === d0 ? r0 : r0 + ((v - d0) * (r1 - r0)) / (d1 - d0);
48
+
49
+ function buildLinearPath(pts: { x: number; y: number }[]): string {
50
+ return pts.map((p, i) => `${i === 0 ? "M" : "L"}${p.x.toFixed(2)},${p.y.toFixed(2)}`).join(" ");
51
+ }
52
+
53
+ function buildSmoothPath(pts: { x: number; y: number }[]): string {
54
+ if (pts.length < 2) return buildLinearPath(pts);
55
+ const t = 0.18;
56
+ let d = `M${pts[0].x.toFixed(2)},${pts[0].y.toFixed(2)}`;
57
+ for (let i = 0; i < pts.length - 1; i++) {
58
+ const p0 = pts[i - 1] ?? pts[i];
59
+ const p1 = pts[i];
60
+ const p2 = pts[i + 1];
61
+ const p3 = pts[i + 2] ?? p2;
62
+ const c1x = p1.x + (p2.x - p0.x) * t;
63
+ const c1y = p1.y + (p2.y - p0.y) * t;
64
+ const c2x = p2.x - (p3.x - p1.x) * t;
65
+ const c2y = p2.y - (p3.y - p1.y) * t;
66
+ d += ` C${c1x.toFixed(2)},${c1y.toFixed(2)} ${c2x.toFixed(2)},${c2y.toFixed(2)} ${p2.x.toFixed(2)},${p2.y.toFixed(2)}`;
67
+ }
68
+ return d;
69
+ }
70
+
71
+ // Valeur non-finie ou négative → 0 (le streamgraph empile des grandeurs ≥ 0).
72
+ const safeValue = (v: number) => (Number.isFinite(v) && v > 0 ? v : 0);
73
+
74
+ const plotWidth = $derived(Math.max(width - MARGIN.left - MARGIN.right, 1));
75
+ const plotHeight = $derived(Math.max(height - MARGIN.top - MARGIN.bottom, 1));
76
+
77
+ // Ordre stable des séries (1re apparition) + ton associé.
78
+ const series = $derived.by(() => {
79
+ const seen = new Map<string, StreamgraphChartTone>();
80
+ data.forEach((d) =>
81
+ d.values.forEach((sv, i) => {
82
+ if (!seen.has(sv.label)) seen.set(sv.label, sv.tone ?? TONES[seen.size % TONES.length]);
83
+ })
84
+ );
85
+ return [...seen.entries()].map(([seriesLabel, tone]) => ({ seriesLabel, tone }));
86
+ });
87
+
88
+ // Échelle Y symétrique : demi-amplitude = max des sommes empilées / 2.
89
+ const halfMax = $derived.by(() => {
90
+ let max = 0;
91
+ for (const d of data) {
92
+ let sum = 0;
93
+ for (const sv of d.values) sum += safeValue(sv.value);
94
+ if (sum > max) max = sum;
95
+ }
96
+ return max / 2 || 1;
97
+ });
98
+
99
+ // Pour chaque x : abscisse en px + bornes basse/haute (px) par série,
100
+ // baseline « wiggle » centrée (pile centrée autour de 0 ⇒ baseline = -somme/2).
101
+ const layout = $derived.by(() => {
102
+ const n = data.length;
103
+ const xs = data.map((_, i) => {
104
+ const denom = Math.max(n - 1, 1);
105
+ const xRatio = n === 1 ? 0.5 : i / denom;
106
+ return MARGIN.left + xRatio * plotWidth;
107
+ });
108
+ const midY = MARGIN.top + plotHeight / 2;
109
+ // valToY : une grandeur signée (par rapport au centre) → coordonnée px.
110
+ const valToY = (signed: number) => midY - scaleLinear(signed, 0, halfMax, 0, plotHeight / 2);
111
+
112
+ // bands[seriesIndex][xIndex] = { x, top, bottom } en px.
113
+ const bands = series.map(() => [] as { x: number; top: number; bottom: number }[]);
114
+ data.forEach((d, xi) => {
115
+ const total = d.values.reduce((s, sv) => s + safeValue(sv.value), 0);
116
+ let acc = -total / 2; // baseline centrée
117
+ series.forEach((s, si) => {
118
+ const sv = d.values.find((v) => v.label === s.seriesLabel);
119
+ const v = sv ? safeValue(sv.value) : 0;
120
+ const lower = acc;
121
+ const upper = acc + v;
122
+ acc = upper;
123
+ bands[si].push({ x: xs[xi], top: valToY(upper), bottom: valToY(lower) });
124
+ });
125
+ });
126
+ return { xs, bands };
127
+ });
128
+
129
+ // Aire fermée par série (haut gauche→droite, puis bas droite→gauche).
130
+ const areas = $derived.by(() =>
131
+ series.map((s, si) => {
132
+ const band = layout.bands[si];
133
+ if (!band || band.length === 0) return { tone: s.tone, seriesLabel: s.seriesLabel, d: "" };
134
+ const topPts = band.map((b) => ({ x: b.x, y: b.top }));
135
+ const bottomPts = band.map((b) => ({ x: b.x, y: b.bottom })).reverse();
136
+ const topPath = smooth ? buildSmoothPath(topPts) : buildLinearPath(topPts);
137
+ const bottomPath = (smooth ? buildSmoothPath(bottomPts) : buildLinearPath(bottomPts)).replace(/^M/, "L");
138
+ return { tone: s.tone, seriesLabel: s.seriesLabel, d: `${topPath} ${bottomPath} Z` };
139
+ })
140
+ );
141
+
142
+ // Étiquettes d'axe X : jusqu'à 5 catégories réparties.
143
+ const xTickEntries = $derived.by(() => {
144
+ const n = data.length;
145
+ if (n === 0) return [] as { x: number; label: string }[];
146
+ const target = Math.min(5, n);
147
+ const stride = Math.max(1, Math.round((n - 1) / (target - 1 || 1)));
148
+ const entries: { x: number; label: string }[] = [];
149
+ for (let i = 0; i < n; i += stride) entries.push({ x: layout.xs[i], label: data[i].category });
150
+ const lastIdx = n - 1;
151
+ if (entries[entries.length - 1]?.label !== data[lastIdx].category) {
152
+ entries.push({ x: layout.xs[lastIdx], label: data[lastIdx].category });
153
+ }
154
+ return entries;
155
+ });
156
+
157
+ // Liste accessible : total par série + total global.
158
+ const dataValueItems = $derived.by(() => {
159
+ const items = series.map((s) => {
160
+ const total = data.reduce((sum, d) => {
161
+ const sv = d.values.find((v) => v.label === s.seriesLabel);
162
+ return sum + (sv ? safeValue(sv.value) : 0);
163
+ }, 0);
164
+ return `${s.seriesLabel}: ${total}`;
165
+ });
166
+ const grand = data.reduce((sum, d) => sum + d.values.reduce((s, sv) => s + safeValue(sv.value), 0), 0);
167
+ if (series.length > 0) items.push(`Total: ${grand}`);
168
+ return items;
169
+ });
170
+
171
+ let hovered: number | null = $state(null);
172
+
173
+ function handleVisualPointerMove(event: PointerEvent) {
174
+ const target = event.target;
175
+ if (!(target instanceof Element)) {
176
+ hovered = null;
177
+ return;
178
+ }
179
+ const si = Number(target.getAttribute("data-series-index"));
180
+ hovered = Number.isInteger(si) ? si : null;
181
+ }
182
+
183
+ const tooltip = $derived.by(() => {
184
+ if (hovered === null || !series[hovered]) return null;
185
+ const s = series[hovered];
186
+ const band = layout.bands[hovered];
187
+ if (!band || band.length === 0) return null;
188
+ const mid = band[Math.floor(band.length / 2)];
189
+ const total = data.reduce((sum, d) => {
190
+ const sv = d.values.find((v) => v.label === s.seriesLabel);
191
+ return sum + (sv ? safeValue(sv.value) : 0);
192
+ }, 0);
193
+ return { label: s.seriesLabel, value: total, cx: mid.x, cy: (mid.top + mid.bottom) / 2 };
194
+ });
195
+
196
+ const classes = () => ["st-streamgraphChart", className].filter(Boolean).join(" ");
197
+ </script>
198
+
199
+ <div class={classes()}>
200
+ <div
201
+ class="st-streamgraphChart__visual"
202
+ role="img"
203
+ aria-label={label}
204
+ onpointermove={handleVisualPointerMove}
205
+ onpointerleave={() => (hovered = null)}
206
+ >
207
+ <svg viewBox="0 0 {width} {height}" preserveAspectRatio="xMidYMid meet" width="100%" height="100%" focusable="false" aria-hidden="true">
208
+ <line class="st-streamgraphChart__axis" x1={MARGIN.left} x2={width - MARGIN.right} y1={height - MARGIN.bottom} y2={height - MARGIN.bottom} />
209
+
210
+ {#each xTickEntries as tick, i (i)}
211
+ <text class="st-streamgraphChart__tickLabel" x={tick.x} y={height - MARGIN.bottom + 16} text-anchor="middle">{tick.label}</text>
212
+ {/each}
213
+
214
+ {#each areas as area, si (area.seriesLabel)}
215
+ {#if area.d}
216
+ <path
217
+ class="st-streamgraphChart__area st-streamgraphChart__area--{area.tone}"
218
+ class:st-streamgraphChart__area--dim={hovered !== null && hovered !== si}
219
+ d={area.d}
220
+ data-series-index={si}
221
+ />
222
+ {/if}
223
+ {/each}
224
+ </svg>
225
+ </div>
226
+
227
+ <ChartDataList {label} items={dataValueItems} />
228
+
229
+ {#if tooltip}
230
+ <div class="st-streamgraphChart__tooltip" role="presentation" style="left: {(tooltip.cx / width) * 100}%; top: {(tooltip.cy / height) * 100}%">
231
+ <span class="st-streamgraphChart__tooltipLabel">{tooltip.label}</span>
232
+ <span class="st-streamgraphChart__tooltipValue">{tooltip.value}</span>
233
+ </div>
234
+ {/if}
235
+
236
+ {#if showLegend && series.length > 0}
237
+ <ul class="st-streamgraphChart__legend">
238
+ {#each series as item (item.seriesLabel)}
239
+ <li class="st-streamgraphChart__legendItem">
240
+ <span class="st-streamgraphChart__legendSwatch st-streamgraphChart__legendSwatch--{item.tone}" aria-hidden="true"></span>
241
+ {item.seriesLabel}
242
+ </li>
243
+ {/each}
244
+ </ul>
245
+ {/if}
246
+ </div>
247
+
248
+ <style>
249
+ .st-streamgraphChart { color: var(--st-semantic-text-secondary); display: block; font-family: inherit; position: relative; width: 100%; }
250
+ .st-streamgraphChart svg, .st-streamgraphChart__visual { display: block; overflow: visible; }
251
+ .st-streamgraphChart__axis { stroke: var(--st-semantic-border-subtle); stroke-width: 1; }
252
+ .st-streamgraphChart__tickLabel { fill: var(--st-semantic-text-secondary); font-size: 0.6875rem; }
253
+ .st-streamgraphChart__area { cursor: pointer; stroke: var(--st-semantic-surface-default, #fff); stroke-width: 0.75; transition: opacity 120ms ease; }
254
+ .st-streamgraphChart__area--dim { opacity: 0.4; }
255
+ .st-streamgraphChart__area--category1 { fill: var(--st-semantic-data-category1); }
256
+ .st-streamgraphChart__area--category2 { fill: var(--st-semantic-data-category2); }
257
+ .st-streamgraphChart__area--category3 { fill: var(--st-semantic-data-category3); }
258
+ .st-streamgraphChart__area--category4 { fill: var(--st-semantic-data-category4); }
259
+ .st-streamgraphChart__area--category5 { fill: var(--st-semantic-data-category5); }
260
+ .st-streamgraphChart__area--category6 { fill: var(--st-semantic-data-category6); }
261
+ .st-streamgraphChart__area--category7 { fill: var(--st-semantic-data-category7); }
262
+ .st-streamgraphChart__area--category8 { fill: var(--st-semantic-data-category8); }
263
+ @media (prefers-reduced-motion: reduce) { .st-streamgraphChart__area { transition: none; } }
264
+ .st-streamgraphChart__tooltip {
265
+ background: var(--st-semantic-surface-inverse); border-radius: var(--st-radius-sm, 0.25rem);
266
+ color: var(--st-semantic-text-inverse); display: inline-flex; flex-direction: column; font-size: 0.75rem;
267
+ gap: 0.125rem; line-height: 1.2; padding: 0.375rem 0.5rem; pointer-events: none; position: absolute;
268
+ transform: translate(-50%, calc(-100% - 8px)); white-space: nowrap; z-index: 1;
269
+ }
270
+ .st-streamgraphChart__tooltipLabel { font-weight: 600; }
271
+ .st-streamgraphChart__tooltipValue { opacity: 0.85; }
272
+ .st-streamgraphChart__legend { display: flex; flex-wrap: wrap; gap: 0.75rem; list-style: none; margin: 0.5rem 0 0; padding: 0; }
273
+ .st-streamgraphChart__legendItem { align-items: center; color: var(--st-semantic-text-secondary); display: inline-flex; font-size: 0.75rem; gap: 0.35rem; }
274
+ .st-streamgraphChart__legendSwatch { border-radius: 2px; height: 0.7rem; width: 0.7rem; }
275
+ .st-streamgraphChart__legendSwatch--category1 { background: var(--st-semantic-data-category1); }
276
+ .st-streamgraphChart__legendSwatch--category2 { background: var(--st-semantic-data-category2); }
277
+ .st-streamgraphChart__legendSwatch--category3 { background: var(--st-semantic-data-category3); }
278
+ .st-streamgraphChart__legendSwatch--category4 { background: var(--st-semantic-data-category4); }
279
+ .st-streamgraphChart__legendSwatch--category5 { background: var(--st-semantic-data-category5); }
280
+ .st-streamgraphChart__legendSwatch--category6 { background: var(--st-semantic-data-category6); }
281
+ .st-streamgraphChart__legendSwatch--category7 { background: var(--st-semantic-data-category7); }
282
+ .st-streamgraphChart__legendSwatch--category8 { background: var(--st-semantic-data-category8); }
283
+ </style>
@@ -0,0 +1,23 @@
1
+ export type StreamgraphChartTone = "category1" | "category2" | "category3" | "category4" | "category5" | "category6" | "category7" | "category8";
2
+ export type StreamgraphChartSeriesValue = {
3
+ label: string;
4
+ value: number;
5
+ tone?: StreamgraphChartTone;
6
+ };
7
+ export type StreamgraphChartDatum = {
8
+ category: string;
9
+ values: StreamgraphChartSeriesValue[];
10
+ };
11
+ type StreamgraphChartProps = {
12
+ data: StreamgraphChartDatum[];
13
+ width?: number;
14
+ height?: number;
15
+ label: string;
16
+ smooth?: boolean;
17
+ showLegend?: boolean;
18
+ class?: string;
19
+ };
20
+ declare const StreamgraphChart: import("svelte").Component<StreamgraphChartProps, {}, "">;
21
+ type StreamgraphChart = ReturnType<typeof StreamgraphChart>;
22
+ export default StreamgraphChart;
23
+ //# sourceMappingURL=StreamgraphChart.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StreamgraphChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/StreamgraphChart.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,oBAAoB,GAC5B,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,GACrD,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;AAG1D,MAAM,MAAM,2BAA2B,GAAG;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,oBAAoB,CAAC;CAC7B,CAAC;AAGF,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,2BAA2B,EAAE,CAAC;CACvC,CAAC;AAMF,KAAK,qBAAqB,GAAG;IAC3B,IAAI,EAAE,qBAAqB,EAAE,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAuNJ,QAAA,MAAM,gBAAgB,2DAAwC,CAAC;AAC/D,KAAK,gBAAgB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5D,eAAe,gBAAgB,CAAC"}
@@ -34,6 +34,18 @@
34
34
 
35
35
  export type StreamingMessageMode = "live" | "passive";
36
36
 
37
+ /**
38
+ * Simple data-driven event shape (cross-framework parity with React/Vue
39
+ * ports). Distinguished from the rich event-stream shape by the absence of a
40
+ * `type` discriminant.
41
+ */
42
+ export type StreamingMessageSimpleEvent = {
43
+ id: string;
44
+ label: string;
45
+ text?: string;
46
+ status?: ChatMessageStatus;
47
+ };
48
+
37
49
  type ToolCallState = {
38
50
  toolCallId: string;
39
51
  toolName: string;
@@ -48,8 +60,14 @@
48
60
  status?: ChatMessageStatus;
49
61
  streamId?: string;
50
62
  initialEvents?: StreamingMessageEvent[];
51
- events?: StreamingMessageEvent[];
63
+ events?: StreamingMessageEvent[] | StreamingMessageSimpleEvent[];
52
64
  finalContent?: string;
65
+ /**
66
+ * Plain message body (cross-framework parity with React/Vue ports). When
67
+ * provided, it is used as the rendered content (equivalent to
68
+ * `finalContent`). The canonical event-stream API still works.
69
+ */
70
+ text?: string;
53
71
  mode?: StreamingMessageMode;
54
72
  placeholder?: string;
55
73
  showTrail?: boolean;
@@ -65,6 +83,7 @@
65
83
  initialEvents = [],
66
84
  events = [],
67
85
  finalContent,
86
+ text,
68
87
  mode = "live",
69
88
  placeholder = "Streaming en cours…",
70
89
  showTrail = true,
@@ -76,10 +95,25 @@
76
95
 
77
96
  const classes = () => ["st-streamingMessage", className].filter(Boolean).join(" ");
78
97
 
79
- const mergedEvents = () => [...initialEvents, ...events];
98
+ // A simple event lacks the `type` discriminant of the rich stream shape.
99
+ const isSimpleEvent = (
100
+ event: StreamingMessageEvent | StreamingMessageSimpleEvent
101
+ ): event is StreamingMessageSimpleEvent =>
102
+ event != null && typeof event === "object" && !("type" in event);
103
+
104
+ const simpleEvents = (): StreamingMessageSimpleEvent[] =>
105
+ (events as Array<StreamingMessageEvent | StreamingMessageSimpleEvent>).filter(isSimpleEvent);
106
+
107
+ const richEvents = (): StreamingMessageEvent[] =>
108
+ (events as Array<StreamingMessageEvent | StreamingMessageSimpleEvent>).filter(
109
+ (event): event is StreamingMessageEvent => !isSimpleEvent(event)
110
+ );
111
+
112
+ const mergedEvents = () => [...initialEvents, ...richEvents()];
80
113
 
81
114
  const resolvedContent = () => {
82
115
  if (finalContent) return finalContent;
116
+ if (text) return text;
83
117
  return mergedEvents()
84
118
  .filter((event) => event.type === "message.delta")
85
119
  .map((event) => event.delta)
@@ -242,6 +276,14 @@
242
276
  </ul>
243
277
  </details>
244
278
  {/if}
279
+
280
+ {#if simpleEvents().length > 0}
281
+ <ul class="st-streamingMessage__trailList">
282
+ {#each simpleEvents() as event (event.id)}
283
+ <li>{event.label}</li>
284
+ {/each}
285
+ </ul>
286
+ {/if}
245
287
  </ChatMessage>
246
288
 
247
289
  <style>