@sentropic/design-system-svelte 0.23.0 → 0.25.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.
@@ -0,0 +1,200 @@
1
+ <script lang="ts" module>
2
+ export type LanguageToggleLocale = "fr" | "en";
3
+
4
+ export interface LanguageToggleProps {
5
+ /** Langue courante (contrôlé). */
6
+ locale?: LanguageToggleLocale;
7
+ /** Notifié au changement de langue. */
8
+ onLocaleChange?: (locale: LanguageToggleLocale) => void;
9
+ /** Libellé FR (i18n-agnostique : fourni par le parent). */
10
+ frLabel?: string;
11
+ /** Libellé EN (i18n-agnostique : fourni par le parent). */
12
+ enLabel?: string;
13
+ /** aria-label du sélecteur. */
14
+ label?: string;
15
+ /**
16
+ * Variante d'affichage :
17
+ * - `select` (défaut) : le <select> de la source (header desktop).
18
+ * - `accordion` : la liste de boutons du tiroir mobile.
19
+ */
20
+ variant?: "select" | "accordion";
21
+ /** Titre de la section accordéon. */
22
+ accordionLabel?: string;
23
+ class?: string;
24
+ }
25
+ </script>
26
+
27
+ <script lang="ts">
28
+ import { ChevronDown } from "@lucide/svelte";
29
+
30
+ let {
31
+ locale = "fr",
32
+ onLocaleChange,
33
+ frLabel = "FR",
34
+ enLabel = "EN",
35
+ label = "Langue",
36
+ variant = "select",
37
+ accordionLabel = "Langue",
38
+ class: className,
39
+ }: LanguageToggleProps = $props();
40
+
41
+ let open = $state(false);
42
+
43
+ const classes = () => ["st-languageToggle", className].filter(Boolean).join(" ");
44
+
45
+ function emit(next: LanguageToggleLocale) {
46
+ onLocaleChange?.(next);
47
+ }
48
+
49
+ function onSelectChange(event: Event) {
50
+ const value = (event.currentTarget as HTMLSelectElement).value as LanguageToggleLocale;
51
+ emit(value);
52
+ }
53
+ </script>
54
+
55
+ {#if variant === "accordion"}
56
+ <div class={classes()}>
57
+ <button
58
+ type="button"
59
+ class="st-languageToggle__accordionTrigger"
60
+ aria-expanded={open}
61
+ onclick={() => (open = !open)}
62
+ >
63
+ <span>{accordionLabel}</span>
64
+ <ChevronDown
65
+ class={`st-languageToggle__chevron${open ? " st-languageToggle__chevron--open" : ""}`}
66
+ size={16}
67
+ aria-hidden="true"
68
+ />
69
+ </button>
70
+ {#if open}
71
+ <div class="st-languageToggle__accordionPanel">
72
+ <button
73
+ type="button"
74
+ class="st-languageToggle__option"
75
+ class:st-languageToggle__option--active={locale === "fr"}
76
+ aria-current={locale === "fr" ? "true" : "false"}
77
+ onclick={() => emit("fr")}
78
+ >
79
+ {frLabel}
80
+ </button>
81
+ <button
82
+ type="button"
83
+ class="st-languageToggle__option"
84
+ class:st-languageToggle__option--active={locale === "en"}
85
+ aria-current={locale === "en" ? "true" : "false"}
86
+ onclick={() => emit("en")}
87
+ >
88
+ {enLabel}
89
+ </button>
90
+ </div>
91
+ {/if}
92
+ </div>
93
+ {:else}
94
+ <select
95
+ class={`st-languageToggle__select${className ? ` ${className}` : ""}`}
96
+ value={locale}
97
+ aria-label={label}
98
+ onchange={onSelectChange}
99
+ >
100
+ <option value="fr">{frLabel}</option>
101
+ <option value="en">{enLabel}</option>
102
+ </select>
103
+ {/if}
104
+
105
+ <style>
106
+ .st-languageToggle {
107
+ width: 100%;
108
+ }
109
+
110
+ /* Variante <select> — calque du <select> source (header desktop). */
111
+ .st-languageToggle__select {
112
+ background: var(--st-semantic-surface-default);
113
+ border: 1px solid var(--st-semantic-border-subtle);
114
+ border-radius: var(--st-radius-md, 0.375rem);
115
+ color: var(--st-semantic-text-primary);
116
+ cursor: pointer;
117
+ font: inherit;
118
+ font-family: var(--st-font-sans);
119
+ font-size: 0.875rem;
120
+ padding: var(--st-spacing-1, 0.25rem) var(--st-spacing-2, 0.5rem);
121
+ }
122
+
123
+ .st-languageToggle__select:focus-visible {
124
+ border-color: var(--st-semantic-border-interactive);
125
+ box-shadow: 0 0 0 2px var(--st-semantic-border-interactive);
126
+ outline: none;
127
+ }
128
+
129
+ /* Variante accordéon — calque du tiroir mobile source. */
130
+ .st-languageToggle__accordionTrigger {
131
+ align-items: center;
132
+ background: transparent;
133
+ border: 0;
134
+ border-radius: var(--st-radius-sm, 0.375rem);
135
+ color: var(--st-semantic-text-primary);
136
+ cursor: pointer;
137
+ display: flex;
138
+ font: inherit;
139
+ font-family: var(--st-font-sans);
140
+ font-size: 0.875rem;
141
+ font-weight: 500;
142
+ justify-content: space-between;
143
+ padding: var(--st-spacing-2, 0.5rem) var(--st-spacing-3, 0.75rem);
144
+ width: 100%;
145
+ }
146
+
147
+ .st-languageToggle__accordionTrigger:hover {
148
+ background: var(--st-semantic-surface-subtle);
149
+ }
150
+
151
+ .st-languageToggle__accordionTrigger:focus-visible {
152
+ box-shadow: 0 0 0 2px var(--st-semantic-border-interactive);
153
+ outline: none;
154
+ }
155
+
156
+ .st-languageToggle :global(.st-languageToggle__chevron) {
157
+ color: var(--st-semantic-text-secondary);
158
+ transition: transform var(--st-motion-fast, 120ms) var(--st-motion-easing, ease);
159
+ }
160
+
161
+ .st-languageToggle :global(.st-languageToggle__chevron--open) {
162
+ transform: rotate(180deg);
163
+ }
164
+
165
+ .st-languageToggle__accordionPanel {
166
+ display: grid;
167
+ gap: var(--st-spacing-1, 0.25rem);
168
+ padding: var(--st-spacing-1, 0.25rem) var(--st-spacing-3, 0.75rem) var(--st-spacing-2, 0.5rem);
169
+ }
170
+
171
+ .st-languageToggle__option {
172
+ background: transparent;
173
+ border: 0;
174
+ border-radius: var(--st-radius-sm, 0.375rem);
175
+ color: var(--st-semantic-text-secondary);
176
+ cursor: pointer;
177
+ font: inherit;
178
+ font-family: var(--st-font-sans);
179
+ font-size: 0.875rem;
180
+ font-weight: 500;
181
+ padding: var(--st-spacing-2, 0.5rem) var(--st-spacing-3, 0.75rem);
182
+ text-align: left;
183
+ width: 100%;
184
+ }
185
+
186
+ .st-languageToggle__option:hover {
187
+ background: var(--st-semantic-surface-subtle);
188
+ color: var(--st-semantic-text-primary);
189
+ }
190
+
191
+ .st-languageToggle__option--active {
192
+ background: var(--st-semantic-surface-subtle);
193
+ color: var(--st-semantic-text-primary);
194
+ }
195
+
196
+ .st-languageToggle__option:focus-visible {
197
+ box-shadow: 0 0 0 2px var(--st-semantic-border-interactive);
198
+ outline: none;
199
+ }
200
+ </style>
@@ -0,0 +1,26 @@
1
+ export type LanguageToggleLocale = "fr" | "en";
2
+ export interface LanguageToggleProps {
3
+ /** Langue courante (contrôlé). */
4
+ locale?: LanguageToggleLocale;
5
+ /** Notifié au changement de langue. */
6
+ onLocaleChange?: (locale: LanguageToggleLocale) => void;
7
+ /** Libellé FR (i18n-agnostique : fourni par le parent). */
8
+ frLabel?: string;
9
+ /** Libellé EN (i18n-agnostique : fourni par le parent). */
10
+ enLabel?: string;
11
+ /** aria-label du sélecteur. */
12
+ label?: string;
13
+ /**
14
+ * Variante d'affichage :
15
+ * - `select` (défaut) : le <select> de la source (header desktop).
16
+ * - `accordion` : la liste de boutons du tiroir mobile.
17
+ */
18
+ variant?: "select" | "accordion";
19
+ /** Titre de la section accordéon. */
20
+ accordionLabel?: string;
21
+ class?: string;
22
+ }
23
+ declare const LanguageToggle: import("svelte").Component<LanguageToggleProps, {}, "">;
24
+ type LanguageToggle = ReturnType<typeof LanguageToggle>;
25
+ export default LanguageToggle;
26
+ //# sourceMappingURL=LanguageToggle.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LanguageToggle.svelte.d.ts","sourceRoot":"","sources":["../src/lib/LanguageToggle.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,oBAAoB,GAAG,IAAI,GAAG,IAAI,CAAC;AAE/C,MAAM,WAAW,mBAAmB;IAClC,kCAAkC;IAClC,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,uCAAuC;IACvC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACxD,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,OAAO,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;IACjC,qCAAqC;IACrC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA+DH,QAAA,MAAM,cAAc,yDAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
@@ -0,0 +1,438 @@
1
+ <script lang="ts" module>
2
+ /**
3
+ * LollipopChart — tige fine (ligne) + cercle au sommet, par catégorie.
4
+ * API canonique (référence Svelte, React/Vue doivent s'aligner).
5
+ *
6
+ * Props obligatoires :
7
+ * data LollipopChartDatum[] - tableau {label, value, tone?}
8
+ * label string - aria-label du graphique
9
+ *
10
+ * Props optionnelles :
11
+ * orientation "vertical"|"horizontal" (défaut "vertical")
12
+ * width number (défaut 480)
13
+ * height number (défaut 240)
14
+ * domain [number, number] - domaine fixe de l'axe des valeurs
15
+ * class string
16
+ */
17
+ export type LollipopChartTone =
18
+ | "category1"
19
+ | "category2"
20
+ | "category3"
21
+ | "category4"
22
+ | "category5"
23
+ | "category6"
24
+ | "category7"
25
+ | "category8";
26
+
27
+ export type LollipopChartDatum = {
28
+ label: string;
29
+ value: number;
30
+ tone?: LollipopChartTone;
31
+ };
32
+ </script>
33
+
34
+ <script lang="ts">
35
+ import ChartDataList from "./ChartDataList.svelte";
36
+ import { contrastTextForTone } from "./chartContrast.js";
37
+
38
+ type LollipopChartProps = {
39
+ data: LollipopChartDatum[];
40
+ width?: number;
41
+ height?: number;
42
+ orientation?: "vertical" | "horizontal";
43
+ label: string;
44
+ /**
45
+ * Domaine fixe de l'axe des valeurs `[min, max]`. Quand fourni (et fini),
46
+ * l'échelle l'utilise au lieu du min/max dérivé des données — laissant
47
+ * plusieurs LollipopCharts d'une grille partager une échelle. Absent ou
48
+ * invalide → repli sur la plage auto (inchangé).
49
+ */
50
+ domain?: [number, number];
51
+ class?: string;
52
+ };
53
+
54
+ let {
55
+ data = [],
56
+ width = 480,
57
+ height = 240,
58
+ orientation = "vertical",
59
+ label,
60
+ domain,
61
+ class: className
62
+ }: LollipopChartProps = $props();
63
+
64
+ const MARGIN = { top: 12, right: 16, bottom: 32, left: 44 };
65
+ const DOT_RADIUS = 5;
66
+
67
+ function niceTicks(min: number, max: number, target = 5): number[] {
68
+ if (!Number.isFinite(min) || !Number.isFinite(max) || min === max) {
69
+ const base = Number.isFinite(max) ? max : 0;
70
+ return [base];
71
+ }
72
+ const range = max - min;
73
+ const rough = range / Math.max(target - 1, 1);
74
+ const pow = Math.pow(10, Math.floor(Math.log10(rough)));
75
+ const norm = rough / pow;
76
+ let step: number;
77
+ if (norm < 1.5) step = 1 * pow;
78
+ else if (norm < 3) step = 2 * pow;
79
+ else if (norm < 7) step = 5 * pow;
80
+ else step = 10 * pow;
81
+ const start = Math.floor(min / step) * step;
82
+ const end = Math.ceil(max / step) * step;
83
+ const ticks: number[] = [];
84
+ for (let v = start; v <= end + step / 2; v += step) {
85
+ ticks.push(Number(v.toFixed(10)));
86
+ }
87
+ return ticks;
88
+ }
89
+
90
+ function scaleLinear(v: number, d0: number, d1: number, r0: number, r1: number) {
91
+ if (d1 === d0) return r0;
92
+ return r0 + ((v - d0) * (r1 - r0)) / (d1 - d0);
93
+ }
94
+
95
+ function formatTick(v: number): string {
96
+ if (Math.abs(v) >= 1000) return `${(v / 1000).toFixed(v % 1000 === 0 ? 0 : 1)}k`;
97
+ if (Number.isInteger(v)) return String(v);
98
+ return v.toFixed(1);
99
+ }
100
+
101
+ let hoveredIndex: number | null = $state(null);
102
+
103
+ // Données valides : value finie.
104
+ const validData = $derived(data.filter((d) => Number.isFinite(d.value)));
105
+
106
+ // Un domaine n'est honoré que si fini et ordonné (min < max).
107
+ const validDomain = $derived.by<[number, number] | null>(() => {
108
+ if (!domain) return null;
109
+ const [d0, d1] = domain;
110
+ if (!Number.isFinite(d0) || !Number.isFinite(d1) || d0 >= d1) return null;
111
+ return [d0, d1];
112
+ });
113
+
114
+ const scales = $derived.by(() => {
115
+ const values = validData.map((d) => d.value);
116
+ const minRaw = validDomain ? validDomain[0] : Math.min(0, ...values);
117
+ const maxRaw = validDomain ? validDomain[1] : Math.max(0, ...values);
118
+ const ticks = niceTicks(minRaw, maxRaw, 5);
119
+ const domainMin = ticks[0];
120
+ const domainMax = ticks[ticks.length - 1];
121
+ const plotWidth = Math.max(width - MARGIN.left - MARGIN.right, 1);
122
+ const plotHeight = Math.max(height - MARGIN.top - MARGIN.bottom, 1);
123
+ return { ticks, domainMin, domainMax, plotWidth, plotHeight };
124
+ });
125
+
126
+ const lollipops = $derived.by(() => {
127
+ const { domainMin, domainMax, plotWidth, plotHeight } = scales;
128
+ if (validData.length === 0) return [];
129
+ if (orientation === "vertical") {
130
+ const band = plotWidth / validData.length;
131
+ const zeroY = scaleLinear(0, domainMin, domainMax, plotHeight, 0);
132
+ return validData.map((d, i) => {
133
+ const valueY = scaleLinear(d.value, domainMin, domainMax, plotHeight, 0);
134
+ const cx = MARGIN.left + band * (i + 0.5);
135
+ return {
136
+ datum: d,
137
+ tone: d.tone ?? "category1",
138
+ // tige : du zéro jusqu'au point
139
+ stemX1: cx,
140
+ stemY1: MARGIN.top + zeroY,
141
+ stemX2: cx,
142
+ stemY2: MARGIN.top + valueY,
143
+ cx,
144
+ cy: MARGIN.top + valueY,
145
+ labelX: cx,
146
+ labelY: height - MARGIN.bottom + 16
147
+ };
148
+ });
149
+ }
150
+ // horizontal
151
+ const band = plotHeight / validData.length;
152
+ const zeroX = scaleLinear(0, domainMin, domainMax, 0, plotWidth);
153
+ return validData.map((d, i) => {
154
+ const valueX = scaleLinear(d.value, domainMin, domainMax, 0, plotWidth);
155
+ const cy = MARGIN.top + band * (i + 0.5);
156
+ return {
157
+ datum: d,
158
+ tone: d.tone ?? "category1",
159
+ stemX1: MARGIN.left + zeroX,
160
+ stemY1: cy,
161
+ stemX2: MARGIN.left + valueX,
162
+ stemY2: cy,
163
+ cx: MARGIN.left + valueX,
164
+ cy,
165
+ labelX: MARGIN.left - 6,
166
+ labelY: cy
167
+ };
168
+ });
169
+ });
170
+
171
+ const dataValueItems = $derived(validData.map((d) => `${d.label}: ${d.value}`));
172
+
173
+ const valueAxisTicks = $derived.by(() => {
174
+ const { ticks, domainMin, domainMax, plotWidth, plotHeight } = scales;
175
+ if (orientation === "vertical") {
176
+ return ticks.map((tick) => ({
177
+ value: tick,
178
+ x1: MARGIN.left,
179
+ x2: MARGIN.left + plotWidth,
180
+ y: MARGIN.top + scaleLinear(tick, domainMin, domainMax, plotHeight, 0),
181
+ x: undefined,
182
+ y1: undefined,
183
+ y2: undefined
184
+ }));
185
+ }
186
+ return ticks.map((tick) => ({
187
+ value: tick,
188
+ x: MARGIN.left + scaleLinear(tick, domainMin, domainMax, 0, plotWidth),
189
+ y1: MARGIN.top,
190
+ y2: MARGIN.top + plotHeight,
191
+ x1: undefined,
192
+ x2: undefined,
193
+ y: undefined
194
+ }));
195
+ });
196
+
197
+ function handleLeave() {
198
+ hoveredIndex = null;
199
+ }
200
+ function handleVisualPointerMove(event: PointerEvent) {
201
+ const target = event.target;
202
+ if (!(target instanceof Element)) {
203
+ hoveredIndex = null;
204
+ return;
205
+ }
206
+ const index = Number(target.getAttribute("data-chart-index"));
207
+ hoveredIndex = Number.isInteger(index) ? index : null;
208
+ }
209
+
210
+ const classes = () => ["st-lollipopChart", className].filter(Boolean).join(" ");
211
+ </script>
212
+
213
+ <div class={classes()}>
214
+ <div
215
+ class="st-lollipopChart__visual"
216
+ role="img"
217
+ aria-label={label}
218
+ onpointermove={handleVisualPointerMove}
219
+ onpointerleave={handleLeave}
220
+ >
221
+ <svg
222
+ viewBox="0 0 {width} {height}"
223
+ preserveAspectRatio="xMidYMid meet"
224
+ width="100%"
225
+ height="100%"
226
+ focusable="false"
227
+ aria-hidden="true"
228
+ >
229
+ <!-- gridlines + value axis ticks -->
230
+ {#if orientation === "vertical"}
231
+ {#each valueAxisTicks as tick (tick.value)}
232
+ <line class="st-lollipopChart__grid" x1={tick.x1} x2={tick.x2} y1={tick.y} y2={tick.y} />
233
+ <text
234
+ class="st-lollipopChart__tickLabel"
235
+ x={MARGIN.left - 6}
236
+ y={tick.y}
237
+ text-anchor="end"
238
+ dominant-baseline="middle"
239
+ >
240
+ {formatTick(tick.value)}
241
+ </text>
242
+ {/each}
243
+ {:else}
244
+ {#each valueAxisTicks as tick (tick.value)}
245
+ <line class="st-lollipopChart__grid" x1={tick.x} x2={tick.x} y1={tick.y1} y2={tick.y2} />
246
+ <text
247
+ class="st-lollipopChart__tickLabel"
248
+ x={tick.x}
249
+ y={height - MARGIN.bottom + 16}
250
+ text-anchor="middle"
251
+ >
252
+ {formatTick(tick.value)}
253
+ </text>
254
+ {/each}
255
+ {/if}
256
+
257
+ <!-- axes -->
258
+ <line
259
+ class="st-lollipopChart__axis"
260
+ x1={MARGIN.left}
261
+ x2={MARGIN.left}
262
+ y1={MARGIN.top}
263
+ y2={height - MARGIN.bottom}
264
+ />
265
+ <line
266
+ class="st-lollipopChart__axis"
267
+ x1={MARGIN.left}
268
+ x2={width - MARGIN.right}
269
+ y1={height - MARGIN.bottom}
270
+ y2={height - MARGIN.bottom}
271
+ />
272
+
273
+ <!-- category labels -->
274
+ {#each lollipops as pop (pop.datum.label)}
275
+ {#if orientation === "vertical"}
276
+ <text
277
+ class="st-lollipopChart__categoryLabel"
278
+ x={pop.labelX}
279
+ y={pop.labelY}
280
+ text-anchor="middle"
281
+ >
282
+ {pop.datum.label}
283
+ </text>
284
+ {:else}
285
+ <text
286
+ class="st-lollipopChart__categoryLabel"
287
+ x={pop.labelX}
288
+ y={pop.labelY}
289
+ text-anchor="end"
290
+ dominant-baseline="middle"
291
+ >
292
+ {pop.datum.label}
293
+ </text>
294
+ {/if}
295
+ {/each}
296
+
297
+ <!-- stems + dots (decorative, inside aria-hidden SVG) -->
298
+ {#each lollipops as pop, i (pop.datum.label)}
299
+ <line
300
+ class="st-lollipopChart__stem"
301
+ x1={pop.stemX1}
302
+ y1={pop.stemY1}
303
+ x2={pop.stemX2}
304
+ y2={pop.stemY2}
305
+ />
306
+ <circle
307
+ class="st-lollipopChart__dot st-lollipopChart__dot--{pop.tone}"
308
+ cx={pop.cx}
309
+ cy={pop.cy}
310
+ r={DOT_RADIUS}
311
+ data-chart-index={i}
312
+ />
313
+ <!-- value label near the dot, colour kept readable via contrastTextForTone -->
314
+ <text
315
+ class="st-lollipopChart__valueLabel"
316
+ x={pop.cx}
317
+ y={orientation === "vertical" ? pop.cy - DOT_RADIUS - 4 : pop.cy}
318
+ dx={orientation === "vertical" ? 0 : DOT_RADIUS + 4}
319
+ text-anchor={orientation === "vertical" ? "middle" : "start"}
320
+ dominant-baseline={orientation === "vertical" ? "auto" : "middle"}
321
+ style="fill: {contrastTextForTone(pop.tone)}"
322
+ >
323
+ {formatTick(pop.datum.value)}
324
+ </text>
325
+ {/each}
326
+ </svg>
327
+ </div>
328
+
329
+ <ChartDataList {label} items={dataValueItems} />
330
+
331
+ {#if hoveredIndex !== null && lollipops[hoveredIndex]}
332
+ {@const pop = lollipops[hoveredIndex]}
333
+ <div
334
+ class="st-lollipopChart__tooltip"
335
+ role="presentation"
336
+ style="left: {(pop.cx / width) * 100}%; top: {(pop.cy / height) * 100}%"
337
+ >
338
+ <span class="st-lollipopChart__tooltipLabel">{pop.datum.label}</span>
339
+ <span class="st-lollipopChart__tooltipValue">{pop.datum.value}</span>
340
+ </div>
341
+ {/if}
342
+ </div>
343
+
344
+ <style>
345
+ .st-lollipopChart {
346
+ color: var(--st-semantic-text-secondary);
347
+ display: block;
348
+ font-family: inherit;
349
+ position: relative;
350
+ width: 100%;
351
+ }
352
+
353
+ .st-lollipopChart svg {
354
+ display: block;
355
+ overflow: visible;
356
+ }
357
+
358
+ .st-lollipopChart__visual {
359
+ display: block;
360
+ }
361
+
362
+ .st-lollipopChart__grid {
363
+ stroke: var(--st-component-lollipopChart-gridStroke, var(--st-semantic-border-subtle));
364
+ stroke-dasharray: 2 3;
365
+ stroke-width: 1;
366
+ opacity: 0.7;
367
+ }
368
+
369
+ .st-lollipopChart__axis {
370
+ stroke: var(--st-component-lollipopChart-axisStroke, var(--st-semantic-border-subtle));
371
+ stroke-width: 1;
372
+ }
373
+
374
+ .st-lollipopChart__tickLabel,
375
+ .st-lollipopChart__categoryLabel {
376
+ fill: var(--st-component-lollipopChart-labelColor, var(--st-semantic-text-secondary));
377
+ font-size: 0.6875rem;
378
+ }
379
+
380
+ .st-lollipopChart__valueLabel {
381
+ font-size: 0.625rem;
382
+ font-weight: 600;
383
+ }
384
+
385
+ .st-lollipopChart__stem {
386
+ stroke: var(--st-semantic-border-interactive, var(--st-semantic-border-subtle));
387
+ stroke-width: 1.5;
388
+ }
389
+
390
+ .st-lollipopChart__dot {
391
+ cursor: pointer;
392
+ transition: opacity var(--st-motion-fast, 120ms) var(--st-motion-easing, ease);
393
+ }
394
+
395
+ .st-lollipopChart__dot:hover {
396
+ opacity: 0.82;
397
+ }
398
+
399
+ .st-lollipopChart__dot--category1 { fill: var(--st-semantic-data-category1); }
400
+ .st-lollipopChart__dot--category2 { fill: var(--st-semantic-data-category2); }
401
+ .st-lollipopChart__dot--category3 { fill: var(--st-semantic-data-category3); }
402
+ .st-lollipopChart__dot--category4 { fill: var(--st-semantic-data-category4); }
403
+ .st-lollipopChart__dot--category5 { fill: var(--st-semantic-data-category5); }
404
+ .st-lollipopChart__dot--category6 { fill: var(--st-semantic-data-category6); }
405
+ .st-lollipopChart__dot--category7 { fill: var(--st-semantic-data-category7); }
406
+ .st-lollipopChart__dot--category8 { fill: var(--st-semantic-data-category8); }
407
+
408
+ .st-lollipopChart__tooltip {
409
+ background: var(--st-component-lollipopChart-tooltipBackground, var(--st-semantic-surface-inverse));
410
+ border-radius: var(--st-radius-sm, 0.25rem);
411
+ color: var(--st-component-lollipopChart-tooltipText, var(--st-semantic-text-inverse));
412
+ display: inline-flex;
413
+ flex-direction: column;
414
+ font-size: 0.75rem;
415
+ gap: 0.125rem;
416
+ line-height: 1.2;
417
+ padding: 0.375rem 0.5rem;
418
+ pointer-events: none;
419
+ position: absolute;
420
+ transform: translate(-50%, calc(-100% - 8px));
421
+ white-space: nowrap;
422
+ z-index: 1;
423
+ }
424
+
425
+ .st-lollipopChart__tooltipLabel {
426
+ font-weight: 600;
427
+ }
428
+
429
+ .st-lollipopChart__tooltipValue {
430
+ opacity: 0.85;
431
+ }
432
+
433
+ @media (prefers-reduced-motion: reduce) {
434
+ .st-lollipopChart__dot {
435
+ transition: none;
436
+ }
437
+ }
438
+ </style>
@@ -0,0 +1,40 @@
1
+ /**
2
+ * LollipopChart — tige fine (ligne) + cercle au sommet, par catégorie.
3
+ * API canonique (référence Svelte, React/Vue doivent s'aligner).
4
+ *
5
+ * Props obligatoires :
6
+ * data LollipopChartDatum[] - tableau {label, value, tone?}
7
+ * label string - aria-label du graphique
8
+ *
9
+ * Props optionnelles :
10
+ * orientation "vertical"|"horizontal" (défaut "vertical")
11
+ * width number (défaut 480)
12
+ * height number (défaut 240)
13
+ * domain [number, number] - domaine fixe de l'axe des valeurs
14
+ * class string
15
+ */
16
+ export type LollipopChartTone = "category1" | "category2" | "category3" | "category4" | "category5" | "category6" | "category7" | "category8";
17
+ export type LollipopChartDatum = {
18
+ label: string;
19
+ value: number;
20
+ tone?: LollipopChartTone;
21
+ };
22
+ type LollipopChartProps = {
23
+ data: LollipopChartDatum[];
24
+ width?: number;
25
+ height?: number;
26
+ orientation?: "vertical" | "horizontal";
27
+ label: string;
28
+ /**
29
+ * Domaine fixe de l'axe des valeurs `[min, max]`. Quand fourni (et fini),
30
+ * l'échelle l'utilise au lieu du min/max dérivé des données — laissant
31
+ * plusieurs LollipopCharts d'une grille partager une échelle. Absent ou
32
+ * invalide → repli sur la plage auto (inchangé).
33
+ */
34
+ domain?: [number, number];
35
+ class?: string;
36
+ };
37
+ declare const LollipopChart: import("svelte").Component<LollipopChartProps, {}, "">;
38
+ type LollipopChart = ReturnType<typeof LollipopChart>;
39
+ export default LollipopChart;
40
+ //# sourceMappingURL=LollipopChart.svelte.d.ts.map