@ruckc/truss-chart 1.5.0 → 1.6.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.
@@ -35,7 +35,7 @@
35
35
  type Tip = { x: number; y: number; cat: string; name: string; value: number; color: string; share?: number };
36
36
  let tip = $state<Tip | null>(null);
37
37
 
38
- const all = $derived(series.slice(0, MAX_SERIES).map((s, i) => ({ ...s, i, color: seriesColor(i) })));
38
+ const all = $derived(series.slice(0, MAX_SERIES).map((s, i) => ({ ...s, i, color: s.color ?? seriesColor(i) })));
39
39
  const vis = $derived(all.filter((s) => !hidden.includes(s.name)));
40
40
  const items = $derived(all.map((s) => ({ name: s.name, color: s.color, hidden: hidden.includes(s.name) })));
41
41
  const toggle = (n: string) => (hidden = hidden.includes(n) ? hidden.filter((h) => h !== n) : [...hidden, n]);
@@ -52,6 +52,8 @@
52
52
  });
53
53
  });
54
54
  const focus = $derived(vis.find((s) => s.name === active));
55
+ /** Share of the visible total; 0 (not NaN) when everything visible is 0. */
56
+ const pct = (v: number) => (total ? Math.round((v / total) * 100) : 0);
55
57
  </script>
56
58
 
57
59
  <ChartFrame {title} {subtitle} {items} ontoggle={toggle}>
@@ -62,14 +64,14 @@
62
64
  <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
63
65
  <path
64
66
  role="img" d={a.d} fill={a.color} fill-rule="evenodd" class="slice" data-dim={active != null && active !== a.name} tabindex="0"
65
- aria-label="{a.name}: {format(a.value)} ({Math.round((a.value / total) * 100)}%)"
67
+ aria-label="{a.name}: {format(a.value)} ({pct(a.value)}%)"
66
68
  onpointerenter={() => (active = a.name)} onpointerleave={() => (active = null)}
67
69
  onfocus={() => (active = a.name)} onblur={() => (active = null)}
68
70
  />
69
71
  {/each}
70
72
  <text class="big" x={size / 2} y={size / 2 - 2} text-anchor="middle">{format(focus?.value ?? total)}</text>
71
73
  <text class="small" x={size / 2} y={size / 2 + 16} text-anchor="middle">
72
- {focus ? `${focus.name} · ${Math.round((focus.value / total) * 100)}%` : centerLabel}
74
+ {focus ? `${focus.name} · ${pct(focus.value)}%` : centerLabel}
73
75
  </text>
74
76
  </svg>
75
77
  </div>
@@ -50,7 +50,6 @@
50
50
  const yScale = $derived(niceTicks(0, Math.max(...counts, 1), 5));
51
51
  const total = $derived(counts.reduce((a, b) => a + b, 0));
52
52
 
53
- const inRange = (i: number) => !range || (edges[i + 1] > range[0] && edges[i] < range[1]);
54
53
  const sel = $derived(drag ? ([Math.min(drag.a, drag.b), Math.max(drag.a, drag.b)] as [number, number]) : range);
55
54
 
56
55
  function set(r: [number, number] | null) {
@@ -35,7 +35,7 @@
35
35
  let hidden = $state<string[]>([]);
36
36
  let hover = $state<number | null>(null);
37
37
 
38
- const all = $derived(series.slice(0, MAX_SERIES).map((s, i) => ({ ...s, i, color: seriesColor(i) })));
38
+ const all = $derived(series.slice(0, MAX_SERIES).map((s, i) => ({ ...s, i, color: s.color ?? seriesColor(i) })));
39
39
  const vis = $derived(all.filter((s) => !hidden.includes(s.name)));
40
40
  const isDate = $derived(x[0] instanceof Date);
41
41
  const xs = $derived(x.map((v) => +v));
@@ -13,9 +13,12 @@
13
13
  const X = $derived(linear(0, Math.max(values.length - 1, 1), 3, width - 3));
14
14
  const Y = $derived(linear(Math.min(...pts.map((p) => p.v)), Math.max(...pts.map((p) => p.v)), height - 4, 4));
15
15
  const last = $derived(pts.at(-1));
16
+ // built in script: template-string interpolation in markup would add unreachable `?? ''` branches
17
+ // a `null` label is printed as nothing (the caller opted out), which is a real branch here
18
+ const ariaLabel = $derived(`${label ?? ''}: ${pts.map((p) => p.v).join(', ')}`);
16
19
  </script>
17
20
 
18
- <svg {width} {height} role="img" aria-label="{label}: {pts.map((p) => p.v).join(', ')}">
21
+ <svg {width} {height} role="img" aria-label={ariaLabel}>
19
22
  <path d={pts.map((p, k) => `${k ? 'L' : 'M'}${X(p.i).toFixed(1)},${Y(p.v).toFixed(1)}`).join(' ')} fill="none" stroke={color} stroke-width="2" stroke-linejoin="round" stroke-linecap="round" />
20
23
  {#if last}<circle cx={X(last.i)} cy={Y(last.v)} r="3.5" fill={color} stroke="var(--tr-surface-container-low)" stroke-width="2" />{/if}
21
24
  </svg>
@@ -3,9 +3,11 @@
3
3
 
4
4
  /** Positioned inside a `position: relative` host. Values lead, names follow, line keys not boxes. */
5
5
  let { x, y, width, children }: { x: number; y: number; width: number; children: Snippet } = $props();
6
+ // built in script: template-string interpolation in markup would add unreachable `?? ''` branches
7
+ const pos = $derived(`left: ${x}px; top: ${y}px`);
6
8
  </script>
7
9
 
8
- <div class="tip" role="status" style:left="{x}px" style:top="{y}px" data-flip={x > width * 0.6}>{@render children()}</div>
10
+ <div class="tip" role="status" style={pos} data-flip={x > width * 0.6}>{@render children()}</div>
9
11
 
10
12
  <style>
11
13
  .tip { position: absolute; z-index: 5; transform: translate(12px, -50%); min-width: 120px; padding: 6px 10px; pointer-events: none; background: var(--tr-surface-container-highest); border: 1px solid var(--tr-outline-variant); border-radius: var(--tr-radius-md); box-shadow: var(--tr-elevation-2); font-size: var(--tr-text-sm); white-space: nowrap; }
package/dist/types.d.ts CHANGED
@@ -1,6 +1,12 @@
1
1
  export type Series = {
2
2
  name: string;
3
3
  values: (number | null)[];
4
+ /**
5
+ * Overrides the series' palette slot. Any CSS colour or `var(--…)`, used for its line, area, bars,
6
+ * legend swatch and tooltip swatch. Custom colours bypass the validated colour-blind-safe palette,
7
+ * so you are responsible for contrast and for keeping series distinguishable from each other.
8
+ */
9
+ color?: string;
4
10
  };
5
11
  export type LegendItem = {
6
12
  name: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ruckc/truss-chart",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Zero-dependency SVG charts for Truss UI",
5
5
  "type": "module",
6
6
  "license": "MIT",