@juspay/svelte-ui-components 2.31.0 → 2.32.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.
Files changed (43) hide show
  1. package/dist/AreaChart/AreaChart.svelte +421 -0
  2. package/dist/AreaChart/AreaChart.svelte.d.ts +4 -0
  3. package/dist/AreaChart/properties.d.ts +62 -0
  4. package/dist/AreaChart/properties.js +1 -0
  5. package/dist/BarChart/BarChart.svelte +372 -0
  6. package/dist/BarChart/BarChart.svelte.d.ts +4 -0
  7. package/dist/BarChart/properties.d.ts +44 -0
  8. package/dist/BarChart/properties.js +1 -0
  9. package/dist/LineChart/LineChart.svelte +361 -0
  10. package/dist/LineChart/LineChart.svelte.d.ts +4 -0
  11. package/dist/LineChart/properties.d.ts +59 -0
  12. package/dist/LineChart/properties.js +1 -0
  13. package/dist/PieChart/PieChart.svelte +236 -0
  14. package/dist/PieChart/PieChart.svelte.d.ts +4 -0
  15. package/dist/PieChart/properties.d.ts +36 -0
  16. package/dist/PieChart/properties.js +1 -0
  17. package/dist/SankeyChart/SankeyChart.svelte +312 -0
  18. package/dist/SankeyChart/SankeyChart.svelte.d.ts +4 -0
  19. package/dist/SankeyChart/properties.d.ts +52 -0
  20. package/dist/SankeyChart/properties.js +1 -0
  21. package/dist/_chart/Axis.svelte +143 -0
  22. package/dist/_chart/Axis.svelte.d.ts +4 -0
  23. package/dist/_chart/ChartContainer.svelte +81 -0
  24. package/dist/_chart/ChartContainer.svelte.d.ts +4 -0
  25. package/dist/_chart/ChartTooltip.svelte +81 -0
  26. package/dist/_chart/ChartTooltip.svelte.d.ts +4 -0
  27. package/dist/_chart/Legend.svelte +59 -0
  28. package/dist/_chart/Legend.svelte.d.ts +4 -0
  29. package/dist/_chart/colors.d.ts +4 -0
  30. package/dist/_chart/colors.js +36 -0
  31. package/dist/_chart/format.d.ts +3 -0
  32. package/dist/_chart/format.js +28 -0
  33. package/dist/_chart/geometry.d.ts +24 -0
  34. package/dist/_chart/geometry.js +204 -0
  35. package/dist/_chart/paths.d.ts +4 -0
  36. package/dist/_chart/paths.js +138 -0
  37. package/dist/_chart/scales.d.ts +5 -0
  38. package/dist/_chart/scales.js +77 -0
  39. package/dist/_chart/types.d.ts +128 -0
  40. package/dist/_chart/types.js +1 -0
  41. package/dist/index.d.ts +10 -0
  42. package/dist/index.js +5 -0
  43. package/package.json +1 -1
