@sentropic/design-system-svelte 0.34.24 → 0.34.26

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.
@@ -24,6 +24,7 @@
24
24
  type ChartAnnotation
25
25
  } from "./chartAnnotations.js";
26
26
  import { formatDataLabel, normalizeDataLabels, type DataLabelsProp } from "./chartDataLabels.js";
27
+ import { keyForX, resolveActiveIndex } from "./chartCrosshair.js";
27
28
 
28
29
  type AreaChartProps = {
29
30
  data: (number | AreaChartDatum)[];
@@ -46,6 +47,20 @@
46
47
  * ChartDataList.
47
48
  */
48
49
  dataLabels?: DataLabelsProp;
50
+ /**
51
+ * CONTROLLED synchronised hover key (FR-3). A datum's key is `String(x)`. When
52
+ * provided (string or null), the crosshair + tooltip track this key instead of
53
+ * the chart's internal pointer hover (null ⇒ nothing shown), letting a parent
54
+ * share one hover channel across several aligned charts. Absent (`undefined`)
55
+ * keeps the legacy uncontrolled behaviour.
56
+ */
57
+ hoverKey?: string | null;
58
+ /**
59
+ * Emitted when the user hovers a datum (its key) or leaves the plot (`null`).
60
+ * Always fired on pointer move/leave — even while CONTROLLED — so dataviz can
61
+ * keep the shared hover channel in sync.
62
+ */
63
+ onHoverKeyChange?: (key: string | null) => void;
49
64
  class?: string;
50
65
  };
51
66
 
@@ -58,6 +73,8 @@
58
73
  label,
59
74
  annotations,
60
75
  dataLabels,
76
+ hoverKey,
77
+ onHoverKeyChange,
61
78
  class: className
62
79
  }: AreaChartProps = $props();
63
80
 
@@ -271,19 +288,34 @@
271
288
  return entries;
272
289
  });
273
290
 
291
+ // Stable key per datum (FR-3): `String(x)` of the normalised datum (a bare
292
+ // number becomes its index). Resolves a controlled `hoverKey` to an index and
293
+ // feeds `onHoverKeyChange` from pointer events.
294
+ const hoverKeys = $derived(normalizedData.map((d) => keyForX(d.x)));
295
+ function emitHoverKey(index: number | null) {
296
+ onHoverKeyChange?.(index == null ? null : hoverKeys[index] ?? null);
297
+ }
274
298
  function handleLeave() {
275
299
  hoveredIndex = null;
300
+ emitHoverKey(null);
276
301
  }
277
302
  function handleVisualPointerMove(event: PointerEvent) {
278
303
  const target = event.target;
279
304
  if (!(target instanceof Element)) {
280
305
  hoveredIndex = null;
306
+ emitHoverKey(null);
281
307
  return;
282
308
  }
283
- const index = Number(target.getAttribute("data-chart-index"));
284
- hoveredIndex = Number.isInteger(index) ? index : null;
309
+ const raw = Number(target.getAttribute("data-chart-index"));
310
+ const index = Number.isInteger(raw) ? raw : null;
311
+ hoveredIndex = index;
312
+ emitHoverKey(index);
285
313
  }
286
314
 
315
+ // Index whose crosshair/tooltip is DISPLAYED: the controlled `hoverKey` when
316
+ // provided (resolved against `hoverKeys`), else the internal pointer index.
317
+ const activeIndex = $derived(resolveActiveIndex(hoverKey, hoveredIndex, hoverKeys));
318
+
287
319
  // Generates a unique gradient id to avoid conflicts when rendering multiple charts on the same page
