@juspay/svelte-ui-components 2.30.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 (45) 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/Select/Select.svelte +43 -24
  22. package/dist/Select/properties.d.ts +5 -0
  23. package/dist/_chart/Axis.svelte +143 -0
  24. package/dist/_chart/Axis.svelte.d.ts +4 -0
  25. package/dist/_chart/ChartContainer.svelte +81 -0
  26. package/dist/_chart/ChartContainer.svelte.d.ts +4 -0
  27. package/dist/_chart/ChartTooltip.svelte +81 -0
  28. package/dist/_chart/ChartTooltip.svelte.d.ts +4 -0
  29. package/dist/_chart/Legend.svelte +59 -0
  30. package/dist/_chart/Legend.svelte.d.ts +4 -0
  31. package/dist/_chart/colors.d.ts +4 -0
  32. package/dist/_chart/colors.js +36 -0
  33. package/dist/_chart/format.d.ts +3 -0
  34. package/dist/_chart/format.js +28 -0
  35. package/dist/_chart/geometry.d.ts +24 -0
  36. package/dist/_chart/geometry.js +204 -0
  37. package/dist/_chart/paths.d.ts +4 -0
  38. package/dist/_chart/paths.js +138 -0
  39. package/dist/_chart/scales.d.ts +5 -0
  40. package/dist/_chart/scales.js +77 -0
  41. package/dist/_chart/types.d.ts +128 -0
  42. package/dist/_chart/types.js +1 -0
  43. package/dist/index.d.ts +10 -0
  44. package/dist/index.js +5 -0
  45. package/package.json +1 -1
