@sentropic/design-system-svelte 0.21.0 → 0.22.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.
@@ -25,6 +25,19 @@
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 when a bar is clicked / activated (Enter / Space) with the bar's
37
+ * key (its `label`). When provided the bars become focusable buttons; when
38
+ * omitted the chart is purely presentational (no interactivity, unchanged).
39
+ */
40
+ onSelect?: (key: string) => void;
28
41
  class?: string;
29
42
  };
30
43
 
@@ -34,6 +47,8 @@
34
47
  height = 240,
35
48
  orientation = "vertical",
36
49
  label,
50
+ selectedKeys = [],
51
+ onSelect,
37
52
  class: className
38
53
  }: BarChartProps = $props();
39
54
 
@@ -75,6 +90,20 @@
75
90
 
76
91
  let hoveredIndex: number | null = $state(null);
77
92
 
93
+ // Selection (controlled): fast lookup + "is any bar selected" flag. Only when
94
+ // something is selected do we dim the non-selected bars.
95
+ const selectedSet = $derived(new Set<string>(selectedKeys));
96
+ const hasSelection = $derived(selectedSet.size > 0);
97
+ const interactive = $derived(typeof onSelect === "function");
98
+
99
+ function handleBarKeydown(key: string, e: KeyboardEvent) {
100
+ if (e.key === "Enter" || e.key === " ") {
101
+ // preventDefault on Space so it activates rather than scrolling the page.
102
+ e.preventDefault();
103
+ onSelect?.(key);
104
+ }
105
+ }
106
+
78
107
  const scales = $derived.by(() => {
79
108
  const values = data.map((d) => d.value);
80
109
  const minRaw = Math.min(0, ...values);
@@ -272,15 +301,29 @@
272
301
  {/each}
273
302
 
274
303
  <!-- bars -->
304
+ <!-- A bar becomes an interactive button only when `onSelect` is provided;
305
+ the role + tabindex are then dynamic, which the static a11y check cannot
306
+ verify, hence the targeted ignore (mirrors SelectableRow). -->
275
307
  {#each bars as bar, i (bar.datum.label)}
308
+ {@const isSelected = selectedSet.has(bar.datum.label)}
309
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
276
310
  <rect
277
311
  class="st-barChart__bar st-barChart__bar--{bar.tone}"
312
+ class:st-barChart__bar--selected={isSelected}
313
+ class:st-barChart__bar--dim={hasSelection && !isSelected}
314
+ class:st-barChart__bar--interactive={interactive}
278
315
  x={bar.x}
279
316
  y={bar.y}
280
317
  width={bar.width}
281
318
  height={bar.height}
282
319
  rx="2"
283
320
  data-chart-index={i}
321
+ role={interactive ? "button" : undefined}
322
+ tabindex={interactive ? 0 : undefined}
323
+ aria-pressed={interactive ? isSelected : undefined}
324
+ aria-label={interactive ? `${bar.datum.label}: ${bar.datum.value}` : undefined}
325
+ onclick={interactive ? () => onSelect?.(bar.datum.label) : undefined}
326
+ onkeydown={interactive ? (e) => handleBarKeydown(bar.datum.label, e) : undefined}
284
327
  />
285
328
  {/each}
286
329
  </svg>
@@ -339,13 +382,38 @@
339
382
 
340
383
  .st-barChart__bar {
341
384
  cursor: pointer;
342
- transition: opacity 120ms ease;
385
+ transition: opacity var(--st-motion-fast, 120ms) var(--st-motion-easing, ease);
343
386
  }
344
387
 
345
388
  .st-barChart__bar:hover {
346
389
  opacity: 0.82;
347
390
  }
348
391
 
392
+ /* Interactive (onSelect provided): the bar is a keyboard-activable button. */
393
+ .st-barChart__bar--interactive:focus-visible {
394
+ outline: 2px solid var(--st-semantic-border-interactive, var(--st-semantic-action-primary));
395
+ outline-offset: 1px;
396
+ }
397
+
398
+ /* Non-selected bars are dimmed while a selection is active. */
399
+ .st-barChart__bar--dim {
400
+ opacity: 0.45;
401
+ }
402
+ /* Hover still lifts a dimmed bar so it stays explorable. */
403
+ .st-barChart__bar--dim:hover {
404
+ opacity: 0.7;
405
+ }
406
+
407
+ /* Selected bar: full opacity + a contrast-safe accent stroke (two signals,
408
+ never a font/size reflow). Outranks the dim rule. */
409
+ .st-barChart__bar--selected,
410
+ .st-barChart__bar--selected:hover {
411
+ opacity: 1;
412
+ stroke: var(--st-semantic-border-interactive, var(--st-semantic-action-primary));
413
+ stroke-width: 2;
414
+ paint-order: stroke;
415
+ }
416
+
349
417
  .st-barChart__bar--category1 { fill: var(--st-semantic-data-category1); }
350
418
  .st-barChart__bar--category2 { fill: var(--st-semantic-data-category2); }
351
419
  .st-barChart__bar--category3 { fill: var(--st-semantic-data-category3); }
@@ -379,4 +447,8 @@
379
447
  .st-barChart__tooltipValue {
380
448
  opacity: 0.85;
381
449
  }
450
+
451
+ @media (prefers-reduced-motion: reduce) {
452
+ .st-barChart__bar { transition: none; }
453
+ }
382
454
  </style>
@@ -10,6 +10,19 @@ 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 when a bar is clicked / activated (Enter / Space) with the bar's
22
+ * key (its `label`). When provided the bars become focusable buttons; when
23
+ * omitted the chart is purely presentational (no interactivity, unchanged).
24
+ */
25
+ onSelect?: (key: string) => void;
13
26
  class?: string;
14
27
  };
15
28
  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;AAqNJ,QAAA,MAAM,QAAQ,mDAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,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;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAwOJ,QAAA,MAAM,QAAQ,mDAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentropic/design-system-svelte",
3
- "version": "0.21.0",
3
+ "version": "0.22.0",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"