288
320
  const gradientId = $derived.by(() => {
289
321
  return `st-areachart-gradient-${Math.random().toString(36).substring(2, 9)}`;
@@ -441,13 +473,23 @@
441
473
  {/each}
442
474
  </g>
443
475
  {/if}
476
+
477
+ <!-- Crosshair (FR-3) — a tokenised vertical line + marker at the active key.
478
+ Decorative (aria-hidden); the value is in the tooltip + ChartDataList. -->
479
+ {#if activeIndex >= 0 && points[activeIndex]}
480
+ {@const cp = points[activeIndex]}
481
+ <g class="st-areaChart__crosshair" aria-hidden="true">
482
+ <line class="st-areaChart__crosshairLine" x1={cp.x} x2={cp.x} y1={MARGIN.top} y2={MARGIN.top + plotHeight} />
483
+ <circle class="st-areaChart__crosshairMarker" cx={cp.x} cy={cp.y} r="5" />
484
+ </g>
485
+ {/if}
444
486
  </svg>
445
487
  </div>
446
488
 
447
489
  <ChartDataList {label} items={dataValueItems} />
448
490
 
449
- {#if hoveredIndex !== null && points[hoveredIndex]}
450
- {@const p = points[hoveredIndex]}
491
+ {#if activeIndex >= 0 && points[activeIndex]}
492
+ {@const p = points[activeIndex]}
451
493
  <div
452
494
  class="st-areaChart__tooltip"
453
495
  role="presentation"
@@ -588,4 +630,19 @@
588
630
  font-size: 0.6875rem;
589
631
  font-weight: 600;
590
632
  }
633
+
634
+ /* --- Crosshair layer (FR-3) ----------------------------------------------
635
+ A tokenised dashed vertical line at the active (hovered/controlled) key,
636
+ plus an emphasised marker on the point. Decorative (aria-hidden). */
637
+ .st-areaChart__crosshairLine {
638
+ stroke: var(--st-semantic-border-strong);
639
+ stroke-width: 1;
640
+ stroke-dasharray: 3 3;
641
+ opacity: 0.7;
642
+ }
643
+ .st-areaChart__crosshairMarker {
644
+ fill: currentColor;
645
+ stroke: var(--st-semantic-surface-default);
646
+ stroke-width: 2;
647
+ }
591
648
  </style>
@@ -26,6 +26,20 @@ type AreaChartProps = {
26
26
  * ChartDataList.
27
27
  */
28
28
  dataLabels?: DataLabelsProp;
29
+ /**
30
+ * CONTROLLED synchronised hover key (FR-3). A datum's key is `String(x)`. When
31
+ * provided (string or null), the crosshair + tooltip track this key instead of
32
+ * the chart's internal pointer hover (null ⇒ nothing shown), letting a parent
33
+ * share one hover channel across several aligned charts. Absent (`undefined`)
34
+ * keeps the legacy uncontrolled behaviour.
35
+ */
36
+ hoverKey?: string | null;
37
+ /**
38
+ * Emitted when the user hovers a datum (its key) or leaves the plot (`null`).
39
+ * Always fired on pointer move/leave — even while CONTROLLED — so dataviz can
40
+ * keep the shared hover channel in sync.
41
+ */
42
+ onHoverKeyChange?: (key: string | null) => void;
29
43
  class?: string;
30
44
  };
31
45
  declare const AreaChart: import("svelte").Component<AreaChartProps, {}, "">;
@@ -1 +1 @@
1
- {"version":3,"file":"AreaChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/AreaChart.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,aAAa,GACrB,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,MAAM,cAAc,GAAG;IAC3B,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACnB,CAAC,EAAE,MAAM,CAAC;CACX,CAAC;AAIJ,OAAO,EAIH,KAAK,eAAe,EACrB,MAAM,uBAAuB,CAAC;AACjC,OAAO,EAAwC,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG/F,KAAK,cAAc,GAAG;IACpB,IAAI,EAAE,CAAC,MAAM,GAAG,cAAc,CAAC,EAAE,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAuWJ,QAAA,MAAM,SAAS,oDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"AreaChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/AreaChart.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,aAAa,GACrB,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,MAAM,cAAc,GAAG;IAC3B,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACnB,CAAC,EAAE,MAAM,CAAC;CACX,CAAC;AAIJ,OAAO,EAIH,KAAK,eAAe,EACrB,MAAM,uBAAuB,CAAC;AACjC,OAAO,EAAwC,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAI/F,KAAK,cAAc,GAAG;IACpB,IAAI,EAAE,CAAC,MAAM,GAAG,cAAc,CAAC,EAAE,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAkYJ,QAAA,MAAM,SAAS,oDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -57,6 +57,7 @@
57
57
  type ChartAnnotation
58
58
  } from "./chartAnnotations.js";
59
59
  import { formatDataLabel, normalizeDataLabels, type DataLabelsProp } from "./chartDataLabels.js";
60
+ import { resolveActiveIndex } from "./chartCrosshair.js";
60
61
 
61
62
  type BarChartProps = {
62
63
  data: BarChartDatum[];
@@ -124,6 +125,20 @@
124
125
  * cross-chart parity and otherwise ignored.
125
126
  */
126
127
  showLegend?: boolean;
128
+ /**
129
+ * CONTROLLED synchronised hover key (FR-3). A bar's key is its `label`. When
130
+ * provided (string or null), the crosshair + tooltip track this key instead of
131
+ * the chart's internal pointer hover (null ⇒ nothing shown), letting a parent
132
+ * share one hover channel across several aligned charts. Absent (`undefined`)
133
+ * keeps the legacy uncontrolled behaviour. Independent of `selectedKeys`.
134
+ */
135
+ hoverKey?: string | null;
136
+ /**
137
+ * Emitted when the user hovers a bar (its `label`) or leaves the plot (`null`).
138
+ * Always fired on pointer move/leave — even while CONTROLLED — so dataviz can
139
+ * keep the shared hover channel in sync.
140
+ */
141
+ onHoverKeyChange?: (key: string | null) => void;
127
142
  class?: string;
128
143
  };
129
144
 
@@ -144,6 +159,8 @@
144
159
  scale = "linear",
145
160
  invertAxis = false,
146
161
  showLegend,
162
+ hoverKey,
163
+ onHoverKeyChange,
147
164
  class: className
148
165
  }: BarChartProps = $props();
149
166
 
@@ -599,19 +616,33 @@
599
616
  }));
600
617
  });
601
618
 
619
+ // Stable key per bar (FR-3): its `label`. Resolves a controlled `hoverKey` to
620
+ // an index and feeds `onHoverKeyChange` from pointer events.
621
+ const hoverKeys = $derived(bars.map((b) => b.datum.label));
622
+ function emitHoverKey(index: number | null) {
623
+ onHoverKeyChange?.(index == null ? null : hoverKeys[index] ?? null);
624
+ }
602
625
  function handleLeave() {
603
626
  hoveredIndex = null;
627
+ emitHoverKey(null);
604
628
  }
605
629
  function handleVisualPointerMove(event: PointerEvent) {
606
630
  const target = event.target;
607
631
  if (!(target instanceof Element)) {
608
632
  hoveredIndex = null;
633
+ emitHoverKey(null);
609
634
  return;
610
635
  }
611
- const index = Number(target.getAttribute("data-chart-index"));
612
- hoveredIndex = Number.isInteger(index) ? index : null;
636
+ const raw = Number(target.getAttribute("data-chart-index"));
637
+ const index = Number.isInteger(raw) ? raw : null;
638
+ hoveredIndex = index;
639
+ emitHoverKey(index);
613
640
  }
614
641
 
642
+ // Index whose crosshair/tooltip is DISPLAYED: the controlled `hoverKey` when
643
+ // provided (resolved against `hoverKeys`), else the internal pointer index.
644
+ const activeIndex = $derived(resolveActiveIndex(hoverKey, hoveredIndex, hoverKeys));
645
+
615
646
  const classes = () => ["st-barChart", className].filter(Boolean).join(" ");
616
647
  </script>
617
648
 
@@ -836,6 +867,20 @@
836
867
  {/each}
837
868
  </g>
838
869
  {/if}