@@ -0,0 +1,361 @@
1
+ <script lang="ts">
2
+ import type { LineChartProperties, LineChartTooltipContext } from './properties';
3
+ import ChartContainer from '../_chart/ChartContainer.svelte';
4
+ import Axis from '../_chart/Axis.svelte';
5
+ import ChartTooltip from '../_chart/ChartTooltip.svelte';
6
+ import Legend from '../_chart/Legend.svelte';
7
+ import { createLinearScale, niceLinearDomain } from '../_chart/scales';
8
+ import { computeChartDimensions } from '../_chart/geometry';
9
+ import { linePath } from '../_chart/paths';
10
+ import { getColor } from '../_chart/colors';
11
+ import { formatNumber } from '../_chart/format';
12
+ import type { LegendItem, Point } from '../_chart/types';
13
+
14
+ // ── Props ──────────────────────────────────────────────────────
15
+
16
+ let {
17
+ series,
18
+ curve = 'monotone',
19
+ showDots = true,
20
+ showValues = false,
21
+ dotRadius = 4,
22
+ strokeWidth = 2,
23
+ showGridlines = true,
24
+ showXAxis = true,
25
+ showYAxis = true,
26
+ showLegend = false,
27
+ xDomain,
28
+ yDomain,
29
+ xAxisLabel,
30
+ yAxisLabel,
31
+ xTickFormat,
32
+ yTickFormat,
33
+ aspectRatio = 16 / 9,
34
+ tooltipSnippet,
35
+ empty,
36
+ onpointclick,
37
+ onpointhover,
38
+ testId,
39
+ classes
40
+ }: LineChartProperties = $props();
41
+
42
+ // ── State ──────────────────────────────────────────────────────
43
+
44
+ let containerEl: HTMLDivElement | null = $state(null);
45
+ let chartWidth = $state(0);
46
+ let chartHeight = $state(0);
47
+ let hovered = $state<{ si: number; pi: number } | null>(null);
48
+ let mouseX = $state(0);
49
+ let mouseY = $state(0);
50
+
51
+ // ── Layout ─────────────────────────────────────────────────────
52
+
53
+ let dims = $derived(computeChartDimensions(chartWidth, chartHeight));
54
+
55
+ let isEmpty = $derived(series.length === 0 || series.every((s) => s.data.length === 0));
56
+
57
+ let xExtent = $derived.by<[number, number]>(() => {
58
+ if (xDomain) {
59
+ return xDomain;
60
+ }
61
+ const allX = series.flatMap((s) => s.data.map((d) => d.x));
62
+ if (allX.length === 0) {
63
+ return [0, 1];
64
+ }
65
+ return niceLinearDomain(Math.min(...allX), Math.max(...allX));
66
+ });
67
+ let yExtent = $derived.by<[number, number]>(() => {
68
+ if (yDomain) {
69
+ return yDomain;
70
+ }
71
+ const allY = series.flatMap((s) => s.data.map((d) => d.y));
72
+ if (allY.length === 0) {
73
+ return [0, 1];
74
+ }
75
+ return niceLinearDomain(Math.min(0, ...allY), Math.max(...allY));
76
+ });
77
+
78
+ let xScale = $derived(createLinearScale(xExtent, [0, dims.innerWidth]));
79
+ let yScale = $derived(createLinearScale(yExtent, [dims.innerHeight, 0]));
80
+
81
+ let lines = $derived(
82
+ series.map((s, si) => {
83
+ const color = s.color ?? getColor(si);
84
+ const points: Point[] = s.data.map((d) => ({ x: xScale(d.x), y: yScale(d.y) }));
85
+ return { color, points, path: linePath(points, curve) };
86
+ })
87
+ );
88
+
89
+ let legendItems = $derived<LegendItem[]>(
90
+ series.map((s, i) => ({ label: s.name, color: s.color ?? getColor(i) }))
91
+ );
92
+
93
+ // ── Tooltip ────────────────────────────────────────────────────
94
+
95
+ let hoveredPoint = $derived(
96
+ hovered === null ? null : (series[hovered.si]?.data[hovered.pi] ?? null)
97
+ );
98
+
99
+ let hoverLineX = $derived(
100
+ hovered === null ? null : (lines[hovered.si]?.points[hovered.pi]?.x ?? null)
101
+ );
102
+
103
+ let tooltipContext = $derived.by<LineChartTooltipContext | null>(() => {
104
+ if (hovered === null || hoveredPoint === null) {
105
+ return null;
106
+ }
107
+ return {
108
+ x: hoveredPoint.x,
109
+ points: series.map((s, si) => {
110
+ const p = s.data[hovered!.pi];
111
+ return {
112
+ name: s.name,
113
+ y: p?.y ?? 0,
114
+ color: s.color ?? getColor(si),
115
+ label: p?.label
116
+ };
117
+ })
118
+ };
119
+ });
120
+
121
+ let tooltipData = $derived.by(() => {
122
+ if (tooltipContext === null) {
123
+ return null;
124
+ }
125
+ return {
126
+ title: `x: ${formatNumber(tooltipContext.x)}`,
127
+ items: tooltipContext.points.map((p) => ({
128
+ label: p.name,
129
+ value: formatNumber(p.y),
130
+ color: p.color
131
+ }))
132
+ };
133
+ });
134
+
135
+ // ── Interactions ───────────────────────────────────────────────
136
+
137
+ function trackMouse(e: MouseEvent) {
138
+ if (containerEl === null) {
139
+ return;
140
+ }
141
+ const rect = containerEl.getBoundingClientRect();
142
+ mouseX = e.clientX - rect.left;
143
+ mouseY = e.clientY - rect.top;
144
+ }
145
+
146
+ function findNearest(plotX: number, plotY: number): { si: number; pi: number } | null {
147
+ if (series.length === 0) {
148
+ return null;
149
+ }
150
+ // Use the longest series as the index reference so hover still works when
151
+ // the first series is empty or shorter than the others.
152
+ const reference = series.reduce((a, b) => (b.data.length > a.data.length ? b : a)).data;
153
+ if (reference.length === 0) {
154
+ return null;
155
+ }
156
+ let nearestPi = 0;
157
+ let nearestXDist = Infinity;
158
+ for (let i = 0; i < reference.length; i++) {
159
+ const px = xScale(reference[i].x);
160
+ const dist = Math.abs(px - plotX);
161
+ if (dist < nearestXDist) {
162
+ nearestXDist = dist;
163
+ nearestPi = i;
164
+ }
165
+ }
166
+ let nearestSi = 0;
167
+ let nearestYDist = Infinity;
168
+ for (let si = 0; si < series.length; si++) {
169
+ const p = series[si].data[nearestPi];
170
+ if (!p) {
171
+ continue;
172
+ }
173
+ const py = yScale(p.y);
174
+ const dist = Math.abs(py - plotY);
175
+ if (dist < nearestYDist) {
176
+ nearestYDist = dist;
177
+ nearestSi = si;
178
+ }
179
+ }
180
+ return { si: nearestSi, pi: nearestPi };
181
+ }
182
+
183
+ function handleOverlayMove(e: MouseEvent) {
184
+ trackMouse(e);
185
+ const plotX = mouseX - dims.margin.left;
186
+ const plotY = mouseY - dims.margin.top;
187
+ const next = findNearest(plotX, plotY);
188
+ if (next === null) {
189
+ hovered = null;
190
+ return;
191
+ }
192
+ if (hovered === null || hovered.si !== next.si || hovered.pi !== next.pi) {
193
+ hovered = next;
194
+ const point = series[next.si].data[next.pi];
195
+ onpointhover?.({ seriesIndex: next.si, pointIndex: next.pi, point });
196
+ }
197
+ }
198
+
199
+ function handleLeave() {
200
+ if (hovered !== null) {
201
+ hovered = null;
202
+ onpointhover?.(null);
203
+ }
204
+ }
205
+
206
+ function handleClick() {
207
+ if (hovered === null) {
208
+ return;
209
+ }
210
+ const point = series[hovered.si]?.data[hovered.pi];
211
+ if (point) {
212
+ onpointclick?.({ seriesIndex: hovered.si, pointIndex: hovered.pi, point });
213
+ }
214
+ }
215
+ </script>
216
+
217
+ <div
218
+ class="line-chart {classes ?? ''}"
219
+ bind:this={containerEl}
220
+ data-pw={typeof testId === 'string' ? testId : null}
221
+ >
222
+ {#if isEmpty && typeof empty === 'function'}
223
+ <div class="chart-empty">{@render empty()}</div>
224
+ {:else}
225
+ {#if showLegend && series.length > 1}
226
+ <Legend items={legendItems} position="top" />
227
+ {/if}
228
+
229
+ <ChartContainer bind:width={chartWidth} bind:height={chartHeight} {aspectRatio}>
230
+ <g transform="translate({dims.margin.left}, {dims.margin.top})">
231
+ {#if showYAxis}
232
+ <Axis
233
+ orientation="left"
234
+ scale={yScale}
235
+ {showGridlines}
236
+ gridlineLength={dims.innerWidth}
237
+ label={yAxisLabel}
238
+ tickFormat={yTickFormat}
239
+ />
240
+ {/if}
241
+ {#if showXAxis}
242
+ <g transform="translate(0, {dims.innerHeight})">
243
+ <Axis orientation="bottom" scale={xScale} label={xAxisLabel} tickFormat={xTickFormat} />
244
+ </g>
245
+ {/if}
246
+
247
+ {#each lines as line, si (si)}
248
+ <path
249
+ class="line-path"
250
+ class:dimmed={hovered !== null && hovered.si !== si}
251
+ d={line.path}
252
+ stroke={line.color}
253
+ stroke-width={strokeWidth}
254
+ fill="none"
255
+ />
256
+ {#if showDots}
257
+ {#each line.points as point, pi (pi)}
258
+ <circle
259
+ class="dot"
260
+ class:dimmed={hovered !== null && hovered.si !== si}
261
+ cx={point.x}
262
+ cy={point.y}
263
+ r={hovered?.si === si && hovered?.pi === pi ? dotRadius * 1.5 : dotRadius}
264
+ fill={line.color}
265
+ />
266
+ {/each}
267
+ {/if}
268
+ {#if showValues}
269
+ {#each line.points as point, pi (pi)}
270
+ <text
271
+ class="point-value"
272
+ x={point.x}
273
+ y={point.y - 8}
274
+ text-anchor="middle"
275
+ dominant-baseline="auto">{formatNumber(series[si].data[pi].y)}</text
276
+ >
277
+ {/each}
278
+ {/if}
279
+ {/each}
280
+
281
+ {#if hoverLineX !== null}
282
+ <line class="hover-line" x1={hoverLineX} x2={hoverLineX} y1={0} y2={dims.innerHeight} />
283
+ {/if}
284
+
285
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
286
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
287
+ <rect
288
+ class="hover-overlay"
289
+ x={0}
290
+ y={0}
291
+ width={dims.innerWidth}
292
+ height={dims.innerHeight}
293
+ fill="transparent"
294
+ onmousemove={handleOverlayMove}
295
+ onmouseleave={handleLeave}
296
+ onclick={handleClick}
297
+ />
298
+ </g>
299
+ </ChartContainer>
300
+
301
+ {#if typeof tooltipSnippet === 'function' && tooltipContext !== null}
302
+ <div class="chart-tooltip-slot" style="left: {mouseX + 12}px; top: {mouseY - 12}px;">
303
+ {@render tooltipSnippet(tooltipContext)}
304
+ </div>
305
+ {:else}
306
+ <ChartTooltip data={tooltipData} {mouseX} {mouseY} />
307
+ {/if}
308
+ {/if}
309
+ </div>
310
+
311
+ <style>
312
+ .line-chart {
313
+ width: 100%;
314
+ position: relative;
315
+ }
316
+ .line-path {
317
+ transition: opacity var(--chart-transition-duration, 0.2s) ease;
318
+ stroke-linecap: round;
319
+ stroke-linejoin: round;
320
+ pointer-events: none;
321
+ }
322
+ .line-path.dimmed {
323
+ opacity: var(--linechart-dimmed-opacity, 0.2);
324
+ }
325
+ .dot {
326
+ transition:
327
+ r var(--chart-transition-duration, 0.2s) ease,
328
+ opacity var(--chart-transition-duration, 0.2s) ease;
329
+ stroke: var(--chart-background, #fff);
330
+ stroke-width: 2;
331
+ pointer-events: none;
332
+ }
333
+ .dot.dimmed {
334
+ opacity: var(--linechart-dimmed-opacity, 0.2);
335
+ }
336
+ .point-value {
337
+ fill: var(--linechart-value-color, #333);
338
+ font-size: var(--linechart-value-font-size, 11px);
339
+ font-family: var(--chart-font-family, inherit);
340
+ pointer-events: none;
341
+ }
342
+ .hover-line {
343
+ stroke: var(--linechart-hover-line-color, #ccc);
344
+ stroke-width: 1;
345
+ stroke-dasharray: var(--linechart-hover-line-dash, 4 4);
346
+ pointer-events: none;
347
+ }
348
+ .hover-overlay {
349
+ cursor: crosshair;
350
+ }
351
+ .chart-tooltip-slot {
352
+ position: absolute;
353
+ z-index: 10;
354
+ pointer-events: none;
355
+ }
356
+ .chart-empty {
357
+ padding: var(--chart-empty-padding, 32px 24px);
358
+ color: var(--chart-empty-color, #9ca3af);
359
+ text-align: center;
360
+ }
361
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { LineChartProperties } from './properties';
2
+ declare const LineChart: import("svelte").Component<LineChartProperties, {}, "">;
3
+ type LineChart = ReturnType<typeof LineChart>;
4
+ export default LineChart;
@@ -0,0 +1,59 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { CurveType } from '../_chart/types';
3
+ export type LineChartDataPoint = {
4
+ x: number;
5
+ y: number;
6
+ label?: string;
7
+ };
8
+ export type LineChartSeries = {
9
+ name: string;
10
+ data: LineChartDataPoint[];
11
+ color?: string;
12
+ };
13
+ export type LineChartTooltipContext = {
14
+ x: number;
15
+ points: Array<{
16
+ name: string;
17
+ y: number;
18
+ color: string;
19
+ label?: string;
20
+ }>;
21
+ };
22
+ export type LineChartProperties = MandatoryLineChartProperties & OptionalLineChartProperties & LineChartEventProperties;
23
+ export type MandatoryLineChartProperties = {
24
+ series: LineChartSeries[];
25
+ };
26
+ export type OptionalLineChartProperties = {
27
+ curve?: CurveType;
28
+ showDots?: boolean;
29
+ showValues?: boolean;
30
+ dotRadius?: number;
31
+ strokeWidth?: number;
32
+ showGridlines?: boolean;
33
+ showXAxis?: boolean;
34
+ showYAxis?: boolean;
35
+ showLegend?: boolean;
36
+ xDomain?: [number, number];
37
+ yDomain?: [number, number];
38
+ xAxisLabel?: string;
39
+ yAxisLabel?: string;
40
+ xTickFormat?: (value: number | string) => string;
41
+ yTickFormat?: (value: number | string) => string;
42
+ aspectRatio?: number;
43
+ tooltipSnippet?: Snippet<[LineChartTooltipContext]>;
44
+ empty?: Snippet;
45
+ testId?: string;
46
+ classes?: string;
47
+ };
48
+ export type LineChartEventProperties = {
49
+ onpointclick?: (event: {
50
+ seriesIndex: number;
51
+ pointIndex: number;
52
+ point: LineChartDataPoint;
53
+ }) => void;
54
+ onpointhover?: (event: {
55
+ seriesIndex: number;
56
+ pointIndex: number;
57
+ point: LineChartDataPoint;
58
+ } | null) => void;
59
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,236 @@
1
+ <script lang="ts">
2
+ import type { PieChartProperties } from './properties';
3
+ import ChartContainer from '../_chart/ChartContainer.svelte';
4
+ import ChartTooltip from '../_chart/ChartTooltip.svelte';
5
+ import Legend from '../_chart/Legend.svelte';
6
+ import { arcPath } from '../_chart/paths';
7
+ import { computePieLayout } from '../_chart/geometry';
8
+ import { getColor } from '../_chart/colors';
9
+ import { formatNumber, formatPercent } from '../_chart/format';
10
+ import type { LegendItem } from '../_chart/types';
11
+
12
+ // ── Props ──────────────────────────────────────────────────────
13
+
14
+ let {
15
+ data,
16
+ innerRadius = 0,
17
+ padAngle = 0.02,
18
+ showLabels = false,
19
+ showValues = false,
20
+ labelPosition = 'outside',
21
+ showLegend = false,
22
+ startAngle = -Math.PI / 2,
23
+ aspectRatio = 1,
24
+ valueFormat,
25
+ tooltipSnippet,
26
+ center,
27
+ empty,
28
+ onsliceclick,
29
+ onslicehover,
30
+ testId,
31
+ classes
32
+ }: PieChartProperties = $props();
33
+
34
+ // ── State ──────────────────────────────────────────────────────
35
+
36
+ let containerEl: HTMLDivElement | null = $state(null);
37
+ let chartWidth = $state(0);
38
+ let chartHeight = $state(0);
39
+ let hoveredIndex = $state<number | null>(null);
40
+ let mouseX = $state(0);
41
+ let mouseY = $state(0);
42
+
43
+ // ── Layout ─────────────────────────────────────────────────────
44
+
45
+ let format = $derived(valueFormat ?? formatNumber);
46
+ let total = $derived(data.reduce((sum, d) => sum + Math.max(0, d.value), 0));
47
+ let isEmpty = $derived(data.length === 0 || total === 0);
48
+
49
+ let cx = $derived(chartWidth / 2);
50
+ let cy = $derived(chartHeight / 2);
51
+ let outerR = $derived(
52
+ Math.max(10, Math.min(cx, cy) - (showLabels && labelPosition === 'outside' ? 40 : 10))
53
+ );
54
+ let innerR = $derived(innerRadius > 0 ? outerR * Math.min(0.95, innerRadius) : 0);
55
+
56
+ let slices = $derived(
57
+ computePieLayout(data, startAngle, padAngle).map((s) => {
58
+ const color = s.color ?? data[s.index]?.color ?? getColor(s.index);
59
+ const labelR = labelPosition === 'outside' ? outerR + 16 : (innerR + outerR) / 2;
60
+ return {
61
+ ...s,
62
+ color,
63
+ path: arcPath(0, 0, innerR, outerR, s.startAngle, s.endAngle),
64
+ labelX: labelR * Math.cos(s.midAngle),
65
+ labelY: labelR * Math.sin(s.midAngle)
66
+ };
67
+ })
68
+ );
69
+
70
+ let legendItems = $derived<LegendItem[]>(
71
+ data.map((d, i) => ({ label: d.label, color: d.color ?? getColor(i) }))
72
+ );
73
+
74
+ let centerBoxSize = $derived(innerR > 0 ? Math.max(0, innerR * 1.3) : 0);
75
+
76
+ // ── Tooltip ────────────────────────────────────────────────────
77
+
78
+ let tooltipData = $derived.by(() => {
79
+ if (hoveredIndex === null || !slices[hoveredIndex]) {
80
+ return null;
81
+ }
82
+ const s = slices[hoveredIndex];
83
+ return {
84
+ title: s.label,
85
+ items: [
86
+ {
87
+ label: s.label,
88
+ value: `${format(s.value)} (${formatPercent(s.value, total)})`,
89
+ color: s.color
90
+ }
91
+ ]
92
+ };
93
+ });
94
+
95
+ // ── Interactions ───────────────────────────────────────────────
96
+
97
+ function trackMouse(e: MouseEvent) {
98
+ if (containerEl === null) {
99
+ return;
100
+ }
101
+ const rect = containerEl.getBoundingClientRect();
102
+ mouseX = e.clientX - rect.left;
103
+ mouseY = e.clientY - rect.top;
104
+ }
105
+
106
+ function handleEnter(e: MouseEvent, i: number) {
107
+ hoveredIndex = i;
108
+ trackMouse(e);
109
+ onslicehover?.({ index: i, slice: data[i] });
110
+ }
111
+
112
+ function handleLeave() {
113
+ hoveredIndex = null;
114
+ onslicehover?.(null);
115
+ }
116
+ </script>
117
+
118
+ <div
119
+ class="pie-chart {classes ?? ''}"
120
+ bind:this={containerEl}
121
+ data-pw={typeof testId === 'string' ? testId : null}
122
+ >
123
+ {#if isEmpty && typeof empty === 'function'}
124
+ <div class="chart-empty">{@render empty()}</div>
125
+ {:else}
126
+ {#if showLegend}
127
+ <Legend items={legendItems} position="top" />
128
+ {/if}
129
+
130
+ <ChartContainer bind:width={chartWidth} bind:height={chartHeight} {aspectRatio}>
131
+ <g transform="translate({cx}, {cy})">
132
+ {#each slices as slice (slice.index)}
133
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
134
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
135
+ <path
136
+ class="slice"
137
+ class:hovered={hoveredIndex === slice.index}
138
+ class:dimmed={hoveredIndex !== null && hoveredIndex !== slice.index}
139
+ d={slice.path}
140
+ fill={slice.color}
141
+ aria-label="{slice.label}: {format(slice.value)}"
142
+ onmouseenter={(e) => handleEnter(e, slice.index)}
143
+ onmousemove={trackMouse}
144
+ onmouseleave={handleLeave}
145
+ onclick={() => onsliceclick?.({ index: slice.index, slice: data[slice.index] })}
146
+ />
147
+ {#if showLabels || showValues}
148
+ <text
149
+ class="slice-label"
150
+ class:label-outside={labelPosition === 'outside'}
151
+ x={slice.labelX}
152
+ y={slice.labelY}
153
+ text-anchor="middle"
154
+ dominant-baseline="middle"
155
+ >
156
+ {#if showLabels}{slice.label}{/if}
157
+ {#if showValues}
158
+ {formatPercent(slice.value, total)}{/if}
159
+ </text>
160
+ {/if}
161
+ {/each}
162
+
163
+ {#if innerR > 0 && typeof center === 'function' && centerBoxSize > 0}
164
+ <foreignObject
165
+ x={-centerBoxSize / 2}
166
+ y={-centerBoxSize / 2}
167
+ width={centerBoxSize}
168
+ height={centerBoxSize}
169
+ >
170
+ <div class="pie-center-content" xmlns="http://www.w3.org/1999/xhtml">
171
+ {@render center()}
172
+ </div>
173
+ </foreignObject>
174
+ {/if}
175
+ </g>
176
+ </ChartContainer>
177
+
178
+ {#if typeof tooltipSnippet === 'function' && hoveredIndex !== null && data[hoveredIndex]}
179
+ <div class="chart-tooltip-slot" style="left: {mouseX + 12}px; top: {mouseY - 12}px;">
180
+ {@render tooltipSnippet(data[hoveredIndex], hoveredIndex)}
181
+ </div>
182
+ {:else}
183
+ <ChartTooltip data={tooltipData} {mouseX} {mouseY} />
184
+ {/if}
185
+ {/if}
186
+ </div>
187
+
188
+ <style>
189
+ .pie-chart {
190
+ width: 100%;
191
+ position: relative;
192
+ }
193
+ .slice {
194
+ stroke: var(--piechart-stroke-color, #fff);
195
+ stroke-width: var(--piechart-stroke-width, 2);
196
+ transition:
197
+ transform var(--chart-transition-duration, 0.2s) ease,
198
+ opacity var(--chart-transition-duration, 0.2s) ease;
199
+ transform-origin: 0 0;
200
+ cursor: pointer;
201
+ }
202
+ .slice.hovered {
203
+ transform: scale(var(--piechart-hover-scale, 1.05));
204
+ opacity: 1;
205
+ }
206
+ .slice.dimmed {
207
+ opacity: var(--piechart-dimmed-opacity, 0.3);
208
+ }
209
+ .slice-label {
210
+ fill: var(--piechart-label-color, #333);
211
+ font-size: var(--piechart-label-font-size, 12px);
212
+ font-family: var(--chart-font-family, inherit);
213
+ pointer-events: none;
214
+ }
215
+ .slice-label.label-outside {
216
+ fill: var(--piechart-label-color, #555);
217
+ }
218
+ .pie-center-content {
219
+ width: 100%;
220
+ height: 100%;
221
+ display: flex;
222
+ align-items: center;
223
+ justify-content: center;
224
+ text-align: center;
225
+ }
226
+ .chart-tooltip-slot {
227
+ position: absolute;
228
+ z-index: 10;
229
+ pointer-events: none;
230
+ }
231
+ .chart-empty {
232
+ padding: var(--chart-empty-padding, 32px 24px);
233
+ color: var(--chart-empty-color, #9ca3af);
234
+ text-align: center;
235
+ }
236
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { PieChartProperties } from './properties';
2
+ declare const PieChart: import("svelte").Component<PieChartProperties, {}, "">;
3
+ type PieChart = ReturnType<typeof PieChart>;
4
+ export default PieChart;
@@ -0,0 +1,36 @@
1
+ import type { Snippet } from 'svelte';
2
+ export type PieChartSlice = {
3
+ label: string;
4
+ value: number;
5
+ color?: string;
6
+ };
7
+ export type PieChartProperties = MandatoryPieChartProperties & OptionalPieChartProperties & PieChartEventProperties;
8
+ export type MandatoryPieChartProperties = {
9
+ data: PieChartSlice[];
10
+ };
11
+ export type OptionalPieChartProperties = {
12
+ innerRadius?: number;
13
+ padAngle?: number;
14
+ showLabels?: boolean;
15
+ showValues?: boolean;
16
+ labelPosition?: 'inside' | 'outside';
17
+ showLegend?: boolean;
18
+ startAngle?: number;
19
+ aspectRatio?: number;
20
+ valueFormat?: (value: number) => string;
21
+ tooltipSnippet?: Snippet<[PieChartSlice, number]>;
22
+ center?: Snippet;
23
+ empty?: Snippet;
24
+ testId?: string;
25
+ classes?: string;
26
+ };
27
+ export type PieChartEventProperties = {
28
+ onsliceclick?: (event: {
29
+ index: number;
30
+ slice: PieChartSlice;
31
+ }) => void;
32
+ onslicehover?: (event: {
33
+ index: number;
34
+ slice: PieChartSlice;
35
+ } | null) => void;
36
+ };