@sentropic/design-system-svelte 0.18.0 → 0.20.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.
- package/dist/BoxPlotChart.svelte +302 -0
- package/dist/BoxPlotChart.svelte.d.ts +40 -0
- package/dist/BoxPlotChart.svelte.d.ts.map +1 -0
- package/dist/BulletChart.svelte +479 -0
- package/dist/BulletChart.svelte.d.ts +32 -0
- package/dist/BulletChart.svelte.d.ts.map +1 -0
- package/dist/BumpChart.svelte +387 -0
- package/dist/BumpChart.svelte.d.ts +35 -0
- package/dist/BumpChart.svelte.d.ts.map +1 -0
- package/dist/CalendarHeatmapChart.svelte +345 -0
- package/dist/CalendarHeatmapChart.svelte.d.ts +28 -0
- package/dist/CalendarHeatmapChart.svelte.d.ts.map +1 -0
- package/dist/CandlestickChart.svelte +333 -0
- package/dist/CandlestickChart.svelte.d.ts +31 -0
- package/dist/CandlestickChart.svelte.d.ts.map +1 -0
- package/dist/HeatmapChart.svelte +337 -0
- package/dist/HeatmapChart.svelte.d.ts +35 -0
- package/dist/HeatmapChart.svelte.d.ts.map +1 -0
- package/dist/HistogramChart.svelte +294 -0
- package/dist/HistogramChart.svelte.d.ts +38 -0
- package/dist/HistogramChart.svelte.d.ts.map +1 -0
- package/dist/MarimekkoChart.svelte +319 -0
- package/dist/MarimekkoChart.svelte.d.ts +35 -0
- package/dist/MarimekkoChart.svelte.d.ts.map +1 -0
- package/dist/ParallelCoordinatesChart.svelte +315 -0
- package/dist/ParallelCoordinatesChart.svelte.d.ts +35 -0
- package/dist/ParallelCoordinatesChart.svelte.d.ts.map +1 -0
- package/dist/RadarChart.svelte +340 -0
- package/dist/RadarChart.svelte.d.ts +43 -0
- package/dist/RadarChart.svelte.d.ts.map +1 -0
- package/dist/SankeyChart.svelte +364 -0
- package/dist/SankeyChart.svelte.d.ts +45 -0
- package/dist/SankeyChart.svelte.d.ts.map +1 -0
- package/dist/SunburstChart.svelte +388 -0
- package/dist/SunburstChart.svelte.d.ts +39 -0
- package/dist/SunburstChart.svelte.d.ts.map +1 -0
- package/dist/chartContrast.d.ts +0 -4
- package/dist/chartContrast.d.ts.map +1 -1
- package/dist/chartContrast.js +4 -56
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -0
- package/package.json +1 -1
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
/**
|
|
3
|
+
* BoxPlotChart - API canonique (référence Svelte, React/Vue doivent s'aligner)
|
|
4
|
+
*
|
|
5
|
+
* Props obligatoires :
|
|
6
|
+
* data BoxPlotChartDatum[] - tableau {label, min, q1, median, q3, max, outliers?, tone?}
|
|
7
|
+
* label string - aria-label du graphique
|
|
8
|
+
*
|
|
9
|
+
* Props optionnelles :
|
|
10
|
+
* width number (défaut 480) - largeur du viewBox en px
|
|
11
|
+
* height number (défaut 260) - hauteur du viewBox en px
|
|
12
|
+
* class string - classe CSS supplémentaire
|
|
13
|
+
*
|
|
14
|
+
* NaN/vide : les valeurs non-finies sont exclues du domaine (filter Number.isFinite).
|
|
15
|
+
* Tableau vide → rendu vide sans crash.
|
|
16
|
+
* Note : l'ordre min≤q1≤median≤q3≤max n'est pas imposé par clamp mais rendu
|
|
17
|
+
* tel quel (abs() sur la hauteur de boîte) ; les données incohérentes produisent
|
|
18
|
+
* un rendu plausible mais peuvent induire en erreur.
|
|
19
|
+
*/
|
|
20
|
+
export type BoxPlotChartTone =
|
|
21
|
+
| "category1"
|
|
22
|
+
| "category2"
|
|
23
|
+
| "category3"
|
|
24
|
+
| "category4"
|
|
25
|
+
| "category5"
|
|
26
|
+
| "category6"
|
|
27
|
+
| "category7"
|
|
28
|
+
| "category8";
|
|
29
|
+
|
|
30
|
+
export type BoxPlotChartDatum = {
|
|
31
|
+
label: string;
|
|
32
|
+
min: number;
|
|
33
|
+
q1: number;
|
|
34
|
+
median: number;
|
|
35
|
+
q3: number;
|
|
36
|
+
max: number;
|
|
37
|
+
outliers?: number[];
|
|
38
|
+
tone?: BoxPlotChartTone;
|
|
39
|
+
};
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<script lang="ts">
|
|
43
|
+
import ChartDataList from "./ChartDataList.svelte";
|
|
44
|
+
|
|
45
|
+
type BoxPlotChartProps = {
|
|
46
|
+
data: BoxPlotChartDatum[];
|
|
47
|
+
label: string;
|
|
48
|
+
width?: number;
|
|
49
|
+
height?: number;
|
|
50
|
+
class?: string;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
let {
|
|
54
|
+
data,
|
|
55
|
+
label,
|
|
56
|
+
width = 480,
|
|
57
|
+
height = 260,
|
|
58
|
+
class: className
|
|
59
|
+
}: BoxPlotChartProps = $props();
|
|
60
|
+
|
|
61
|
+
const MARGIN = { top: 16, right: 20, bottom: 38, left: 48 };
|
|
62
|
+
const TONES = [
|
|
63
|
+
"category1",
|
|
64
|
+
"category2",
|
|
65
|
+
"category3",
|
|
66
|
+
"category4",
|
|
67
|
+
"category5",
|
|
68
|
+
"category6",
|
|
69
|
+
"category7",
|
|
70
|
+
"category8"
|
|
71
|
+
] as const;
|
|
72
|
+
|
|
73
|
+
function formatNumber(value: number): string {
|
|
74
|
+
if (!Number.isFinite(value)) return "0";
|
|
75
|
+
if (Number.isInteger(value)) return String(value);
|
|
76
|
+
return value.toFixed(2).replace(/\.?0+$/, "");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function scaleLinear(v: number, d0: number, d1: number, r0: number, r1: number) {
|
|
80
|
+
if (d1 === d0) return r0;
|
|
81
|
+
return r0 + ((v - d0) * (r1 - r0)) / (d1 - d0);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let hoveredIndex: number | null = $state(null);
|
|
85
|
+
|
|
86
|
+
const plotWidth = $derived(Math.max(width - MARGIN.left - MARGIN.right, 1));
|
|
87
|
+
const plotHeight = $derived(Math.max(height - MARGIN.top - MARGIN.bottom, 1));
|
|
88
|
+
|
|
89
|
+
const domain = $derived.by(() => {
|
|
90
|
+
const values = data.flatMap((datum) => [
|
|
91
|
+
datum.min,
|
|
92
|
+
datum.q1,
|
|
93
|
+
datum.median,
|
|
94
|
+
datum.q3,
|
|
95
|
+
datum.max,
|
|
96
|
+
...(datum.outliers ?? [])
|
|
97
|
+
]).filter(Number.isFinite);
|
|
98
|
+
if (values.length === 0) return { min: 0, max: 1 };
|
|
99
|
+
const min = Math.min(...values);
|
|
100
|
+
const max = Math.max(...values);
|
|
101
|
+
const pad = (max - min) * 0.08 || Math.max(Math.abs(max), 1) * 0.1;
|
|
102
|
+
return { min: min - pad, max: max + pad };
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const plots = $derived.by(() => {
|
|
106
|
+
const band = data.length > 0 ? plotWidth / data.length : plotWidth;
|
|
107
|
+
const boxWidth = Math.min(54, Math.max(18, band * 0.44));
|
|
108
|
+
|
|
109
|
+
return data.map((datum, index) => {
|
|
110
|
+
const cx = MARGIN.left + band * (index + 0.5);
|
|
111
|
+
const y = (value: number) => MARGIN.top + scaleLinear(value, domain.min, domain.max, plotHeight, 0);
|
|
112
|
+
const q1Y = y(datum.q1);
|
|
113
|
+
const q3Y = y(datum.q3);
|
|
114
|
+
const minY = y(datum.min);
|
|
115
|
+
const maxY = y(datum.max);
|
|
116
|
+
return {
|
|
117
|
+
datum,
|
|
118
|
+
tone: datum.tone ?? TONES[index % TONES.length],
|
|
119
|
+
cx,
|
|
120
|
+
boxX: cx - boxWidth / 2,
|
|
121
|
+
boxY: Math.min(q1Y, q3Y),
|
|
122
|
+
boxWidth,
|
|
123
|
+
boxHeight: Math.max(Math.abs(q1Y - q3Y), 1),
|
|
124
|
+
medianY: y(datum.median),
|
|
125
|
+
minY,
|
|
126
|
+
maxY,
|
|
127
|
+
capWidth: boxWidth * 0.72,
|
|
128
|
+
outliers: (datum.outliers ?? []).filter(Number.isFinite).map((value) => ({ value, y: y(value) }))
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const dataValueItems = $derived(
|
|
134
|
+
data.map((datum) => {
|
|
135
|
+
const summary = `${datum.label}: min ${formatNumber(datum.min)}, q1 ${formatNumber(datum.q1)}, median ${formatNumber(datum.median)}, q3 ${formatNumber(datum.q3)}, max ${formatNumber(datum.max)}`;
|
|
136
|
+
const outliers = datum.outliers?.length ? `, outliers ${datum.outliers.map(formatNumber).join(", ")}` : "";
|
|
137
|
+
return `${summary}${outliers}`;
|
|
138
|
+
})
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
function handleVisualPointerMove(event: PointerEvent) {
|
|
142
|
+
const target = event.target;
|
|
143
|
+
if (!(target instanceof Element)) {
|
|
144
|
+
hoveredIndex = null;
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const index = Number(target.getAttribute("data-chart-index"));
|
|
148
|
+
hoveredIndex = Number.isInteger(index) ? index : null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const classes = () => ["st-boxPlotChart", className].filter(Boolean).join(" ");
|
|
152
|
+
</script>
|
|
153
|
+
|
|
154
|
+
<div class={classes()}>
|
|
155
|
+
<div
|
|
156
|
+
class="st-boxPlotChart__visual"
|
|
157
|
+
role="img"
|
|
158
|
+
aria-label={label}
|
|
159
|
+
onpointermove={handleVisualPointerMove}
|
|
160
|
+
onpointerleave={() => (hoveredIndex = null)}
|
|
161
|
+
>
|
|
162
|
+
<svg
|
|
163
|
+
viewBox="0 0 {width} {height}"
|
|
164
|
+
preserveAspectRatio="xMidYMid meet"
|
|
165
|
+
width="100%"
|
|
166
|
+
height="100%"
|
|
167
|
+
focusable="false"
|
|
168
|
+
aria-hidden="true"
|
|
169
|
+
>
|
|
170
|
+
<line class="st-boxPlotChart__axis" x1={MARGIN.left} x2={MARGIN.left} y1={MARGIN.top} y2={height - MARGIN.bottom} />
|
|
171
|
+
<line class="st-boxPlotChart__axis" x1={MARGIN.left} x2={width - MARGIN.right} y1={height - MARGIN.bottom} y2={height - MARGIN.bottom} />
|
|
172
|
+
|
|
173
|
+
{#each plots as plot, i (plot.datum.label)}
|
|
174
|
+
<line class="st-boxPlotChart__whisker" x1={plot.cx} x2={plot.cx} y1={plot.minY} y2={plot.maxY} data-chart-index={i} />
|
|
175
|
+
<line class="st-boxPlotChart__whiskerCap" x1={plot.cx - plot.capWidth / 2} x2={plot.cx + plot.capWidth / 2} y1={plot.minY} y2={plot.minY} data-chart-index={i} />
|
|
176
|
+
<line class="st-boxPlotChart__whiskerCap" x1={plot.cx - plot.capWidth / 2} x2={plot.cx + plot.capWidth / 2} y1={plot.maxY} y2={plot.maxY} data-chart-index={i} />
|
|
177
|
+
<rect
|
|
178
|
+
class="st-boxPlotChart__box st-boxPlotChart__box--{plot.tone}"
|
|
179
|
+
class:st-boxPlotChart__box--dim={hoveredIndex !== null && hoveredIndex !== i}
|
|
180
|
+
x={plot.boxX}
|
|
181
|
+
y={plot.boxY}
|
|
182
|
+
width={plot.boxWidth}
|
|
183
|
+
height={plot.boxHeight}
|
|
184
|
+
data-chart-index={i}
|
|
185
|
+
/>
|
|
186
|
+
<line class="st-boxPlotChart__median" x1={plot.boxX} x2={plot.boxX + plot.boxWidth} y1={plot.medianY} y2={plot.medianY} data-chart-index={i} />
|
|
187
|
+
{#each plot.outliers as outlier (`${plot.datum.label}-${outlier.value}`)}
|
|
188
|
+
<circle class="st-boxPlotChart__outlier" cx={plot.cx} cy={outlier.y} r="3" data-chart-index={i} />
|
|
189
|
+
{/each}
|
|
190
|
+
<text class="st-boxPlotChart__label" x={plot.cx} y={height - MARGIN.bottom + 16} text-anchor="middle">
|
|
191
|
+
{plot.datum.label}
|
|
192
|
+
</text>
|
|
193
|
+
{/each}
|
|
194
|
+
</svg>
|
|
195
|
+
</div>
|
|
196
|
+
|
|
197
|
+
<ChartDataList {label} items={dataValueItems} />
|
|
198
|
+
|
|
199
|
+
{#if hoveredIndex !== null && plots[hoveredIndex]}
|
|
200
|
+
{@const plot = plots[hoveredIndex]}
|
|
201
|
+
<div
|
|
202
|
+
class="st-boxPlotChart__tooltip"
|
|
203
|
+
role="presentation"
|
|
204
|
+
style="left: {(plot.cx / width) * 100}%; top: {(plot.medianY / height) * 100}%"
|
|
205
|
+
>
|
|
206
|
+
<span class="st-boxPlotChart__tooltipLabel">{plot.datum.label}</span>
|
|
207
|
+
<span class="st-boxPlotChart__tooltipValue">Median {formatNumber(plot.datum.median)}</span>
|
|
208
|
+
</div>
|
|
209
|
+
{/if}
|
|
210
|
+
</div>
|
|
211
|
+
|
|
212
|
+
<style>
|
|
213
|
+
.st-boxPlotChart {
|
|
214
|
+
color: var(--st-semantic-text-secondary);
|
|
215
|
+
display: block;
|
|
216
|
+
font-family: inherit;
|
|
217
|
+
max-width: 100%;
|
|
218
|
+
position: relative;
|
|
219
|
+
width: 100%;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.st-boxPlotChart svg,
|
|
223
|
+
.st-boxPlotChart__visual {
|
|
224
|
+
display: block;
|
|
225
|
+
overflow: visible;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.st-boxPlotChart__axis,
|
|
229
|
+
.st-boxPlotChart__whisker,
|
|
230
|
+
.st-boxPlotChart__whiskerCap {
|
|
231
|
+
stroke: var(--st-semantic-border-subtle);
|
|
232
|
+
stroke-width: 1;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.st-boxPlotChart__median {
|
|
236
|
+
stroke: var(--st-semantic-text-primary);
|
|
237
|
+
stroke-width: 2;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.st-boxPlotChart__box {
|
|
241
|
+
cursor: pointer;
|
|
242
|
+
fill-opacity: 0.72;
|
|
243
|
+
stroke: var(--st-semantic-surface-default, Canvas);
|
|
244
|
+
stroke-width: 1;
|
|
245
|
+
transition: opacity 120ms ease;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.st-boxPlotChart__box--dim {
|
|
249
|
+
opacity: 0.45;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
@media (prefers-reduced-motion: reduce) {
|
|
253
|
+
.st-boxPlotChart__box {
|
|
254
|
+
transition: none;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.st-boxPlotChart__box--category1 { fill: var(--st-semantic-data-category1); }
|
|
259
|
+
.st-boxPlotChart__box--category2 { fill: var(--st-semantic-data-category2); }
|
|
260
|
+
.st-boxPlotChart__box--category3 { fill: var(--st-semantic-data-category3); }
|
|
261
|
+
.st-boxPlotChart__box--category4 { fill: var(--st-semantic-data-category4); }
|
|
262
|
+
.st-boxPlotChart__box--category5 { fill: var(--st-semantic-data-category5); }
|
|
263
|
+
.st-boxPlotChart__box--category6 { fill: var(--st-semantic-data-category6); }
|
|
264
|
+
.st-boxPlotChart__box--category7 { fill: var(--st-semantic-data-category7); }
|
|
265
|
+
.st-boxPlotChart__box--category8 { fill: var(--st-semantic-data-category8); }
|
|
266
|
+
|
|
267
|
+
.st-boxPlotChart__outlier {
|
|
268
|
+
fill: var(--st-semantic-surface-default, Canvas);
|
|
269
|
+
stroke: var(--st-semantic-text-secondary);
|
|
270
|
+
stroke-width: 1.5;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.st-boxPlotChart__label {
|
|
274
|
+
fill: var(--st-semantic-text-secondary);
|
|
275
|
+
font-size: 0.75rem;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.st-boxPlotChart__tooltip {
|
|
279
|
+
background: var(--st-semantic-surface-inverse);
|
|
280
|
+
border-radius: var(--st-radius-sm, 0.25rem);
|
|
281
|
+
color: var(--st-semantic-text-inverse);
|
|
282
|
+
display: inline-flex;
|
|
283
|
+
flex-direction: column;
|
|
284
|
+
font-size: 0.75rem;
|
|
285
|
+
gap: 0.125rem;
|
|
286
|
+
line-height: 1.2;
|
|
287
|
+
padding: 0.375rem 0.5rem;
|
|
288
|
+
pointer-events: none;
|
|
289
|
+
position: absolute;
|
|
290
|
+
transform: translate(-50%, -115%);
|
|
291
|
+
white-space: nowrap;
|
|
292
|
+
z-index: 1;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.st-boxPlotChart__tooltipLabel {
|
|
296
|
+
font-weight: 600;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.st-boxPlotChart__tooltipValue {
|
|
300
|
+
opacity: 0.85;
|
|
301
|
+
}
|
|
302
|
+
</style>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BoxPlotChart - API canonique (référence Svelte, React/Vue doivent s'aligner)
|
|
3
|
+
*
|
|
4
|
+
* Props obligatoires :
|
|
5
|
+
* data BoxPlotChartDatum[] - tableau {label, min, q1, median, q3, max, outliers?, tone?}
|
|
6
|
+
* label string - aria-label du graphique
|
|
7
|
+
*
|
|
8
|
+
* Props optionnelles :
|
|
9
|
+
* width number (défaut 480) - largeur du viewBox en px
|
|
10
|
+
* height number (défaut 260) - hauteur du viewBox en px
|
|
11
|
+
* class string - classe CSS supplémentaire
|
|
12
|
+
*
|
|
13
|
+
* NaN/vide : les valeurs non-finies sont exclues du domaine (filter Number.isFinite).
|
|
14
|
+
* Tableau vide → rendu vide sans crash.
|
|
15
|
+
* Note : l'ordre min≤q1≤median≤q3≤max n'est pas imposé par clamp mais rendu
|
|
16
|
+
* tel quel (abs() sur la hauteur de boîte) ; les données incohérentes produisent
|
|
17
|
+
* un rendu plausible mais peuvent induire en erreur.
|
|
18
|
+
*/
|
|
19
|
+
export type BoxPlotChartTone = "category1" | "category2" | "category3" | "category4" | "category5" | "category6" | "category7" | "category8";
|
|
20
|
+
export type BoxPlotChartDatum = {
|
|
21
|
+
label: string;
|
|
22
|
+
min: number;
|
|
23
|
+
q1: number;
|
|
24
|
+
median: number;
|
|
25
|
+
q3: number;
|
|
26
|
+
max: number;
|
|
27
|
+
outliers?: number[];
|
|
28
|
+
tone?: BoxPlotChartTone;
|
|
29
|
+
};
|
|
30
|
+
type BoxPlotChartProps = {
|
|
31
|
+
data: BoxPlotChartDatum[];
|
|
32
|
+
label: string;
|
|
33
|
+
width?: number;
|
|
34
|
+
height?: number;
|
|
35
|
+
class?: string;
|
|
36
|
+
};
|
|
37
|
+
declare const BoxPlotChart: import("svelte").Component<BoxPlotChartProps, {}, "">;
|
|
38
|
+
type BoxPlotChart = ReturnType<typeof BoxPlotChart>;
|
|
39
|
+
export default BoxPlotChart;
|
|
40
|
+
//# sourceMappingURL=BoxPlotChart.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BoxPlotChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/BoxPlotChart.svelte.ts"],"names":[],"mappings":"AAGE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,gBAAgB,GACxB,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB,CAAC;AAMF,KAAK,iBAAiB,GAAG;IACvB,IAAI,EAAE,iBAAiB,EAAE,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAiJJ,QAAA,MAAM,YAAY,uDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|