870
+
871
+ <!-- Crosshair (FR-3) — a tokenised dashed line on the CATEGORY axis at the
872
+ active bar: vertical (vertical bars) / horizontal (horizontal bars).
873
+ Decorative (aria-hidden); the value is in the tooltip + ChartDataList. -->
874
+ {#if activeIndex >= 0 && bars[activeIndex]}
875
+ {@const cb = bars[activeIndex]}
876
+ <g class="st-barChart__crosshair" aria-hidden="true">
877
+ {#if isVertical}
878
+ <line class="st-barChart__crosshairLine" x1={cb.cx} x2={cb.cx} y1={MARGIN.top} y2={MARGIN.top + scales.plotHeight} />
879
+ {:else}
880
+ <line class="st-barChart__crosshairLine" x1={MARGIN.left} x2={MARGIN.left + scales.plotWidth} y1={cb.cy} y2={cb.cy} />
881
+ {/if}
882
+ </g>
883
+ {/if}
839
884
  </svg>
840
885
  </div>
841
886
 
@@ -861,8 +906,8 @@
861
906
 
862
907
  <ChartDataList {label} items={dataValueItems} />
863
908
 
864
- {#if hoveredIndex !== null && bars[hoveredIndex]}
865
- {@const bar = bars[hoveredIndex]}
909
+ {#if activeIndex >= 0 && bars[activeIndex]}
910
+ {@const bar = bars[activeIndex]}
866
911
  <div
867
912
  class="st-barChart__tooltip"
868
913
  role="presentation"
@@ -1124,4 +1169,14 @@
1124
1169
  font-size: 0.6875rem;
1125
1170
  font-weight: 600;
1126
1171
  }
1172
+
1173
+ /* --- Crosshair layer (FR-3) ----------------------------------------------
1174
+ A tokenised dashed line on the CATEGORY axis at the active (hovered/
1175
+ controlled) bar. Decorative (aria-hidden). */
1176
+ .st-barChart__crosshairLine {
1177
+ stroke: var(--st-semantic-border-strong);
1178
+ stroke-width: 1;
1179
+ stroke-dasharray: 3 3;
1180
+ opacity: 0.7;
1181
+ }
1127
1182
  </style>
@@ -99,6 +99,20 @@ type BarChartProps = {
99
99
  * cross-chart parity and otherwise ignored.
100
100
  */
101
101
  showLegend?: boolean;
102
+ /**
103
+ * CONTROLLED synchronised hover key (FR-3). A bar's key is its `label`. When
104
+ * provided (string or null), the crosshair + tooltip track this key instead of
105
+ * the chart's internal pointer hover (null ⇒ nothing shown), letting a parent
106
+ * share one hover channel across several aligned charts. Absent (`undefined`)
107
+ * keeps the legacy uncontrolled behaviour. Independent of `selectedKeys`.
108
+ */
109
+ hoverKey?: string | null;
110
+ /**
111
+ * Emitted when the user hovers a bar (its `label`) or leaves the plot (`null`).
112
+ * Always fired on pointer move/leave — even while CONTROLLED — so dataviz can
113
+ * keep the shared hover channel in sync.
114
+ */
115
+ onHoverKeyChange?: (key: string | null) => void;
102
116
  class?: string;
103
117
  };
104
118
  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;IACpB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;AAEpF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,IAAI,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,sEAAsE;AACtE,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,KAAK,CAAC;AAI5C,OAAO,EAIH,KAAK,eAAe,EACrB,MAAM,uBAAuB,CAAC;AACjC,OAAO,EAAwC,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG/F,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,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,+DAA+D;IAC/D,cAAc,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACtC,oDAAoD;IACpD,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC;IACpB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B;;;;;OAKG;IACH,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAipBJ,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;IACpB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;AAEpF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,IAAI,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,sEAAsE;AACtE,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,KAAK,CAAC;AAI5C,OAAO,EAIH,KAAK,eAAe,EACrB,MAAM,uBAAuB,CAAC;AACjC,OAAO,EAAwC,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAI/F,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,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,+DAA+D;IAC/D,cAAc,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACtC,oDAAoD;IACpD,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC;IACpB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B;;;;;OAKG;IACH,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA8qBJ,QAAA,MAAM,QAAQ,mDAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
@@ -33,6 +33,15 @@
33
33
  leftAxisLabel?: string;
34
34
  rightAxisLabel?: string;
35
35
  legend?: boolean;
36
+ /**
37
+ * Interactive legend (FR-4). Ids/labels of bar/line series hidden from the
38
+ * render (controlled by the parent; default = all visible). Hidden series
39
+ * are omitted and their legend item is shown "off" (`aria-pressed`).
40
+ * Undefined → legacy non-interactive legend, unless `onToggleSeries` is set.
41
+ */
42
+ hiddenSeries?: string[];
43
+ /** Emitted on click / Enter / Space on a legend item. */
44
+ onToggleSeries?: (seriesId: string) => void;
36
45
  width?: number;
37
46
  height?: number;
38
47
  label: string;
@@ -46,12 +55,18 @@
46
55
  leftAxisLabel,
47
56
  rightAxisLabel,
48
57
  legend = true,
58
+ hiddenSeries,
59
+ onToggleSeries,
49
60
  width = 480,
50
61
  height = 240,
51
62
  label,
52
63
  class: className
53
64
  }: ComboChartProps = $props();
54
65
 
66
+ // Interactive legend is active as soon as the parent wires either prop.
67
+ const legendInteractive = $derived(onToggleSeries !== undefined || hiddenSeries !== undefined);
68
+ const hiddenSet = $derived(new Set(hiddenSeries ?? []));
69
+
55
70
  const MARGIN = { top: 12, right: 52, bottom: 32, left: 52 };
56
71
 
57
72
  function niceTicks(min: number, max: number, target = 5): number[] {
@@ -92,8 +107,9 @@
92
107
  const plotHeight = $derived(Math.max(height - MARGIN.top - MARGIN.bottom, 1));
93
108
 
94
109
  // Left axis (bars): include zero in the domain so bars rest on a baseline.
110
+ // Hidden series are excluded so the axis rescales to what is visible.
95
111
  const leftScale = $derived.by(() => {
96
- const values = bars.flatMap((s) => s.data);
112
+ const values = bars.filter((s) => !hiddenSet.has(s.label)).flatMap((s) => s.data);
97
113
  const minRaw = Math.min(0, ...(values.length ? values : [0]));
98
114
  const maxRaw = Math.max(0, ...(values.length ? values : [0]));
99
115
  const ticks = niceTicks(minRaw, maxRaw, 5);
@@ -102,7 +118,7 @@
102
118
 
103
119
  // Right axis (lines): padded domain like LineChart.
104
120
  const rightScale = $derived.by(() => {
105
- const values = lines.flatMap((s) => s.data);
121
+ const values = lines.filter((s) => !hiddenSet.has(s.label)).flatMap((s) => s.data);
106
122
  if (values.length === 0) {
107
123
  const ticks = niceTicks(0, 1, 5);
108
124
  return { ticks, domainMin: ticks[0], domainMax: ticks[ticks.length - 1] };
@@ -136,6 +152,7 @@
136
152
  const groupX = MARGIN.left + band * ci + (band - groupWidth) / 2;
137
153
  const segments = bars
138
154
  .map((series, si) => {
155
+ if (hiddenSet.has(series.label)) return null;
139
156
  const raw = series.data[ci];
140
157
  if (!isPresent(raw)) return null;
141
158
  const value = raw;
@@ -203,6 +220,7 @@
203
220
  path,
204
221
  points,
205
222
  seriesLabel: series.label,
223
+ hidden: hiddenSet.has(series.label),
206
224
  tone: series.tone ?? `category${((bars.length + li) % 8) + 1}`
207
225
  };
208
226
  });
@@ -240,12 +258,12 @@
240
258
  ]);
241
259
 
242
260
  const dataValueItems = $derived([
243
- ...bars.flatMap((s) =>
244
- categories.map((c, ci) => `${s.label}, ${c}: ${s.data[ci] ?? 0}`)
245
- ),
246
- ...lines.flatMap((s) =>
247
- categories.map((c, ci) => `${s.label}, ${c}: ${s.data[ci] ?? 0}`)
248
- )
261
+ ...bars
262
+ .filter((s) => !hiddenSet.has(s.label))
263
+ .flatMap((s) => categories.map((c, ci) => `${s.label}, ${c}: ${s.data[ci] ?? 0}`)),
264
+ ...lines
265
+ .filter((s) => !hiddenSet.has(s.label))
266
+ .flatMap((s) => categories.map((c, ci) => `${s.label}, ${c}: ${s.data[ci] ?? 0}`))
249
267
  ]);
250
268
 
251
269
  type Hover =
@@ -404,25 +422,27 @@
404
422
 
405
423
  <!-- lines -->
406
424
  {#each lineSeries as series, li (li)}
407
- <path
408
- class="st-comboChart__line st-comboChart__line--{series.tone}"
409
- d={series.path}
410
- fill="none"
411
- stroke-width="2"
412
- stroke-linecap="round"
413
- stroke-linejoin="round"
414
- />
415
- {#each series.points as p, pi (pi)}
416
- <circle
417
- class="st-comboChart__dot st-comboChart__dot--{series.tone}"
418
- cx={p.x}
419
- cy={p.y}
420
- r="4"
421
- data-chart-kind="line"
422
- data-chart-a={li}
423
- data-chart-b={pi}
425
+ {#if !series.hidden}
426
+ <path
427
+ class="st-comboChart__line st-comboChart__line--{series.tone}"
428
+ d={series.path}
429
+ fill="none"
430
+ stroke-width="2"
431
+ stroke-linecap="round"
432
+ stroke-linejoin="round"
424
433
  />
425
- {/each}
434
+ {#each series.points as p, pi (pi)}
435
+ <circle
436
+ class="st-comboChart__dot st-comboChart__dot--{series.tone}"
437
+ cx={p.x}
438
+ cy={p.y}
439
+ r="4"
440
+ data-chart-kind="line"
441
+ data-chart-a={li}
442
+ data-chart-b={pi}
443
+ />
444
+ {/each}
445
+ {/if}
426
446
  {/each}
427
447
  </svg>
428
448
  </div>
@@ -430,13 +450,28 @@
430
450
  <ChartDataList {label} items={dataValueItems} />
431
451
 
432
452
  {#if legend && legendItems.length > 0}
433
- <ul class="st-comboChart__legend" aria-hidden="true">
453
+ <ul class="st-comboChart__legend" aria-hidden={legendInteractive ? undefined : "true"}>
434
454
  {#each legendItems as item (item.key)}
435
- <li class="st-comboChart__legendItem">
436
- <span
437
- class="st-comboChart__legendSwatch st-comboChart__legendSwatch--{item.kind} st-comboChart__legendSwatch--{item.tone}"
438
- ></span>
439
- {item.label}
455
+ {@const off = hiddenSet.has(item.label)}
456
+ <li class="st-comboChart__legendItem" class:st-comboChart__legendItem--off={legendInteractive && off}>
457
+ {#if legendInteractive}
458
+ <button
459
+ type="button"
460
+ class="st-comboChart__legendButton"
461
+ aria-pressed={off}
462
+ onclick={() => onToggleSeries?.(item.label)}
463
+ >
464
+ <span
465
+ class="st-comboChart__legendSwatch st-comboChart__legendSwatch--{item.kind} st-comboChart__legendSwatch--{item.tone}"
466
+ ></span>
467
+ {item.label}
468
+ </button>
469
+ {:else}
470
+ <span
471
+ class="st-comboChart__legendSwatch st-comboChart__legendSwatch--{item.kind} st-comboChart__legendSwatch--{item.tone}"
472
+ ></span>
473
+ {item.label}
474
+ {/if}
440
475
  </li>
441
476
  {/each}
442
477
  </ul>
@@ -567,6 +602,28 @@
567
602
  gap: var(--st-spacing-1, 0.25rem);
568
603
  }
569
604
 
605
+ .st-comboChart__legendItem--off {
606
+ opacity: 0.45;
607
+ }
608
+
609
+ .st-comboChart__legendButton {
610
+ align-items: center;
611
+ background: none;
612
+ border: 0;
613
+ border-radius: var(--st-radius-sm, 0.25rem);
614
+ color: inherit;
615
+ cursor: pointer;
616
+ display: inline-flex;
617
+ font: inherit;
618
+ gap: var(--st-spacing-1, 0.25rem);
619
+ padding: 0.125rem 0.25rem;
620
+ }
621
+
622
+ .st-comboChart__legendButton:focus-visible {
623
+ outline: 2px solid var(--st-semantic-border-interactive);
624
+ outline-offset: 2px;
625
+ }
626
+
570
627
  .st-comboChart__legendSwatch {
571
628
  display: inline-block;
572
629
  flex: none;
@@ -17,6 +17,15 @@ type ComboChartProps = {
17
17
  leftAxisLabel?: string;
18
18
  rightAxisLabel?: string;
19
19
  legend?: boolean;
20
+ /**
21
+ * Interactive legend (FR-4). Ids/labels of bar/line series hidden from the
22
+ * render (controlled by the parent; default = all visible). Hidden series
23
+ * are omitted and their legend item is shown "off" (`aria-pressed`).
24
+ * Undefined → legacy non-interactive legend, unless `onToggleSeries` is set.
25
+ */
26
+ hiddenSeries?: string[];
27
+ /** Emitted on click / Enter / Space on a legend item. */
28
+ onToggleSeries?: (seriesId: string) => void;
20
29
  width?: number;
21
30
  height?: number;
22
31
  label: string;
@@ -1 +1 @@
1
- {"version":3,"file":"ComboChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/ComboChart.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,cAAc,GACtB,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAMF,KAAK,eAAe,GAAG;IACrB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAC7B,KAAK,CAAC,EAAE,oBAAoB,EAAE,CAAC;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA0VJ,QAAA,MAAM,UAAU,qDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"ComboChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/ComboChart.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,cAAc,GACtB,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAMF,KAAK,eAAe,GAAG;IACrB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAC7B,KAAK,CAAC,EAAE,oBAAoB,EAAE,CAAC;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,yDAAyD;IACzD,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA6WJ,QAAA,MAAM,UAAU,qDAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -60,6 +60,7 @@
60
60
  type ChartAnnotation
61
61
  } from "./chartAnnotations.js";
62
62
  import { formatDataLabel, normalizeDataLabels, type DataLabelsProp } from "./chartDataLabels.js";
63
+ import { keyForX, resolveActiveIndex } from "./chartCrosshair.js";
63
64
 
64
65
  type LineChartProps = {
65
66
  data: LineChartDatum[];
@@ -111,6 +112,20 @@
111
112
  * no legend surface, so this prop is accepted for parity and otherwise ignored.
112
113
  */
113
114
  showLegend?: boolean;
115
+ /**
116
+ * CONTROLLED synchronised hover key (FR-3). A datum's key is `String(x)`. When
117
+ * provided (string or null), the crosshair + tooltip track this key instead of
118
+ * the chart's internal pointer hover (null ⇒ nothing shown), letting a parent
119
+ * share one hover channel across several aligned charts. Absent (`undefined`)
120
+ * keeps the legacy uncontrolled behaviour.
121
+ */
122
+ hoverKey?: string | null;
123
+ /**
124
+ * Emitted when the user hovers a datum (its key) or leaves the plot (`null`).
125
+ * Always fired on pointer move/leave — even while CONTROLLED — so dataviz can
126
+ * keep the shared hover channel in sync.
127
+ */
128
+ onHoverKeyChange?: (key: string | null) => void;
114
129
  class?: string;
115
130
  };
116
131
 
@@ -132,6 +147,8 @@
132
147
  scale = "linear",
133
148
  invertAxis = false,
134
149
  showLegend,
150
+ hoverKey,
151
+ onHoverKeyChange,
135
152
  class: className
136
153
  }: LineChartProps = $props();
137
154
 
@@ -617,19 +634,33 @@
617
634
  return entries;
618
635
  });
619
636
 
637
+ // Stable key per datum (FR-3): `String(x)`. Resolves a controlled `hoverKey`
638
+ // to an index and feeds `onHoverKeyChange` from pointer events.
639
+ const hoverKeys = $derived(data.map((d) => keyForX(d.x)));
640
+ function emitHoverKey(index: number | null) {
641
+ onHoverKeyChange?.(index == null ? null : hoverKeys[index] ?? null);
642
+ }
620
643
  function handleLeave() {
621
644
  hoveredIndex = null;
645
+ emitHoverKey(null);
622
646
  }
623
647
  function handleVisualPointerMove(event: PointerEvent) {
624
648
  const target = event.target;
625
649
  if (!(target instanceof Element)) {
626
650
  hoveredIndex = null;
651
+ emitHoverKey(null);
627
652
  return;
628
653
  }
629
- const index = Number(target.getAttribute("data-chart-index"));
630
- hoveredIndex = Number.isInteger(index) ? index : null;
654
+ const raw = Number(target.getAttribute("data-chart-index"));
655
+ const index = Number.isInteger(raw) ? raw : null;
656
+ hoveredIndex = index;
657
+ emitHoverKey(index);
631
658
  }
632
659
 
660
+ // Index whose crosshair/tooltip is DISPLAYED: the controlled `hoverKey` when
661
+ // provided (resolved against `hoverKeys`), else the internal pointer index.
662
+ const activeIndex = $derived(resolveActiveIndex(hoverKey, hoveredIndex, hoverKeys));
663
+
633
664
  const classes = () =>
634
665
  ["st-lineChart", `st-lineChart--${tone}`, className].filter(Boolean).join(" ");
635
666
  </script>
@@ -820,13 +851,23 @@
820
851
  {/each}
821
852
  </g>
822
853
  {/if}
854
+
855
+ <!-- Crosshair (FR-3) — a tokenised vertical line + marker at the active key.
856
+ Decorative (aria-hidden); the value is in the tooltip + ChartDataList. -->
857
+ {#if activeIndex >= 0 && points[activeIndex]}
858
+ {@const cp = points[activeIndex]}
859
+ <g class="st-lineChart__crosshair" aria-hidden="true">
860
+ <line class="st-lineChart__crosshairLine" x1={cp.x} x2={cp.x} y1={MARGIN.top} y2={MARGIN.top + plotHeight} />
861
+ <circle class="st-lineChart__crosshairMarker" cx={cp.x} cy={cp.y} r="5" />
862
+ </g>
863
+ {/if}
823
864
  </svg>
824
865
  </div>
825
866
 
826
867
  <ChartDataList {label} items={dataValueItems} />
827
868
 
828
- {#if hoveredIndex !== null && points[hoveredIndex]}
829
- {@const p = points[hoveredIndex]}
869
+ {#if activeIndex >= 0 && points[activeIndex]}
870
+ {@const p = points[activeIndex]}
830
871
  <div
831
872
  class="st-lineChart__tooltip"
832
873
  role="presentation"
@@ -1028,4 +1069,19 @@
1028
1069
  font-size: 0.6875rem;
1029
1070
  font-weight: 600;
1030
1071
  }
1072
+
1073
+ /* --- Crosshair layer (FR-3) ----------------------------------------------
1074
+ A tokenised dashed vertical line at the active (hovered/controlled) key,
1075
+ plus an emphasised marker on the point. Decorative (aria-hidden). */
1076
+ .st-lineChart__crosshairLine {
1077
+ stroke: var(--st-semantic-border-strong);
1078
+ stroke-width: 1;
1079
+ stroke-dasharray: 3 3;
1080
+ opacity: 0.7;
1081
+ }
1082
+ .st-lineChart__crosshairMarker {
1083
+ fill: currentColor;
1084
+ stroke: var(--st-semantic-surface-default);
1085
+ stroke-width: 2;
1086
+ }
1031
1087
  </style>
@@ -86,6 +86,20 @@ type LineChartProps = {
86
86
  * no legend surface, so this prop is accepted for parity and otherwise ignored.
87
87
  */
88
88
  showLegend?: boolean;
89
+ /**
90
+ * CONTROLLED synchronised hover key (FR-3). A datum's key is `String(x)`. When
91
+ * provided (string or null), the crosshair + tooltip track this key instead of
92
+ * the chart's internal pointer hover (null ⇒ nothing shown), letting a parent
93
+ * share one hover channel across several aligned charts. Absent (`undefined`)
94
+ * keeps the legacy uncontrolled behaviour.
95
+ */
96
+ hoverKey?: string | null;
97
+ /**
98
+ * Emitted when the user hovers a datum (its key) or leaves the plot (`null`).
99
+ * Always fired on pointer move/leave — even while CONTROLLED — so dataviz can
100
+ * keep the shared hover channel in sync.
101
+ */
102
+ onHoverKeyChange?: (key: string | null) => void;
89
103
  class?: string;
90
104
  };
91
105
  declare const LineChart: import("svelte").Component<LineChartProps, {}, "">;
@@ -1 +1 @@
1
- {"version":3,"file":"LineChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/LineChart.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,aAAa,GACrB,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,MAAM,cAAc,GAAG;IAC3B,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACnB,CAAC,EAAE,MAAM,CAAC;IACV;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;AAEpF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,IAAI,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,sEAAsE;AACtE,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,KAAK,CAAC;AAI5C,OAAO,EAIH,KAAK,eAAe,EACrB,MAAM,uBAAuB,CAAC;AACjC,OAAO,EAAwC,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG/F,KAAK,cAAc,GAAG;IACpB,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,cAAc,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACtC,oDAAoD;IACpD,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC;IACpB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,qDAAqD;IACrD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B;;;;OAIG;IACH,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,iDAAiD;IACjD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAqpBJ,QAAA,MAAM,SAAS,oDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"LineChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/LineChart.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,aAAa,GACrB,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,MAAM,cAAc,GAAG;IAC3B,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACnB,CAAC,EAAE,MAAM,CAAC;IACV;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;AAEpF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,IAAI,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,sEAAsE;AACtE,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,KAAK,CAAC;AAI5C,OAAO,EAIH,KAAK,eAAe,EACrB,MAAM,uBAAuB,CAAC;AACjC,OAAO,EAAwC,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAI/F,KAAK,cAAc,GAAG;IACpB,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,cAAc,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACtC,oDAAoD;IACpD,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC;IACpB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,qDAAqD;IACrD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B;;;;OAIG;IACH,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,iDAAiD;IACjD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA+qBJ,QAAA,MAAM,SAAS,oDAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -33,6 +33,16 @@
33
33
  * values already live in the accessible ChartDataList.
34
34
  */
35
35
  dataLabels?: DataLabelsProp;
36
+ /**
37
+ * Interactive legend (FR-4). Ids/labels of series hidden from the render
38
+ * (controlled by the parent; default = all visible). Each segment whose
39
+ * `label` ∈ `hiddenSeries` is omitted and its legend item is shown "off"
40
+ * (`aria-pressed`). Undefined → legacy non-interactive legend, unless
41
+ * `onToggleSeries` is provided.
42
+ */
43
+ hiddenSeries?: string[];
44
+ /** Emitted on click / Enter / Space on a legend item. */
45
+ onToggleSeries?: (seriesId: string) => void;
36
46
  class?: string;
37
47
  };
38
48
 
@@ -43,9 +53,15 @@
43
53
  label,
44
54
  showLegend = true,
45
55
  dataLabels,
56
+ hiddenSeries,
57
+ onToggleSeries,
46
58
  class: className
47
59
  }: StackedBarChartProps = $props();
48
60
 
61
+ // Interactive legend is active as soon as the parent wires either prop.
62
+ const legendInteractive = $derived(onToggleSeries !== undefined || hiddenSeries !== undefined);
63
+ const hiddenSet = $derived(new Set(hiddenSeries ?? []));
64
+
49
65
  const MARGIN = { top: 14, right: 16, bottom: 34, left: 44 };
50
66
  const TONES = ["category1","category2","category3","category4","category5","category6","category7","category8"] as const;
51
67
 
@@ -81,7 +97,9 @@
81
97
  let hovered: { bar: number; seg: number } | null = $state(null);
82
98
 
83
99
  const scales = $derived.by(() => {
84
- const totals = data.map((b) => b.segments.reduce((s, x) => s + Math.max(x.value, 0), 0));
100
+ const totals = data.map((b) =>
101
+ b.segments.reduce((s, x) => (hiddenSet.has(x.label) ? s : s + Math.max(x.value, 0)), 0)
102
+ );
85
103
  const ticks = niceTicks(0, Math.max(0, ...totals));
86
104
  return {
87
105
  ticks, domainMax: ticks[ticks.length - 1],
@@ -98,23 +116,32 @@
98
116
  return data.map((bar, bi) => {
99
117
  const x = MARGIN.left + band * bi + (band - barWidth) / 2;
100
118
  let acc = 0;
101
- const segs = bar.segments.map((seg, si) => {
102
- const v = Math.max(seg.value, 0);
103
- const yTop = MARGIN.top + scaleLinear(acc + v, 0, domainMax, plotH, 0);
104
- const yBottom = MARGIN.top + scaleLinear(acc, 0, domainMax, plotH, 0);
105
- acc += v;
106
- return {
107
- x, y: yTop, width: barWidth, height: Math.max(yBottom - yTop, 0),
108
- seg, tone: seg.tone ?? TONES[si % TONES.length],
109
- cx: x + barWidth / 2, cy: yTop + (yBottom - yTop) / 2
110
- };
111
- });
119
+ // Tone is bound to the original segment index so it stays stable when a
120
+ // series is toggled off; hidden segments are dropped before stacking.
121
+ const segs = bar.segments
122
+ .map((seg, si) => ({ seg, tone: seg.tone ?? TONES[si % TONES.length] }))
123
+ .filter(({ seg }) => !hiddenSet.has(seg.label))
124
+ .map(({ seg, tone }) => {
125
+ const v = Math.max(seg.value, 0);
126
+ const yTop = MARGIN.top + scaleLinear(acc + v, 0, domainMax, plotH, 0);
127
+ const yBottom = MARGIN.top + scaleLinear(acc, 0, domainMax, plotH, 0);
128
+ acc += v;
129
+ return {
130
+ x, y: yTop, width: barWidth, height: Math.max(yBottom - yTop, 0),
131
+ seg, tone,
132
+ cx: x + barWidth / 2, cy: yTop + (yBottom - yTop) / 2
133
+ };
134
+ });
112
135
  return { x, band, label: bar.label, segs, cxLabel: MARGIN.left + band * (bi + 0.5) };
113
136
  });
114
137
  });
115
138
 
116
139
  const dataValueItems = $derived(
117
- data.flatMap((bar) => bar.segments.map((seg) => `${bar.label}, ${seg.label}: ${seg.value}`))
140
+ data.flatMap((bar) =>
141
+ bar.segments
142
+ .filter((seg) => !hiddenSet.has(seg.label))
143
+ .map((seg) => `${bar.label}, ${seg.label}: ${seg.value}`)
144
+ )
118
145
  );
119
146
 
120
147
  // --- Data labels ----------------------------------------------------------
@@ -206,9 +233,22 @@
206
233
  {#if showLegend && legend.length > 0}
207
234
  <ul class="st-stackedBar__legend">
208
235
  {#each legend as item (item.seriesLabel)}
209
- <li class="st-stackedBar__legendItem">
210
- <span class="st-stackedBar__legendSwatch st-stackedBar__legendSwatch--{item.tone}" aria-hidden="true"></span>
211
- {item.seriesLabel}
236
+ {@const off = hiddenSet.has(item.seriesLabel)}
237
+ <li class="st-stackedBar__legendItem" class:st-stackedBar__legendItem--off={legendInteractive && off}>
238
+ {#if legendInteractive}
239
+ <button
240
+ type="button"
241
+ class="st-stackedBar__legendButton"
242
+ aria-pressed={off}
243
+ onclick={() => onToggleSeries?.(item.seriesLabel)}
244
+ >
245
+ <span class="st-stackedBar__legendSwatch st-stackedBar__legendSwatch--{item.tone}" aria-hidden="true"></span>
246
+ {item.seriesLabel}
247
+ </button>
248
+ {:else}
249
+ <span class="st-stackedBar__legendSwatch st-stackedBar__legendSwatch--{item.tone}" aria-hidden="true"></span>
250
+ {item.seriesLabel}
251
+ {/if}
212
252
  </li>
213
253
  {/each}
214
254
  </ul>
@@ -243,6 +283,12 @@
243
283
  .st-stackedBar__tooltipValue { opacity: 0.85; }
244
284
  .st-stackedBar__legend { display: flex; flex-wrap: wrap; gap: 0.75rem; list-style: none; margin: 0.5rem 0 0; padding: 0; }
245
285
  .st-stackedBar__legendItem { align-items: center; color: var(--st-semantic-text-secondary); display: inline-flex; font-size: 0.75rem; gap: 0.35rem; }
286
+ .st-stackedBar__legendItem--off { opacity: 0.45; }
287
+ .st-stackedBar__legendButton {
288
+ align-items: center; background: none; border: 0; border-radius: var(--st-radius-sm, 0.25rem);
289
+ color: inherit; cursor: pointer; display: inline-flex; font: inherit; gap: 0.35rem; padding: 0.125rem 0.25rem;
290
+ }
291
+ .st-stackedBar__legendButton:focus-visible { outline: 2px solid var(--st-semantic-border-interactive); outline-offset: 2px; }
246
292
  .st-stackedBar__legendSwatch { border-radius: 2px; height: 0.7rem; width: 0.7rem; }
247
293
  .st-stackedBar__legendSwatch--category1 { background: var(--st-semantic-data-category1); }
248
294
  .st-stackedBar__legendSwatch--category2 { background: var(--st-semantic-data-category2); }
@@ -23,6 +23,16 @@ type StackedBarChartProps = {
23
23
  * values already live in the accessible ChartDataList.
24
24
  */
25
25
  dataLabels?: DataLabelsProp;
26
+ /**
27
+ * Interactive legend (FR-4). Ids/labels of series hidden from the render
28
+ * (controlled by the parent; default = all visible). Each segment whose
29
+ * `label` ∈ `hiddenSeries` is omitted and its legend item is shown "off"
30
+ * (`aria-pressed`). Undefined → legacy non-interactive legend, unless
31
+ * `onToggleSeries` is provided.
32
+ */
33
+ hiddenSeries?: string[];
34
+ /** Emitted on click / Enter / Space on a legend item. */
35
+ onToggleSeries?: (seriesId: string) => void;
26
36
  class?: string;
27
37
  };
28
38
  declare const StackedBarChart: import("svelte").Component<StackedBarChartProps, {}, "">;
@@ -1 +1 @@
1
- {"version":3,"file":"StackedBarChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/StackedBarChart.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,cAAc,GACtB,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,GACrD,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;AAE1D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B,CAAC;AAIJ,OAAO,EAAwC,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG/F,KAAK,oBAAoB,GAAG;IAC1B,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAmLJ,QAAA,MAAM,eAAe,0DAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"StackedBarChart.svelte.d.ts","sourceRoot":"","sources":["../src/lib/StackedBarChart.svelte.ts"],"names":[],"mappings":"AAGE,MAAM,MAAM,cAAc,GACtB,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,GACrD,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;AAE1D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B,CAAC;AAIJ,OAAO,EAAwC,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG/F,KAAK,oBAAoB,GAAG;IAC1B,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,yDAAyD;IACzD,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AA4MJ,QAAA,MAAM,eAAe,0DAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
@@ -0,0 +1,19 @@
1
+ /** Serialises a Line/Area datum's `x` to its stable hover key. */
2
+ export declare function keyForX(x: number | string): string;
3
+ /**
4
+ * Resolves the hover key to a datum index within `keys` (the ordered list of
5
+ * every datum's key). Returns -1 when the key is null/undefined or unmatched.
6
+ */
7
+ export declare function indexForHoverKey(hoverKey: string | null | undefined, keys: string[]): number;
8
+ /**
9
+ * Picks the datum index to DISPLAY the crosshair/tooltip at.
10
+ * - Controlled (`hoverKey !== undefined`): the index of `hoverKey` in `keys`
11
+ * (or -1 when null/unmatched). The internal pointer index is ignored for
12
+ * display.
13
+ * - Uncontrolled (`hoverKey === undefined`): the internal pointer index.
14
+ * Returns -1 when nothing should be shown.
15
+ */
16
+ export declare function resolveActiveIndex(hoverKey: string | null | undefined, internalIndex: number | null, keys: string[]): number;
17
+ /** True when the chart is CONTROLLED (the parent supplied `hoverKey`). */
18
+ export declare function isControlled(hoverKey: string | null | undefined): boolean;
19
+ //# sourceMappingURL=chartCrosshair.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chartCrosshair.d.ts","sourceRoot":"","sources":["../src/lib/chartCrosshair.ts"],"names":[],"mappings":"AAsBA,kEAAkE;AAClE,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAElD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAG5F;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACnC,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,IAAI,EAAE,MAAM,EAAE,GACb,MAAM,CAGR;AAED,0EAA0E;AAC1E,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAEzE"}
@@ -0,0 +1,51 @@
1
+ // --- Chart crosshair / synchronised hover layer (shared, framework-agnostic) -
2
+ //
3
+ // FR-3: a CONTROLLED crosshair + tooltip whose position is driven by a `hoverKey`
4
+ // string the parent owns, so dataviz can share one hover channel across several
5
+ // aligned panels (a "linked" tooltip). The DS stays presentational: it resolves
6
+ // the key to a datum index, draws a tokenised vertical line at that x (plus a
7
+ // marker on the point for Line/Area), and reuses the existing tooltip surface.
8
+ //
9
+ // The "key" is the stable identifier of a datum on the categorical/x axis:
10
+ // - Bar: the bar's `label`.
11
+ // - Line/Area: the point's `x`, serialised with `String(x)`.
12
+ //
13
+ // Behaviour:
14
+ // - `hoverKey === undefined` → UNCONTROLLED (internal hover, unchanged, fully
15
+ // backward compatible).
16
+ // - `hoverKey` provided (string or null) → CONTROLLED: the displayed
17
+ // crosshair/tooltip tracks `hoverKey` (null = nothing shown), the chart's own
18
+ // pointer hover no longer drives the DISPLAY, but `onHoverKeyChange` is still
19
+ // emitted so the parent can keep the shared channel in sync.
20
+ //
21
+ // Purely additive: a chart that passes neither prop renders exactly as before.
22
+ /** Serialises a Line/Area datum's `x` to its stable hover key. */
23
+ export function keyForX(x) {
24
+ return String(x);
25
+ }
26
+ /**
27
+ * Resolves the hover key to a datum index within `keys` (the ordered list of
28
+ * every datum's key). Returns -1 when the key is null/undefined or unmatched.
29
+ */
30
+ export function indexForHoverKey(hoverKey, keys) {
31
+ if (hoverKey == null)
32
+ return -1;
33
+ return keys.indexOf(hoverKey);
34
+ }
35
+ /**
36
+ * Picks the datum index to DISPLAY the crosshair/tooltip at.
37
+ * - Controlled (`hoverKey !== undefined`): the index of `hoverKey` in `keys`
38
+ * (or -1 when null/unmatched). The internal pointer index is ignored for
39
+ * display.
40
+ * - Uncontrolled (`hoverKey === undefined`): the internal pointer index.
41
+ * Returns -1 when nothing should be shown.
42
+ */
43
+ export function resolveActiveIndex(hoverKey, internalIndex, keys) {
44
+ if (hoverKey !== undefined)
45
+ return indexForHoverKey(hoverKey, keys);
46
+ return internalIndex == null ? -1 : internalIndex;
47
+ }
48
+ /** True when the chart is CONTROLLED (the parent supplied `hoverKey`). */
49
+ export function isControlled(hoverKey) {
50
+ return hoverKey !== undefined;
51
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentropic/design-system-svelte",
3
- "version": "0.34.24",
3
+ "version": "0.34.26",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"