@@ -0,0 +1,372 @@
1
+ <script lang="ts">
2
+ import type { BarChartProperties } 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 { createBandScale, createLinearScale, niceLinearDomain } from '../_chart/scales';
8
+ import { computeChartDimensions } from '../_chart/geometry';
9
+ import { getColor } from '../_chart/colors';
10
+ import { formatNumber } from '../_chart/format';
11
+ import type { LegendItem, BarRect } from '../_chart/types';
12
+
13
+ // ── Props ──────────────────────────────────────────────────────
14
+
15
+ let {
16
+ data,
17
+ series,
18
+ groupMode = 'grouped',
19
+ orientation = 'vertical',
20
+ showValues = false,
21
+ showGridlines = true,
22
+ showXAxis = true,
23
+ showYAxis = true,
24
+ showLegend = false,
25
+ barPadding = 0.2,
26
+ barRadius = 4,
27
+ aspectRatio = 16 / 9,
28
+ xAxisLabel,
29
+ yAxisLabel,
30
+ yDomain,
31
+ valueFormat,
32
+ tooltipSnippet,
33
+ empty,
34
+ onbarclick,
35
+ onbarhover,
36
+ testId,
37
+ classes
38
+ }: BarChartProperties = $props();
39
+
40
+ // ── State ──────────────────────────────────────────────────────
41
+
42
+ let containerEl: HTMLDivElement | null = $state(null);
43
+ let chartWidth = $state(0);
44
+ let chartHeight = $state(0);
45
+ let hovered = $state<{ si: number; pi: number } | null>(null);
46
+ let mouseX = $state(0);
47
+ let mouseY = $state(0);
48
+
49
+ // ── Layout ─────────────────────────────────────────────────────
50
+
51
+ let format = $derived(valueFormat ?? formatNumber);
52
+ let isVertical = $derived(orientation === 'vertical');
53
+ let dims = $derived(computeChartDimensions(chartWidth, chartHeight));
54
+
55
+ let isMulti = $derived(Array.isArray(series) && series.length > 0);
56
+ let resolvedSeries = $derived(isMulti ? series! : [{ name: '', data: data ?? [] }]);
57
+
58
+ let labels = $derived.by(() => {
59
+ const first = resolvedSeries[0]?.data ?? [];
60
+ return first.map((d) => d.label);
61
+ });
62
+
63
+ let yExtent = $derived.by<[number, number]>(() => {
64
+ if (yDomain) {
65
+ return yDomain;
66
+ }
67
+ if (isMulti && groupMode === 'stacked') {
68
+ const totalsPerLabel = labels.map((_, i) =>
69
+ resolvedSeries.reduce((sum, s) => sum + Math.max(0, s.data[i]?.value ?? 0), 0)
70
+ );
71
+ return niceLinearDomain(0, Math.max(0, ...totalsPerLabel));
72
+ }
73
+ const all = resolvedSeries.flatMap((s) => s.data.map((d) => d.value));
74
+ if (all.length === 0) {
75
+ return [0, 1];
76
+ }
77
+ return niceLinearDomain(Math.min(0, ...all), Math.max(0, ...all));
78
+ });
79
+
80
+ let catScale = $derived(
81
+ createBandScale(labels, isVertical ? [0, dims.innerWidth] : [0, dims.innerHeight], barPadding)
82
+ );
83
+ let valScale = $derived(
84
+ createLinearScale(yExtent, isVertical ? [dims.innerHeight, 0] : [0, dims.innerWidth])
85
+ );
86
+
87
+ let bars = $derived.by<BarRect[]>(() => {
88
+ const zeroPos = valScale(0);
89
+ const result: BarRect[] = [];
90
+
91
+ if (isMulti && groupMode === 'grouped') {
92
+ const subBand = catScale.bandwidth / resolvedSeries.length;
93
+ for (let si = 0; si < resolvedSeries.length; si++) {
94
+ const s = resolvedSeries[si];
95
+ const seriesColor = s.color ?? getColor(si);
96
+ for (let pi = 0; pi < s.data.length; pi++) {
97
+ const d = s.data[pi];
98
+ const catPos = catScale(d.label) + si * subBand;
99
+ const valPos = valScale(d.value);
100
+ const color = d.color ?? seriesColor;
101
+ result.push(
102
+ isVertical
103
+ ? {
104
+ x: catPos,
105
+ y: d.value >= 0 ? valPos : zeroPos,
106
+ width: Math.max(1, subBand * 0.9),
107
+ height: Math.max(2, Math.abs(valPos - zeroPos)),
108
+ color,
109
+ si,
110
+ pi,
111
+ dataPoint: d,
112
+ seriesName: s.name
113
+ }
114
+ : {
115
+ x: d.value >= 0 ? zeroPos : valPos,
116
+ y: catPos,
117
+ width: Math.max(2, Math.abs(valPos - zeroPos)),
118
+ height: Math.max(1, subBand * 0.9),
119
+ color,
120
+ si,
121
+ pi,
122
+ dataPoint: d,
123
+ seriesName: s.name
124
+ }
125
+ );
126
+ }
127
+ }
128
+ } else if (isMulti && groupMode === 'stacked') {
129
+ const stackBase = new Array(labels.length).fill(0);
130
+ for (let si = 0; si < resolvedSeries.length; si++) {
131
+ const s = resolvedSeries[si];
132
+ const seriesColor = s.color ?? getColor(si);
133
+ for (let pi = 0; pi < s.data.length; pi++) {
134
+ const d = s.data[pi];
135
+ const val = Math.max(0, d.value);
136
+ const y0 = stackBase[pi];
137
+ const y1 = y0 + val;
138
+ stackBase[pi] = y1;
139
+ const color = d.color ?? seriesColor;
140
+ if (isVertical) {
141
+ result.push({
142
+ x: catScale(d.label),
143
+ y: valScale(y1),
144
+ width: catScale.bandwidth,
145
+ height: Math.max(0, valScale(y0) - valScale(y1)),
146
+ color,
147
+ si,
148
+ pi,
149
+ dataPoint: d,
150
+ seriesName: s.name
151
+ });
152
+ } else {
153
+ result.push({
154
+ x: valScale(y0),
155
+ y: catScale(d.label),
156
+ width: Math.max(0, valScale(y1) - valScale(y0)),
157
+ height: catScale.bandwidth,
158
+ color,
159
+ si,
160
+ pi,
161
+ dataPoint: d,
162
+ seriesName: s.name
163
+ });
164
+ }
165
+ }
166
+ }
167
+ } else {
168
+ const singleSeries = resolvedSeries[0];
169
+ for (let pi = 0; pi < singleSeries.data.length; pi++) {
170
+ const d = singleSeries.data[pi];
171
+ const catPos = catScale(d.label);
172
+ const valPos = valScale(d.value);
173
+ const color = d.color ?? getColor(pi);
174
+ result.push(
175
+ isVertical
176
+ ? {
177
+ x: catPos,
178
+ y: d.value >= 0 ? valPos : zeroPos,
179
+ width: catScale.bandwidth,
180
+ height: Math.max(2, Math.abs(valPos - zeroPos)),
181
+ color,
182
+ si: 0,
183
+ pi,
184
+ dataPoint: d,
185
+ seriesName: singleSeries.name
186
+ }
187
+ : {
188
+ x: d.value >= 0 ? zeroPos : valPos,
189
+ y: catPos,
190
+ width: Math.max(2, Math.abs(valPos - zeroPos)),
191
+ height: catScale.bandwidth,
192
+ color,
193
+ si: 0,
194
+ pi,
195
+ dataPoint: d,
196
+ seriesName: singleSeries.name
197
+ }
198
+ );
199
+ }
200
+ }
201
+ return result;
202
+ });
203
+
204
+ let legendItems = $derived<LegendItem[]>(
205
+ isMulti ? resolvedSeries.map((s, i) => ({ label: s.name, color: s.color ?? getColor(i) })) : []
206
+ );
207
+
208
+ let isEmpty = $derived(resolvedSeries.every((s) => s.data.length === 0) || labels.length === 0);
209
+
210
+ // ── Tooltip ────────────────────────────────────────────────────
211
+
212
+ let tooltipData = $derived.by(() => {
213
+ if (hovered === null) {
214
+ return null;
215
+ }
216
+ const bar = bars.find((b) => b.si === hovered!.si && b.pi === hovered!.pi);
217
+ if (!bar) {
218
+ return null;
219
+ }
220
+ const title = isMulti ? `${bar.dataPoint.label} — ${bar.seriesName}` : bar.dataPoint.label;
221
+ return {
222
+ title,
223
+ items: [{ label: bar.dataPoint.label, value: format(bar.dataPoint.value), color: bar.color }]
224
+ };
225
+ });
226
+
227
+ // ── Interactions ───────────────────────────────────────────────
228
+
229
+ function trackMouse(e: MouseEvent) {
230
+ if (containerEl === null) {
231
+ return;
232
+ }
233
+ const rect = containerEl.getBoundingClientRect();
234
+ mouseX = e.clientX - rect.left;
235
+ mouseY = e.clientY - rect.top;
236
+ }
237
+
238
+ function handleEnter(e: MouseEvent, bar: BarRect) {
239
+ hovered = { si: bar.si, pi: bar.pi };
240
+ trackMouse(e);
241
+ onbarhover?.({ index: bar.pi, dataPoint: bar.dataPoint });
242
+ }
243
+
244
+ function handleLeave() {
245
+ hovered = null;
246
+ onbarhover?.(null);
247
+ }
248
+
249
+ function handleClick(bar: BarRect) {
250
+ onbarclick?.({ index: bar.pi, dataPoint: bar.dataPoint });
251
+ }
252
+
253
+ function hoveredBar() {
254
+ return hovered === null
255
+ ? null
256
+ : (bars.find((b) => b.si === hovered!.si && b.pi === hovered!.pi) ?? null);
257
+ }
258
+ </script>
259
+
260
+ <div
261
+ class="bar-chart {classes ?? ''}"
262
+ bind:this={containerEl}
263
+ data-pw={typeof testId === 'string' ? testId : null}
264
+ >
265
+ {#if isEmpty && typeof empty === 'function'}
266
+ <div class="chart-empty">{@render empty()}</div>
267
+ {:else}
268
+ {#if isMulti && showLegend}
269
+ <Legend items={legendItems} position="top" />
270
+ {/if}
271
+
272
+ <ChartContainer bind:width={chartWidth} bind:height={chartHeight} {aspectRatio}>
273
+ <g transform="translate({dims.margin.left}, {dims.margin.top})">
274
+ {#if showYAxis}
275
+ <Axis
276
+ orientation="left"
277
+ scale={isVertical ? valScale : catScale}
278
+ {showGridlines}
279
+ gridlineLength={dims.innerWidth}
280
+ label={yAxisLabel}
281
+ />
282
+ {/if}
283
+ {#if showXAxis}
284
+ <g transform="translate(0, {dims.innerHeight})">
285
+ <Axis
286
+ orientation="bottom"
287
+ scale={isVertical ? catScale : valScale}
288
+ showGridlines={!isVertical && showGridlines}
289
+ gridlineLength={dims.innerHeight}
290
+ label={xAxisLabel}
291
+ />
292
+ </g>
293
+ {/if}
294
+
295
+ {#each bars as bar, i (i)}
296
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
297
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
298
+ <rect
299
+ class="bar"
300
+ class:hovered={hovered?.si === bar.si && hovered?.pi === bar.pi}
301
+ class:dimmed={hovered !== null && (hovered.si !== bar.si || hovered.pi !== bar.pi)}
302
+ x={bar.x}
303
+ y={bar.y}
304
+ width={bar.width}
305
+ height={bar.height}
306
+ rx={barRadius}
307
+ ry={barRadius}
308
+ fill={bar.color}
309
+ aria-label="{bar.dataPoint.label}: {format(bar.dataPoint.value)}"
310
+ onmouseenter={(e) => handleEnter(e, bar)}
311
+ onmousemove={trackMouse}
312
+ onmouseleave={handleLeave}
313
+ onclick={() => handleClick(bar)}
314
+ />
315
+ {#if showValues && !(isMulti && groupMode === 'stacked')}
316
+ <text
317
+ class="bar-value"
318
+ x={isVertical ? bar.x + bar.width / 2 : bar.x + bar.width + 4}
319
+ y={isVertical ? bar.y - 4 : bar.y + bar.height / 2}
320
+ text-anchor={isVertical ? 'middle' : 'start'}
321
+ dominant-baseline={isVertical ? 'auto' : 'middle'}>{format(bar.dataPoint.value)}</text
322
+ >
323
+ {/if}
324
+ {/each}
325
+ </g>
326
+ </ChartContainer>
327
+
328
+ {#if typeof tooltipSnippet === 'function' && hoveredBar()}
329
+ {@const hb = hoveredBar()}
330
+ {#if hb}
331
+ <div class="chart-tooltip-slot" style="left: {mouseX + 12}px; top: {mouseY - 12}px;">
332
+ {@render tooltipSnippet(hb.dataPoint, hb.pi)}
333
+ </div>
334
+ {/if}
335
+ {:else}
336
+ <ChartTooltip data={tooltipData} {mouseX} {mouseY} />
337
+ {/if}
338
+ {/if}
339
+ </div>
340
+
341
+ <style>
342
+ .bar-chart {
343
+ width: 100%;
344
+ position: relative;
345
+ }
346
+ .bar {
347
+ transition: opacity var(--chart-transition-duration, 0.2s) ease;
348
+ cursor: pointer;
349
+ }
350
+ .bar.hovered {
351
+ opacity: var(--barchart-bar-hover-opacity, 1);
352
+ }
353
+ .bar.dimmed {
354
+ opacity: var(--barchart-bar-dimmed-opacity, 0.3);
355
+ }
356
+ .bar-value {
357
+ fill: var(--barchart-value-color, #333);
358
+ font-size: var(--barchart-value-font-size, 11px);
359
+ font-family: var(--chart-font-family, inherit);
360
+ pointer-events: none;
361
+ }
362
+ .chart-tooltip-slot {
363
+ position: absolute;
364
+ z-index: 10;
365
+ pointer-events: none;
366
+ }
367
+ .chart-empty {
368
+ padding: var(--chart-empty-padding, 32px 24px);
369
+ color: var(--chart-empty-color, #9ca3af);
370
+ text-align: center;
371
+ }
372
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { BarChartProperties } from './properties';
2
+ declare const BarChart: import("svelte").Component<BarChartProperties, {}, "">;
3
+ type BarChart = ReturnType<typeof BarChart>;
4
+ export default BarChart;
@@ -0,0 +1,44 @@
1
+ import type { Snippet } from 'svelte';
2
+ export type BarChartDataPoint = {
3
+ label: string;
4
+ value: number;
5
+ color?: string;
6
+ };
7
+ export type BarChartSeries = {
8
+ name: string;
9
+ data: BarChartDataPoint[];
10
+ color?: string;
11
+ };
12
+ export type BarChartProperties = OptionalBarChartProperties & BarChartEventProperties;
13
+ export type OptionalBarChartProperties = {
14
+ data?: BarChartDataPoint[];
15
+ series?: BarChartSeries[];
16
+ orientation?: 'vertical' | 'horizontal';
17
+ showValues?: boolean;
18
+ showGridlines?: boolean;
19
+ showXAxis?: boolean;
20
+ showYAxis?: boolean;
21
+ barPadding?: number;
22
+ barRadius?: number;
23
+ aspectRatio?: number;
24
+ xAxisLabel?: string;
25
+ yAxisLabel?: string;
26
+ yDomain?: [number, number];
27
+ valueFormat?: (value: number) => string;
28
+ groupMode?: 'grouped' | 'stacked';
29
+ showLegend?: boolean;
30
+ tooltipSnippet?: Snippet<[BarChartDataPoint, number]>;
31
+ empty?: Snippet;
32
+ testId?: string;
33
+ classes?: string;
34
+ };
35
+ export type BarChartEventProperties = {
36
+ onbarclick?: (event: {
37
+ index: number;
38
+ dataPoint: BarChartDataPoint;
39
+ }) => void;
40
+ onbarhover?: (event: {
41
+ index: number;
42
+ dataPoint: BarChartDataPoint;
43
+ } | null) => void;
44
+ };
@@ -0,0 +1 @@
1
+ export {};