@sentropic/design-system-svelte 0.21.0 → 0.22.1
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/BarChart.svelte +149 -1
- package/dist/BarChart.svelte.d.ts +16 -0
- package/dist/BarChart.svelte.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/BarChart.svelte
CHANGED
|
@@ -25,6 +25,22 @@
|
|
|
25
25
|
height?: number;
|
|
26
26
|
orientation?: "vertical" | "horizontal";
|
|
27
27
|
label: string;
|
|
28
|
+
/**
|
|
29
|
+
* Keys of the currently selected bars (a bar's key is its `label`).
|
|
30
|
+
* CONTROLLED — the parent owns the toggle; the component never stores
|
|
31
|
+
* selection. When non-empty the selected bars stay full opacity (+ accent)
|
|
32
|
+
* and the rest dim; when empty every bar is normal. Defaults to [].
|
|
33
|
+
*/
|
|
34
|
+
selectedKeys?: string[];
|
|
35
|
+
/**
|
|
36
|
+
* Called with the bar's key (its `label`) when the user selects it. When
|
|
37
|
+
* provided, an ACCESSIBLE row of filter chips (real <button>s) is rendered
|
|
38
|
+
* OUTSIDE the aria-hidden SVG — that is the keyboard + screen-reader surface.
|
|
39
|
+
* The SVG bars themselves stay decorative (aria-hidden) and only offer a
|
|
40
|
+
* mouse click shortcut for sighted pointer users. When omitted the chart is
|
|
41
|
+
* purely presentational (no interactivity, unchanged).
|
|
42
|
+
*/
|
|
43
|
+
onSelect?: (key: string) => void;
|
|
28
44
|
class?: string;
|
|
29
45
|
};
|
|
30
46
|
|
|
@@ -34,6 +50,8 @@
|
|
|
34
50
|
height = 240,
|
|
35
51
|
orientation = "vertical",
|
|
36
52
|
label,
|
|
53
|
+
selectedKeys = [],
|
|
54
|
+
onSelect,
|
|
37
55
|
class: className
|
|
38
56
|
}: BarChartProps = $props();
|
|
39
57
|
|
|
@@ -75,6 +93,12 @@
|
|
|
75
93
|
|
|
76
94
|
let hoveredIndex: number | null = $state(null);
|
|
77
95
|
|
|
96
|
+
// Selection (controlled): fast lookup + "is any bar selected" flag. Only when
|
|
97
|
+
// something is selected do we dim the non-selected bars.
|
|
98
|
+
const selectedSet = $derived(new Set<string>(selectedKeys));
|
|
99
|
+
const hasSelection = $derived(selectedSet.size > 0);
|
|
100
|
+
const interactive = $derived(typeof onSelect === "function");
|
|
101
|
+
|
|
78
102
|
const scales = $derived.by(() => {
|
|
79
103
|
const values = data.map((d) => d.value);
|
|
80
104
|
const minRaw = Math.min(0, ...values);
|
|
@@ -272,20 +296,55 @@
|
|
|
272
296
|
{/each}
|
|
273
297
|
|
|
274
298
|
<!-- bars -->
|
|
299
|
+
<!-- The bars live inside an aria-hidden SVG, so they are NEVER an accessible
|
|
300
|
+
surface. When `onSelect` is provided they only carry a mouse click
|
|
301
|
+
shortcut (cursor:pointer) for sighted pointer users — keyboard + screen
|
|
302
|
+
readers use the filter-chip buttons rendered below, outside this SVG. -->
|
|
275
303
|
{#each bars as bar, i (bar.datum.label)}
|
|
304
|
+
{@const isSelected = selectedSet.has(bar.datum.label)}
|
|
305
|
+
<!-- The mouse click is a deliberate sighted-pointer-only shortcut on a
|
|
306
|
+
decorative element inside an aria-hidden SVG; the real keyboard + AT
|
|
307
|
+
path is the filter-chip <button>s below. No ARIA role/keyboard here
|
|
308
|
+
on purpose (it would be a lie under aria-hidden). -->
|
|
309
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
310
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
276
311
|
<rect
|
|
277
312
|
class="st-barChart__bar st-barChart__bar--{bar.tone}"
|
|
313
|
+
class:st-barChart__bar--selected={isSelected}
|
|
314
|
+
class:st-barChart__bar--dim={hasSelection && !isSelected}
|
|
315
|
+
class:st-barChart__bar--interactive={interactive}
|
|
278
316
|
x={bar.x}
|
|
279
317
|
y={bar.y}
|
|
280
318
|
width={bar.width}
|
|
281
319
|
height={bar.height}
|
|
282
320
|
rx="2"
|
|
283
321
|
data-chart-index={i}
|
|
322
|
+
onclick={interactive ? () => onSelect?.(bar.datum.label) : undefined}
|
|
284
323
|
/>
|
|
285
324
|
{/each}
|
|
286
325
|
</svg>
|
|
287
326
|
</div>
|
|
288
327
|
|
|
328
|
+
{#if interactive}
|
|
329
|
+
<!-- Accessible selection surface — real <button>s OUTSIDE the aria-hidden
|
|
330
|
+
SVG. This is the keyboard + screen-reader path for filtering. -->
|
|
331
|
+
<div class="st-barChart__filters" role="group" aria-label={`Filtrer par ${label}`}>
|
|
332
|
+
{#each bars as bar (bar.datum.label)}
|
|
333
|
+
{@const isSelected = selectedSet.has(bar.datum.label)}
|
|
334
|
+
<button
|
|
335
|
+
type="button"
|
|
336
|
+
class="st-barChart__filterChip st-barChart__filterChip--{bar.tone}"
|
|
337
|
+
class:st-barChart__filterChip--selected={isSelected}
|
|
338
|
+
aria-pressed={isSelected}
|
|
339
|
+
onclick={() => onSelect?.(bar.datum.label)}
|
|
340
|
+
>
|
|
341
|
+
<span class="st-barChart__filterSwatch" aria-hidden="true"></span>
|
|
342
|
+
{bar.datum.label}: {bar.datum.value}
|
|
343
|
+
</button>
|
|
344
|
+
{/each}
|
|
345
|
+
</div>
|
|
346
|
+
{/if}
|
|
347
|
+
|
|
289
348
|
<ChartDataList {label} items={dataValueItems} />
|
|
290
349
|
|
|
291
350
|
{#if hoveredIndex !== null && bars[hoveredIndex]}
|
|
@@ -339,13 +398,35 @@
|
|
|
339
398
|
|
|
340
399
|
.st-barChart__bar {
|
|
341
400
|
cursor: pointer;
|
|
342
|
-
transition: opacity 120ms ease;
|
|
401
|
+
transition: opacity var(--st-motion-fast, 120ms) var(--st-motion-easing, ease);
|
|
343
402
|
}
|
|
344
403
|
|
|
345
404
|
.st-barChart__bar:hover {
|
|
346
405
|
opacity: 0.82;
|
|
347
406
|
}
|
|
348
407
|
|
|
408
|
+
/* Non-selected bars are dimmed while a selection is active. Floor kept high
|
|
409
|
+
(0.6) so the colour stays distinguishable — opacity is never the sole cue;
|
|
410
|
+
selection also adds a stroke (shape), and the values stay in the chips +
|
|
411
|
+
ChartDataList. */
|
|
412
|
+
.st-barChart__bar--dim {
|
|
413
|
+
opacity: 0.6;
|
|
414
|
+
}
|
|
415
|
+
/* Hover still lifts a dimmed bar so it stays explorable. */
|
|
416
|
+
.st-barChart__bar--dim:hover {
|
|
417
|
+
opacity: 0.8;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/* Selected bar: full opacity + a contrast-safe accent stroke (two signals,
|
|
421
|
+
never a font/size reflow). Outranks the dim rule. */
|
|
422
|
+
.st-barChart__bar--selected,
|
|
423
|
+
.st-barChart__bar--selected:hover {
|
|
424
|
+
opacity: 1;
|
|
425
|
+
stroke: var(--st-semantic-border-interactive, var(--st-semantic-action-primary));
|
|
426
|
+
stroke-width: 2;
|
|
427
|
+
paint-order: stroke;
|
|
428
|
+
}
|
|
429
|
+
|
|
349
430
|
.st-barChart__bar--category1 { fill: var(--st-semantic-data-category1); }
|
|
350
431
|
.st-barChart__bar--category2 { fill: var(--st-semantic-data-category2); }
|
|
351
432
|
.st-barChart__bar--category3 { fill: var(--st-semantic-data-category3); }
|
|
@@ -355,6 +436,68 @@
|
|
|
355
436
|
.st-barChart__bar--category7 { fill: var(--st-semantic-data-category7); }
|
|
356
437
|
.st-barChart__bar--category8 { fill: var(--st-semantic-data-category8); }
|
|
357
438
|
|
|
439
|
+
/* Accessible filter chips — keyboard + screen-reader selection surface,
|
|
440
|
+
rendered outside the aria-hidden SVG. */
|
|
441
|
+
.st-barChart__filters {
|
|
442
|
+
display: flex;
|
|
443
|
+
flex-wrap: wrap;
|
|
444
|
+
gap: var(--st-spacing-2, 0.5rem);
|
|
445
|
+
margin-top: var(--st-spacing-2, 0.5rem);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.st-barChart__filterChip {
|
|
449
|
+
align-items: center;
|
|
450
|
+
background: var(--st-semantic-surface-subtle, #f8fafc);
|
|
451
|
+
border: 1px solid var(--st-semantic-border-interactive, #cbd5e1);
|
|
452
|
+
border-radius: var(--st-radius-pill, 999px);
|
|
453
|
+
color: var(--st-semantic-text-secondary, #475569);
|
|
454
|
+
cursor: var(--st-cursor-interactive, pointer);
|
|
455
|
+
display: inline-flex;
|
|
456
|
+
font: inherit;
|
|
457
|
+
font-size: 0.8125rem;
|
|
458
|
+
font-weight: 500;
|
|
459
|
+
gap: var(--st-spacing-1, 0.25rem);
|
|
460
|
+
line-height: 1;
|
|
461
|
+
padding: 0.3125rem var(--st-spacing-2, 0.5rem);
|
|
462
|
+
transition:
|
|
463
|
+
background-color var(--st-motion-fast, 120ms) var(--st-motion-easing, ease),
|
|
464
|
+
color var(--st-motion-fast, 120ms) var(--st-motion-easing, ease),
|
|
465
|
+
border-color var(--st-motion-fast, 120ms) var(--st-motion-easing, ease);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.st-barChart__filterChip:hover {
|
|
469
|
+
background: var(--st-semantic-surface-hover, #eef2f7);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.st-barChart__filterChip:focus-visible {
|
|
473
|
+
outline: 2px solid var(--st-semantic-border-interactive, var(--st-semantic-action-primary));
|
|
474
|
+
outline-offset: 2px;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/* Selected chip: solid accent fill + matching text — signalled by colour AND
|
|
478
|
+
by aria-pressed, never by opacity alone. */
|
|
479
|
+
.st-barChart__filterChip--selected {
|
|
480
|
+
background: var(--st-semantic-action-primary, #2563eb);
|
|
481
|
+
border-color: var(--st-semantic-action-primary, #2563eb);
|
|
482
|
+
color: var(--st-semantic-text-inverse, #fff);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/* Colour swatch echoing the bar tone, for quick visual mapping chip↔bar. */
|
|
486
|
+
.st-barChart__filterSwatch {
|
|
487
|
+
border-radius: var(--st-radius-sm, 0.25rem);
|
|
488
|
+
display: inline-block;
|
|
489
|
+
height: 0.625rem;
|
|
490
|
+
width: 0.625rem;
|
|
491
|
+
}
|
|
492
|
+
.st-barChart__filterChip--category1 .st-barChart__filterSwatch { background: var(--st-semantic-data-category1); }
|
|
493
|
+
.st-barChart__filterChip--category2 .st-barChart__filterSwatch { background: var(--st-semantic-data-category2); }
|
|
494
|
+
.st-barChart__filterChip--category3 .st-barChart__filterSwatch { background: var(--st-semantic-data-category3); }
|
|
495
|
+
.st-barChart__filterChip--category4 .st-barChart__filterSwatch { background: var(--st-semantic-data-category4); }
|
|
496
|
+
.st-barChart__filterChip--category5 .st-barChart__filterSwatch { background: var(--st-semantic-data-category5); }
|
|
497
|
+
.st-barChart__filterChip--category6 .st-barChart__filterSwatch { background: var(--st-semantic-data-category6); }
|
|
498
|
+
.st-barChart__filterChip--category7 .st-barChart__filterSwatch { background: var(--st-semantic-data-category7); }
|
|
499
|
+
.st-barChart__filterChip--category8 .st-barChart__filterSwatch { background: var(--st-semantic-data-category8); }
|
|
500
|
+
|
|
358
501
|
.st-barChart__tooltip {
|
|
359
502
|
background: var(--st-component-barChart-tooltipBackground, var(--st-semantic-surface-inverse));
|
|
360
503
|
border-radius: var(--st-radius-sm, 0.25rem);
|
|
@@ -379,4 +522,9 @@
|
|
|
379
522
|
.st-barChart__tooltipValue {
|
|
380
523
|
opacity: 0.85;
|
|
381
524
|
}
|
|
525
|
+
|
|
526
|
+
@media (prefers-reduced-motion: reduce) {
|
|
527
|
+
.st-barChart__bar,
|
|
528
|
+
.st-barChart__filterChip { transition: none; }
|
|
529
|
+
}
|
|
382
530
|
</style>
|
|
@@ -10,6 +10,22 @@ type BarChartProps = {
|
|
|
10
10
|
height?: number;
|
|
11
11
|
orientation?: "vertical" | "horizontal";
|
|
12
12
|
label: string;
|
|
13
|
+
/**
|
|
14
|
+
* Keys of the currently selected bars (a bar's key is its `label`).
|
|
15
|
+
* CONTROLLED — the parent owns the toggle; the component never stores
|
|
16
|
+
* selection. When non-empty the selected bars stay full opacity (+ accent)
|
|
17
|
+
* and the rest dim; when empty every bar is normal. Defaults to [].
|
|
18
|
+
*/
|
|
19
|
+
selectedKeys?: string[];
|
|
20
|
+
/**
|
|
21
|
+
* Called with the bar's key (its `label`) when the user selects it. When
|
|
22
|
+
* provided, an ACCESSIBLE row of filter chips (real <button>s) is rendered
|
|
23
|
+
* OUTSIDE the aria-hidden SVG — that is the keyboard + screen-reader surface.
|
|
24
|
+
* The SVG bars themselves stay decorative (aria-hidden) and only offer a
|
|
25
|
+
* mouse click shortcut for sighted pointer users. When omitted the chart is
|
|
26
|
+
* purely presentational (no interactivity, unchanged).
|
|
27
|
+
*/
|
|
28
|
+
onSelect?: (key: string) => void;
|
|
13
29
|
class?: string;
|
|
14
30
|
};
|
|
15
31
|
declare const BarChart: import("svelte").Component<BarChartProps, {}, "">;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BarChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/BarChart.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,YAAY,GACpB,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,YAAY,CAAC;CACrB,CAAC;AAMF,KAAK,aAAa,GAAG;IACnB,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;
|
|
1
|
+
{"version":3,"file":"BarChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/BarChart.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,YAAY,GACpB,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,YAAY,CAAC;CACrB,CAAC;AAMF,KAAK,aAAa,GAAG;IACnB,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA+OJ,QAAA,MAAM,QAAQ,mDAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
|