@sentropic/design-system-svelte 0.16.0 → 0.17.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,620 @@
1
+ <script lang="ts" module>
2
+ export type ComboChartTone =
3
+ | "category1"
4
+ | "category2"
5
+ | "category3"
6
+ | "category4"
7
+ | "category5"
8
+ | "category6"
9
+ | "category7"
10
+ | "category8";
11
+
12
+ export type ComboChartBarSeries = {
13
+ label: string;
14
+ data: number[];
15
+ tone?: ComboChartTone;
16
+ };
17
+
18
+ export type ComboChartLineSeries = {
19
+ label: string;
20
+ data: number[];
21
+ tone?: ComboChartTone;
22
+ smooth?: boolean;
23
+ };
24
+ </script>
25
+
26
+ <script lang="ts">
27
+ import ChartDataList from "./ChartDataList.svelte";
28
+
29
+ type ComboChartProps = {
30
+ categories: string[];
31
+ bars?: ComboChartBarSeries[];
32
+ lines?: ComboChartLineSeries[];
33
+ leftAxisLabel?: string;
34
+ rightAxisLabel?: string;
35
+ legend?: boolean;
36
+ width?: number;
37
+ height?: number;
38
+ label: string;
39
+ class?: string;
40
+ };
41
+
42
+ let {
43
+ categories,
44
+ bars = [],
45
+ lines = [],
46
+ leftAxisLabel,
47
+ rightAxisLabel,
48
+ legend = true,
49
+ width = 480,
50
+ height = 240,
51
+ label,
52
+ class: className
53
+ }: ComboChartProps = $props();
54
+
55
+ const MARGIN = { top: 12, right: 52, bottom: 32, left: 52 };
56
+
57
+ function niceTicks(min: number, max: number, target = 5): number[] {
58
+ if (!Number.isFinite(min) || !Number.isFinite(max) || min === max) {
59
+ const base = Number.isFinite(max) ? max : 0;
60
+ return [base];
61
+ }
62
+ const range = max - min;
63
+ const rough = range / Math.max(target - 1, 1);
64
+ const pow = Math.pow(10, Math.floor(Math.log10(rough)));
65
+ const norm = rough / pow;
66
+ let step: number;
67
+ if (norm < 1.5) step = 1 * pow;
68
+ else if (norm < 3) step = 2 * pow;
69
+ else if (norm < 7) step = 5 * pow;
70
+ else step = 10 * pow;
71
+ const start = Math.floor(min / step) * step;
72
+ const end = Math.ceil(max / step) * step;
73
+ const ticks: number[] = [];
74
+ for (let v = start; v <= end + step / 2; v += step) {
75
+ ticks.push(Number(v.toFixed(10)));
76
+ }
77
+ return ticks;
78
+ }
79
+
80
+ function scaleLinear(v: number, d0: number, d1: number, r0: number, r1: number) {
81
+ if (d1 === d0) return r0;
82
+ return r0 + ((v - d0) * (r1 - r0)) / (d1 - d0);
83
+ }
84
+
85
+ function formatTick(v: number): string {
86
+ if (Math.abs(v) >= 1000) return `${(v / 1000).toFixed(v % 1000 === 0 ? 0 : 1)}k`;
87
+ if (Number.isInteger(v)) return String(v);
88
+ return v.toFixed(1);
89
+ }
90
+
91
+ const plotWidth = $derived(Math.max(width - MARGIN.left - MARGIN.right, 1));
92
+ const plotHeight = $derived(Math.max(height - MARGIN.top - MARGIN.bottom, 1));
93
+
94
+ // Left axis (bars): include zero in the domain so bars rest on a baseline.
95
+ const leftScale = $derived.by(() => {
96
+ const values = bars.flatMap((s) => s.data);
97
+ const minRaw = Math.min(0, ...(values.length ? values : [0]));
98
+ const maxRaw = Math.max(0, ...(values.length ? values : [0]));
99
+ const ticks = niceTicks(minRaw, maxRaw, 5);
100
+ return { ticks, domainMin: ticks[0], domainMax: ticks[ticks.length - 1] };
101
+ });
102
+
103
+ // Right axis (lines): padded domain like LineChart.
104
+ const rightScale = $derived.by(() => {
105
+ const values = lines.flatMap((s) => s.data);
106
+ if (values.length === 0) {
107
+ const ticks = niceTicks(0, 1, 5);
108
+ return { ticks, domainMin: ticks[0], domainMax: ticks[ticks.length - 1] };
109
+ }
110
+ const minRaw = Math.min(...values);
111
+ const maxRaw = Math.max(...values);
112
+ const padded = (maxRaw - minRaw) * 0.08 || Math.max(Math.abs(maxRaw), 1) * 0.1;
113
+ const ticks = niceTicks(minRaw - padded, maxRaw + padded, 5);
114
+ return { ticks, domainMin: ticks[0], domainMax: ticks[ticks.length - 1] };
115
+ });
116
+
117
+ // Categories are ordinal: each gets a band centred at the band midpoint.
118
+ function bandCenter(i: number): number {
119
+ const band = plotWidth / Math.max(categories.length, 1);
120
+ return MARGIN.left + band * (i + 0.5);
121
+ }
122
+
123
+ // A point is missing when its category index is absent or non-finite. Missing
124
+ // bar values render NO bar (not a zero-height baseline artefact); missing line
125
+ // values break the line into gaps. Values are never silently coerced to 0.
126
+ const isPresent = (v: number | undefined): v is number => typeof v === "number" && Number.isFinite(v);
127
+
128
+ const barGroups = $derived.by(() => {
129
+ if (categories.length === 0 || bars.length === 0) return [];
130
+ const { domainMin, domainMax } = leftScale;
131
+ const band = plotWidth / categories.length;
132
+ const groupWidth = band * 0.62;
133
+ const barWidth = groupWidth / bars.length;
134
+ const zeroY = scaleLinear(0, domainMin, domainMax, plotHeight, 0);
135
+ return categories.map((_, ci) => {
136
+ const groupX = MARGIN.left + band * ci + (band - groupWidth) / 2;
137
+ const segments = bars
138
+ .map((series, si) => {
139
+ const raw = series.data[ci];
140
+ if (!isPresent(raw)) return null;
141
+ const value = raw;
142
+ const valueY = scaleLinear(value, domainMin, domainMax, plotHeight, 0);
143
+ const y = Math.min(valueY, zeroY);
144
+ const h = Math.abs(zeroY - valueY);
145
+ return {
146
+ x: groupX + barWidth * si,
147
+ y: MARGIN.top + y,
148
+ width: barWidth,
149
+ height: Math.max(h, 0.5),
150
+ cx: groupX + barWidth * (si + 0.5),
151
+ cy: MARGIN.top + valueY,
152
+ value,
153
+ seriesLabel: series.label,
154
+ category: categories[ci],
155
+ si,
156
+ tone: series.tone ?? `category${(si % 8) + 1}`
157
+ };
158
+ })
159
+ .filter((seg): seg is NonNullable<typeof seg> => seg !== null);
160
+ return segments;
161
+ });
162
+ });
163
+
164
+ const flatBars = $derived(barGroups.flat());
165
+
166
+ function buildLinearPath(pts: { x: number; y: number }[]): string {
167
+ return pts.map((p, i) => `${i === 0 ? "M" : "L"}${p.x.toFixed(2)},${p.y.toFixed(2)}`).join(" ");
168
+ }
169
+
170
+ function buildSmoothPath(pts: { x: number; y: number }[]): string {
171
+ if (pts.length < 2) return buildLinearPath(pts);
172
+ const t = 0.18;
173
+ let d = `M${pts[0].x.toFixed(2)},${pts[0].y.toFixed(2)}`;
174
+ for (let i = 0; i < pts.length - 1; i++) {
175
+ const p0 = pts[i - 1] ?? pts[i];
176
+ const p1 = pts[i];
177
+ const p2 = pts[i + 1];
178
+ const p3 = pts[i + 2] ?? p2;
179
+ const c1x = p1.x + (p2.x - p0.x) * t;
180
+ const c1y = p1.y + (p2.y - p0.y) * t;
181
+ const c2x = p2.x - (p3.x - p1.x) * t;
182
+ const c2y = p2.y - (p3.y - p1.y) * t;
183
+ d += ` C${c1x.toFixed(2)},${c1y.toFixed(2)} ${c2x.toFixed(2)},${c2y.toFixed(2)} ${p2.x.toFixed(2)},${p2.y.toFixed(2)}`;
184
+ }
185
+ return d;
186
+ }
187
+
188
+ const lineSeries = $derived.by(() => {
189
+ if (categories.length === 0 || lines.length === 0) return [];
190
+ const { domainMin, domainMax } = rightScale;
191
+ return lines.map((series, li) => {
192
+ const points = categories.map((_, ci) => {
193
+ const value = series.data[ci] ?? 0;
194
+ return {
195
+ x: bandCenter(ci),
196
+ y: MARGIN.top + scaleLinear(value, domainMin, domainMax, plotHeight, 0),
197
+ value,
198
+ category: categories[ci]
199
+ };
200
+ });
201
+ const path = series.smooth ? buildSmoothPath(points) : buildLinearPath(points);
202
+ return {
203
+ path,
204
+ points,
205
+ seriesLabel: series.label,
206
+ tone: series.tone ?? `category${((bars.length + li) % 8) + 1}`
207
+ };
208
+ });
209
+ });
210
+
211
+ const leftGridLines = $derived(
212
+ leftScale.ticks.map((tick) => ({
213
+ value: tick,
214
+ y: MARGIN.top + scaleLinear(tick, leftScale.domainMin, leftScale.domainMax, plotHeight, 0)
215
+ }))
216
+ );
217
+
218
+ const rightTickEntries = $derived(
219
+ lines.length === 0
220
+ ? []
221
+ : rightScale.ticks.map((tick) => ({
222
+ value: tick,
223
+ y: MARGIN.top + scaleLinear(tick, rightScale.domainMin, rightScale.domainMax, plotHeight, 0)
224
+ }))
225
+ );
226
+
227
+ const legendItems = $derived([
228
+ ...bars.map((s, i) => ({
229
+ key: `bar-${i}`,
230
+ label: s.label,
231
+ tone: s.tone ?? `category${(i % 8) + 1}`,
232
+ kind: "bar" as const
233
+ })),
234
+ ...lines.map((s, i) => ({
235
+ key: `line-${i}`,
236
+ label: s.label,
237
+ tone: s.tone ?? `category${((bars.length + i) % 8) + 1}`,
238
+ kind: "line" as const
239
+ }))
240
+ ]);
241
+
242
+ 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
+ )
249
+ ]);
250
+
251
+ type Hover =
252
+ | { kind: "bar"; gi: number; si: number }
253
+ | { kind: "line"; li: number; pi: number }
254
+ | null;
255
+ let hovered: Hover = $state(null);
256
+
257
+ function handleLeave() {
258
+ hovered = null;
259
+ }
260
+ function handleVisualPointerMove(event: PointerEvent) {
261
+ const target = event.target;
262
+ if (!(target instanceof Element)) {
263
+ hovered = null;
264
+ return;
265
+ }
266
+ const kind = target.getAttribute("data-chart-kind");
267
+ const a = Number(target.getAttribute("data-chart-a"));
268
+ const b = Number(target.getAttribute("data-chart-b"));
269
+ if (kind === "bar" && Number.isInteger(a) && Number.isInteger(b)) {
270
+ hovered = { kind: "bar", gi: a, si: b };
271
+ } else if (kind === "line" && Number.isInteger(a) && Number.isInteger(b)) {
272
+ hovered = { kind: "line", li: a, pi: b };
273
+ } else {
274
+ hovered = null;
275
+ }
276
+ }
277
+
278
+ const tooltip = $derived.by(() => {
279
+ if (!hovered) return null;
280
+ if (hovered.kind === "bar") {
281
+ const seg = barGroups[hovered.gi]?.[hovered.si];
282
+ if (!seg) return null;
283
+ return { cx: seg.cx, cy: seg.cy, label: `${seg.seriesLabel} · ${seg.category}`, value: seg.value };
284
+ }
285
+ const series = lineSeries[hovered.li];
286
+ const p = series?.points[hovered.pi];
287
+ if (!series || !p) return null;
288
+ return { cx: p.x, cy: p.y, label: `${series.seriesLabel} · ${p.category}`, value: p.value };
289
+ });
290
+
291
+ const classes = () => ["st-comboChart", className].filter(Boolean).join(" ");
292
+ </script>
293
+
294
+ <div class={classes()}>
295
+ <div
296
+ class="st-comboChart__visual"
297
+ role="img"
298
+ aria-label={label}
299
+ onpointermove={handleVisualPointerMove}
300
+ onpointerleave={handleLeave}
301
+ >
302
+ <svg
303
+ viewBox="0 0 {width} {height}"
304
+ preserveAspectRatio="xMidYMid meet"
305
+ width="100%"
306
+ height="100%"
307
+ focusable="false"
308
+ aria-hidden="true"
309
+ >
310
+ <!-- gridlines + left (bar) axis ticks -->
311
+ {#each leftGridLines as g (g.value)}
312
+ <line class="st-comboChart__grid" x1={MARGIN.left} x2={MARGIN.left + plotWidth} y1={g.y} y2={g.y} />
313
+ <text
314
+ class="st-comboChart__tickLabel"
315
+ x={MARGIN.left - 6}
316
+ y={g.y}
317
+ text-anchor="end"
318
+ dominant-baseline="middle"
319
+ >
320
+ {formatTick(g.value)}
321
+ </text>
322
+ {/each}
323
+
324
+ <!-- right (line) axis ticks -->
325
+ {#each rightTickEntries as g (g.value)}
326
+ <text
327
+ class="st-comboChart__tickLabel"
328
+ x={MARGIN.left + plotWidth + 6}
329
+ y={g.y}
330
+ text-anchor="start"
331
+ dominant-baseline="middle"
332
+ >
333
+ {formatTick(g.value)}
334
+ </text>
335
+ {/each}
336
+
337
+ <!-- axes -->
338
+ <line class="st-comboChart__axis" x1={MARGIN.left} x2={MARGIN.left} y1={MARGIN.top} y2={height - MARGIN.bottom} />
339
+ <line
340
+ class="st-comboChart__axis"
341
+ x1={MARGIN.left}
342
+ x2={width - MARGIN.right}
343
+ y1={height - MARGIN.bottom}
344
+ y2={height - MARGIN.bottom}
345
+ />
346
+ {#if lines.length > 0}
347
+ <line
348
+ class="st-comboChart__axis"
349
+ x1={MARGIN.left + plotWidth}
350
+ x2={MARGIN.left + plotWidth}
351
+ y1={MARGIN.top}
352
+ y2={height - MARGIN.bottom}
353
+ />
354
+ {/if}
355
+
356
+ <!-- axis labels -->
357
+ {#if leftAxisLabel}
358
+ <text
359
+ class="st-comboChart__axisLabel"
360
+ text-anchor="middle"
361
+ transform="translate({MARGIN.left - 40}, {MARGIN.top + plotHeight / 2}) rotate(-90)"
362
+ >
363
+ {leftAxisLabel}
364
+ </text>
365
+ {/if}
366
+ {#if rightAxisLabel}
367
+ <text
368
+ class="st-comboChart__axisLabel"
369
+ text-anchor="middle"
370
+ transform="translate({MARGIN.left + plotWidth + 40}, {MARGIN.top + plotHeight / 2}) rotate(90)"
371
+ >
372
+ {rightAxisLabel}
373
+ </text>
374
+ {/if}
375
+
376
+ <!-- category labels -->
377
+ {#each categories as category, ci (ci)}
378
+ <text
379
+ class="st-comboChart__categoryLabel"
380
+ x={bandCenter(ci)}
381
+ y={height - MARGIN.bottom + 16}
382
+ text-anchor="middle"
383
+ >
384
+ {category}
385
+ </text>
386
+ {/each}
387
+
388
+ <!-- bars -->
389
+ {#each barGroups as group, gi (gi)}
390
+ {#each group as seg, si (si)}
391
+ <rect
392
+ class="st-comboChart__bar st-comboChart__bar--{seg.tone}"
393
+ x={seg.x}
394
+ y={seg.y}
395
+ width={seg.width}
396
+ height={seg.height}
397
+ rx="2"
398
+ data-chart-kind="bar"
399
+ data-chart-a={gi}
400
+ data-chart-b={si}
401
+ />
402
+ {/each}
403
+ {/each}
404
+
405
+ <!-- lines -->
406
+ {#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}
424
+ />
425
+ {/each}
426
+ {/each}
427
+ </svg>
428
+ </div>
429
+
430
+ <ChartDataList {label} items={dataValueItems} />
431
+
432
+ {#if legend && legendItems.length > 0}
433
+ <ul class="st-comboChart__legend" aria-hidden="true">
434
+ {#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}
440
+ </li>
441
+ {/each}
442
+ </ul>
443
+ {/if}
444
+
445
+ {#if tooltip}
446
+ <div
447
+ class="st-comboChart__tooltip"
448
+ role="presentation"
449
+ style="left: {(tooltip.cx / width) * 100}%; top: {(tooltip.cy / height) * 100}%"
450
+ >
451
+ <span class="st-comboChart__tooltipLabel">{tooltip.label}</span>
452
+ <span class="st-comboChart__tooltipValue">{tooltip.value}</span>
453
+ </div>
454
+ {/if}
455
+ </div>
456
+
457
+ <style>
458
+ .st-comboChart {
459
+ color: var(--st-semantic-text-secondary);
460
+ display: block;
461
+ font-family: inherit;
462
+ position: relative;
463
+ width: 100%;
464
+ }
465
+
466
+ .st-comboChart svg {
467
+ display: block;
468
+ overflow: visible;
469
+ }
470
+
471
+ .st-comboChart__visual {
472
+ display: block;
473
+ }
474
+
475
+ .st-comboChart__grid {
476
+ stroke: var(--st-component-comboChart-gridStroke, var(--st-semantic-border-subtle));
477
+ stroke-dasharray: 2 3;
478
+ stroke-width: 1;
479
+ opacity: 0.7;
480
+ }
481
+
482
+ .st-comboChart__axis {
483
+ stroke: var(--st-component-comboChart-axisStroke, var(--st-semantic-border-subtle));
484
+ stroke-width: 1;
485
+ }
486
+
487
+ .st-comboChart__tickLabel,
488
+ .st-comboChart__categoryLabel {
489
+ fill: var(--st-component-comboChart-labelColor, var(--st-semantic-text-secondary));
490
+ font-size: 0.6875rem;
491
+ }
492
+
493
+ .st-comboChart__axisLabel {
494
+ fill: var(--st-component-comboChart-labelColor, var(--st-semantic-text-secondary));
495
+ font-size: 0.6875rem;
496
+ font-weight: 600;
497
+ }
498
+
499
+ .st-comboChart__bar {
500
+ cursor: pointer;
501
+ transition: opacity 120ms ease;
502
+ }
503
+
504
+ .st-comboChart__bar:hover {
505
+ opacity: 0.82;
506
+ }
507
+
508
+ .st-comboChart__bar--category1 { fill: var(--st-semantic-data-category1); }
509
+ .st-comboChart__bar--category2 { fill: var(--st-semantic-data-category2); }
510
+ .st-comboChart__bar--category3 { fill: var(--st-semantic-data-category3); }
511
+ .st-comboChart__bar--category4 { fill: var(--st-semantic-data-category4); }
512
+ .st-comboChart__bar--category5 { fill: var(--st-semantic-data-category5); }
513
+ .st-comboChart__bar--category6 { fill: var(--st-semantic-data-category6); }
514
+ .st-comboChart__bar--category7 { fill: var(--st-semantic-data-category7); }
515
+ .st-comboChart__bar--category8 { fill: var(--st-semantic-data-category8); }
516
+
517
+ .st-comboChart__line--category1 { stroke: var(--st-semantic-data-category1); }
518
+ .st-comboChart__line--category2 { stroke: var(--st-semantic-data-category2); }
519
+ .st-comboChart__line--category3 { stroke: var(--st-semantic-data-category3); }
520
+ .st-comboChart__line--category4 { stroke: var(--st-semantic-data-category4); }
521
+ .st-comboChart__line--category5 { stroke: var(--st-semantic-data-category5); }
522
+ .st-comboChart__line--category6 { stroke: var(--st-semantic-data-category6); }
523
+ .st-comboChart__line--category7 { stroke: var(--st-semantic-data-category7); }
524
+ .st-comboChart__line--category8 { stroke: var(--st-semantic-data-category8); }
525
+
526
+ .st-comboChart__dot {
527
+ stroke: var(--st-semantic-surface-default);
528
+ stroke-width: 1.5;
529
+ cursor: pointer;
530
+ transition: r 120ms ease;
531
+ }
532
+
533
+ .st-comboChart__dot:hover {
534
+ r: 5.5;
535
+ }
536
+
537
+ .st-comboChart__dot--category1 { fill: var(--st-semantic-data-category1); }
538
+ .st-comboChart__dot--category2 { fill: var(--st-semantic-data-category2); }
539
+ .st-comboChart__dot--category3 { fill: var(--st-semantic-data-category3); }
540
+ .st-comboChart__dot--category4 { fill: var(--st-semantic-data-category4); }
541
+ .st-comboChart__dot--category5 { fill: var(--st-semantic-data-category5); }
542
+ .st-comboChart__dot--category6 { fill: var(--st-semantic-data-category6); }
543
+ .st-comboChart__dot--category7 { fill: var(--st-semantic-data-category7); }
544
+ .st-comboChart__dot--category8 { fill: var(--st-semantic-data-category8); }
545
+
546
+ @media (prefers-reduced-motion: reduce) {
547
+ .st-comboChart__bar,
548
+ .st-comboChart__dot {
549
+ transition: none;
550
+ }
551
+ }
552
+
553
+ .st-comboChart__legend {
554
+ display: flex;
555
+ flex-wrap: wrap;
556
+ gap: var(--st-spacing-3, 0.75rem);
557
+ list-style: none;
558
+ margin: var(--st-spacing-2, 0.5rem) 0 0;
559
+ padding: 0;
560
+ }
561
+
562
+ .st-comboChart__legendItem {
563
+ align-items: center;
564
+ color: var(--st-semantic-text-secondary);
565
+ display: inline-flex;
566
+ font-size: 0.75rem;
567
+ gap: var(--st-spacing-1, 0.25rem);
568
+ }
569
+
570
+ .st-comboChart__legendSwatch {
571
+ display: inline-block;
572
+ flex: none;
573
+ }
574
+
575
+ .st-comboChart__legendSwatch--bar {
576
+ border-radius: 2px;
577
+ height: 0.75rem;
578
+ width: 0.75rem;
579
+ }
580
+
581
+ .st-comboChart__legendSwatch--line {
582
+ border-radius: 999px;
583
+ height: 0.25rem;
584
+ width: 1rem;
585
+ }
586
+
587
+ .st-comboChart__legendSwatch--category1 { background: var(--st-semantic-data-category1); }
588
+ .st-comboChart__legendSwatch--category2 { background: var(--st-semantic-data-category2); }
589
+ .st-comboChart__legendSwatch--category3 { background: var(--st-semantic-data-category3); }
590
+ .st-comboChart__legendSwatch--category4 { background: var(--st-semantic-data-category4); }
591
+ .st-comboChart__legendSwatch--category5 { background: var(--st-semantic-data-category5); }
592
+ .st-comboChart__legendSwatch--category6 { background: var(--st-semantic-data-category6); }
593
+ .st-comboChart__legendSwatch--category7 { background: var(--st-semantic-data-category7); }
594
+ .st-comboChart__legendSwatch--category8 { background: var(--st-semantic-data-category8); }
595
+
596
+ .st-comboChart__tooltip {
597
+ background: var(--st-component-comboChart-tooltipBackground, var(--st-semantic-surface-inverse));
598
+ border-radius: var(--st-radius-sm, 0.25rem);
599
+ color: var(--st-component-comboChart-tooltipText, var(--st-semantic-text-inverse));
600
+ display: inline-flex;
601
+ flex-direction: column;
602
+ font-size: 0.75rem;
603
+ gap: 0.125rem;
604
+ line-height: 1.2;
605
+ padding: 0.375rem 0.5rem;
606
+ pointer-events: none;
607
+ position: absolute;
608
+ transform: translate(-50%, calc(-100% - 8px));
609
+ white-space: nowrap;
610
+ z-index: 1;
611
+ }
612
+
613
+ .st-comboChart__tooltipLabel {
614
+ font-weight: 600;
615
+ }
616
+
617
+ .st-comboChart__tooltipValue {
618
+ opacity: 0.85;
619
+ }
620
+ </style>
@@ -0,0 +1,28 @@
1
+ export type ComboChartTone = "category1" | "category2" | "category3" | "category4" | "category5" | "category6" | "category7" | "category8";
2
+ export type ComboChartBarSeries = {
3
+ label: string;
4
+ data: number[];
5
+ tone?: ComboChartTone;
6
+ };
7
+ export type ComboChartLineSeries = {
8
+ label: string;
9
+ data: number[];
10
+ tone?: ComboChartTone;
11
+ smooth?: boolean;
12
+ };
13
+ type ComboChartProps = {
14
+ categories: string[];
15
+ bars?: ComboChartBarSeries[];
16
+ lines?: ComboChartLineSeries[];
17
+ leftAxisLabel?: string;
18
+ rightAxisLabel?: string;
19
+ legend?: boolean;
20
+ width?: number;
21
+ height?: number;
22
+ label: string;
23
+ class?: string;
24
+ };
25
+ declare const ComboChart: import("svelte").Component<ComboChartProps, {}, "">;
26
+ type ComboChart = ReturnType<typeof ComboChart>;
27
+ export default ComboChart;
28
+ //# sourceMappingURL=ComboChart.svelte.d.ts.map
@@ -0,0 +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"}