@juspay/svelte-ui-components 2.71.0 → 2.73.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.
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { AreaChartProperties, AreaChartTooltipContext } from './properties';
3
+ import { onMount } from 'svelte';
3
4
  import ChartContainer from '../_chart/ChartContainer.svelte';
4
5
  import Axis from '../_chart/Axis.svelte';
5
6
  import ChartTooltip from '../_chart/ChartTooltip.svelte';
@@ -8,17 +9,24 @@
8
9
  import { computeChartDimensions, computeStackedValues } from '../_chart/geometry';
9
10
  import { linePath, areaPath } from '../_chart/paths';
10
11
  import { getColor } from '../_chart/colors';
11
- import { formatNumber } from '../_chart/format';
12
+ import { formatNumber, formatPercent } from '../_chart/format';
12
13
  import type { LegendItem, Point } from '../_chart/types';
13
14
 
14
15
  // ── Props ──────────────────────────────────────────────────────
15
16
 
17
+ // Per-instance ID for SVG gradient <linearGradient id> references. Initialised
18
+ // inside onMount so the value is only ever generated on the client — avoids an
19
+ // SSR hydration mismatch that would occur if Math.random() ran on both server
20
+ // and client and produced different strings.
21
+ let uid = $state('');
22
+
16
23
  let {
17
24
  series,
18
25
  curve = 'monotone',
19
26
  stacked = false,
20
27
  stackNormalize = false,
21
28
  fillOpacity = 0.3,
29
+ gradientFill = false,
22
30
  showDots = false,
23
31
  showLine = true,
24
32
  showValues = false,
@@ -51,6 +59,10 @@
51
59
  let mouseX = $state(0);
52
60
  let mouseY = $state(0);
53
61
 
62
+ onMount(() => {
63
+ uid = Math.random().toString(36).slice(2, 9);
64
+ });
65
+
54
66
  // ── Layout ─────────────────────────────────────────────────────
55
67
 
56
68
  let dims = $derived(computeChartDimensions(chartWidth, chartHeight));
@@ -169,8 +181,23 @@
169
181
  if (tooltipContext === null) {
170
182
  return null;
171
183
  }
184
+ const title = xTickFormat
185
+ ? xTickFormat(tooltipContext.x)
186
+ : `x: ${formatNumber(tooltipContext.x)}`;
187
+ if (stackNormalize) {
188
+ const pi = hovered?.pi ?? 0;
189
+ const columnTotal = series.reduce((sum, s) => sum + Math.max(0, s.data[pi]?.y ?? 0), 0);
190
+ return {
191
+ title,
192
+ items: tooltipContext.points.map((p) => ({
193
+ label: p.name,
194
+ value: formatPercent(Math.max(0, p.y), columnTotal),
195
+ color: p.color
196
+ }))
197
+ };
198
+ }
172
199
  return {
173
- title: `x: ${formatNumber(tooltipContext.x)}`,
200
+ title,
174
201
  items: tooltipContext.points.map((p) => ({
175
202
  label: p.name,
176
203
  value: formatNumber(p.y),
@@ -273,6 +300,37 @@
273
300
  {/if}
274
301
 
275
302
  <ChartContainer bind:width={chartWidth} bind:height={chartHeight} {aspectRatio}>
303
+ {#if gradientFill}
304
+ <!-- <defs> must be a direct child of <svg> (SVG root), not inside a transformed <g>.
305
+ gradientUnits="userSpaceOnUse" with y1/y2 in the inner coordinate space (0..innerHeight)
306
+ correctly spans the full chart height regardless of how thin each band is. -->
307
+ <defs>
308
+ {#each areas as area, si (si)}
309
+ <linearGradient
310
+ id="area-grad-{uid}-{si}"
311
+ x1="0"
312
+ y1="0"
313
+ x2="0"
314
+ y2={dims.innerHeight}
315
+ gradientUnits="userSpaceOnUse"
316
+ >
317
+ <!-- The gradient top stop is fillOpacity + 0.3 (clamped to 1), giving a richer
318
+ anchor at the top that fades to transparent at the bottom. This intentionally
319
+ exceeds the base fillOpacity so that gradient-fill areas appear more vivid
320
+ than their solid-fill counterparts (where fill-opacity equals fillOpacity). -->
321
+ <stop
322
+ offset="0%"
323
+ stop-color={area.color}
324
+ stop-opacity={Math.min(
325
+ (hovered?.si === si ? fillOpacity + 0.2 : fillOpacity) + 0.3,
326
+ 1
327
+ )}
328
+ />
329
+ <stop offset="100%" stop-color={area.color} stop-opacity={0} />
330
+ </linearGradient>
331
+ {/each}
332
+ </defs>
333
+ {/if}
276
334
  <g transform="translate({dims.margin.left}, {dims.margin.top})">
277
335
  {#if showYAxis}
278
336
  <Axis
@@ -295,8 +353,8 @@
295
353
  class="area-fill"
296
354
  class:dimmed={hovered !== null && hovered.si !== si}
297
355
  d={area.areaD}
298
- fill={area.color}
299
- fill-opacity={hovered?.si === si ? fillOpacity + 0.2 : fillOpacity}
356
+ fill={gradientFill ? `url(#area-grad-${uid}-${si})` : area.color}
357
+ fill-opacity={gradientFill ? 1 : hovered?.si === si ? fillOpacity + 0.2 : fillOpacity}
300
358
  />
301
359
  {#if showLine}
302
360
  <path
@@ -308,6 +366,16 @@
308
366
  fill="none"
309
367
  />
310
368
  {/if}
369
+ {#if area.points.length === 1 && !showDots}
370
+ <circle
371
+ class="single-point"
372
+ class:dimmed={hovered !== null && hovered.si !== si}
373
+ cx={area.points[0].x}
374
+ cy={area.points[0].y}
375
+ r={6}
376
+ fill={area.color}
377
+ />
378
+ {/if}
311
379
  {#if showDots}
312
380
  {#each area.points as point, pi (pi)}
313
381
  <circle
@@ -385,6 +453,12 @@
385
453
  .area-line.dimmed {
386
454
  opacity: var(--areachart-dimmed-opacity, 0.1);
387
455
  }
456
+ .single-point {
457
+ pointer-events: none;
458
+ }
459
+ .single-point.dimmed {
460
+ opacity: var(--areachart-dimmed-opacity, 0.1);
461
+ }
388
462
  .dot {
389
463
  transition:
390
464
  r var(--chart-transition-duration, 0.2s) ease,
@@ -400,9 +474,9 @@
400
474
  pointer-events: none;
401
475
  }
402
476
  .hover-line {
403
- stroke: var(--linechart-hover-line-color, #ccc);
477
+ stroke: var(--areachart-hover-line-color, var(--linechart-hover-line-color, #ccc));
404
478
  stroke-width: 1;
405
- stroke-dasharray: 4 4;
479
+ stroke-dasharray: var(--areachart-hover-line-dash, var(--linechart-hover-line-dash, 4 4));
406
480
  pointer-events: none;
407
481
  }
408
482
  .hover-overlay {
@@ -28,6 +28,7 @@ export type OptionalAreaChartProperties = {
28
28
  stacked?: boolean;
29
29
  stackNormalize?: boolean;
30
30
  fillOpacity?: number;
31
+ gradientFill?: boolean;
31
32
  showDots?: boolean;
32
33
  showLine?: boolean;
33
34
  showValues?: boolean;
@@ -1,21 +1,30 @@
1
1
  <script lang="ts">
2
2
  import type { LineChartProperties, LineChartTooltipContext } from './properties';
3
+ import { onMount } from 'svelte';
3
4
  import ChartContainer from '../_chart/ChartContainer.svelte';
4
5
  import Axis from '../_chart/Axis.svelte';
5
6
  import ChartTooltip from '../_chart/ChartTooltip.svelte';
6
7
  import Legend from '../_chart/Legend.svelte';
7
8
  import { createLinearScale, niceLinearDomain } from '../_chart/scales';
8
9
  import { computeChartDimensions } from '../_chart/geometry';
9
- import { linePath } from '../_chart/paths';
10
+ import { linePath, areaPath } from '../_chart/paths';
10
11
  import { getColor } from '../_chart/colors';
11
12
  import { formatNumber } from '../_chart/format';
12
13
  import type { LegendItem, Point } from '../_chart/types';
13
14
 
15
+ // Per-instance ID for SVG gradient <linearGradient id> references. Initialised
16
+ // inside onMount so the value is only ever generated on the client — avoids an
17
+ // SSR hydration mismatch that would occur if Math.random() ran on both server
18
+ // and client and produced different strings.
19
+ let uid = $state('');
20
+
14
21
  // ── Props ──────────────────────────────────────────────────────
15
22
 
16
23
  let {
17
24
  series,
18
25
  curve = 'monotone',
26
+ gradientFill = false,
27
+ fillOpacity = 0.3,
19
28
  showDots = true,
20
29
  showValues = false,
21
30
  dotRadius = 4,
@@ -48,6 +57,10 @@
48
57
  let mouseX = $state(0);
49
58
  let mouseY = $state(0);
50
59
 
60
+ onMount(() => {
61
+ uid = Math.random().toString(36).slice(2, 9);
62
+ });
63
+
51
64
  // ── Layout ─────────────────────────────────────────────────────
52
65
 
53
66
  let dims = $derived(computeChartDimensions(chartWidth, chartHeight));
@@ -82,7 +95,12 @@
82
95
  series.map((s, si) => {
83
96
  const color = s.color ?? getColor(si);
84
97
  const points: Point[] = s.data.map((d) => ({ x: xScale(d.x), y: yScale(d.y) }));
85
- return { color, points, path: linePath(points, curve) };
98
+ return {
99
+ color,
100
+ points,
101
+ path: linePath(points, curve),
102
+ areaD: areaPath(points, dims.innerHeight, curve)
103
+ };
86
104
  })
87
105
  );
88
106
 
@@ -123,7 +141,7 @@
123
141
  return null;
124
142
  }
125
143
  return {
126
- title: `x: ${formatNumber(tooltipContext.x)}`,
144
+ title: xTickFormat ? xTickFormat(tooltipContext.x) : `x: ${formatNumber(tooltipContext.x)}`,
127
145
  items: tooltipContext.points.map((p) => ({
128
146
  label: p.name,
129
147
  value: formatNumber(p.y),
@@ -227,6 +245,34 @@
227
245
  {/if}
228
246
 
229
247
  <ChartContainer bind:width={chartWidth} bind:height={chartHeight} {aspectRatio}>
248
+ {#if gradientFill}
249
+ <defs>
250
+ {#each lines as line, si (si)}
251
+ <linearGradient
252
+ id="line-grad-{uid}-{si}"
253
+ x1="0"
254
+ y1="0"
255
+ x2="0"
256
+ y2={dims.innerHeight}
257
+ gradientUnits="userSpaceOnUse"
258
+ >
259
+ <!-- The gradient top stop is fillOpacity + 0.3 (clamped to 1), giving a richer
260
+ anchor at the top that fades to transparent at the bottom. This intentionally
261
+ exceeds the base fillOpacity so that gradient-fill areas appear more vivid
262
+ than a flat solid-fill at fillOpacity alone. -->
263
+ <stop
264
+ offset="0%"
265
+ stop-color={line.color}
266
+ stop-opacity={Math.min(
267
+ (hovered?.si === si ? fillOpacity + 0.2 : fillOpacity) + 0.3,
268
+ 1
269
+ )}
270
+ />
271
+ <stop offset="100%" stop-color={line.color} stop-opacity={0} />
272
+ </linearGradient>
273
+ {/each}
274
+ </defs>
275
+ {/if}
230
276
  <g transform="translate({dims.margin.left}, {dims.margin.top})">
231
277
  {#if showYAxis}
232
278
  <Axis
@@ -245,6 +291,14 @@
245
291
  {/if}
246
292
 
247
293
  {#each lines as line, si (si)}
294
+ {#if gradientFill}
295
+ <path
296
+ class="line-area-fill"
297
+ class:dimmed={hovered !== null && hovered.si !== si}
298
+ d={line.areaD}
299
+ fill="url(#line-grad-{uid}-{si})"
300
+ />
301
+ {/if}
248
302
  <path
249
303
  class="line-path"
250
304
  class:dimmed={hovered !== null && hovered.si !== si}
@@ -253,6 +307,16 @@
253
307
  stroke-width={strokeWidth}
254
308
  fill="none"
255
309
  />
310
+ {#if line.points.length === 1 && !showDots}
311
+ <circle
312
+ class="single-point"
313
+ class:dimmed={hovered !== null && hovered.si !== si}
314
+ cx={line.points[0].x}
315
+ cy={line.points[0].y}
316
+ r={dotRadius * 1.5}
317
+ fill={line.color}
318
+ />
319
+ {/if}
256
320
  {#if showDots}
257
321
  {#each line.points as point, pi (pi)}
258
322
  <circle
@@ -313,6 +377,15 @@
313
377
  width: 100%;
314
378
  position: relative;
315
379
  }
380
+ .line-area-fill {
381
+ transition:
382
+ fill-opacity var(--chart-transition-duration, 0.2s) ease,
383
+ opacity var(--chart-transition-duration, 0.2s) ease;
384
+ pointer-events: none;
385
+ }
386
+ .line-area-fill.dimmed {
387
+ opacity: var(--linechart-dimmed-opacity, 0.2);
388
+ }
316
389
  .line-path {
317
390
  transition: opacity var(--chart-transition-duration, 0.2s) ease;
318
391
  stroke-linecap: round;
@@ -322,6 +395,12 @@
322
395
  .line-path.dimmed {
323
396
  opacity: var(--linechart-dimmed-opacity, 0.2);
324
397
  }
398
+ .single-point {
399
+ pointer-events: none;
400
+ }
401
+ .single-point.dimmed {
402
+ opacity: var(--linechart-dimmed-opacity, 0.2);
403
+ }
325
404
  .dot {
326
405
  transition:
327
406
  r var(--chart-transition-duration, 0.2s) ease,
@@ -25,6 +25,8 @@ export type MandatoryLineChartProperties = {
25
25
  };
26
26
  export type OptionalLineChartProperties = {
27
27
  curve?: CurveType;
28
+ gradientFill?: boolean;
29
+ fillOpacity?: number;
28
30
  showDots?: boolean;
29
31
  showValues?: boolean;
30
32
  dotRadius?: number;
@@ -0,0 +1,170 @@
1
+ <script lang="ts">
2
+ import type { StatCardProperties } from './properties';
3
+
4
+ let {
5
+ title,
6
+ value,
7
+ delta,
8
+ deltaPositive,
9
+ subtitle,
10
+ footer,
11
+ valueSnippet,
12
+ testId,
13
+ classes,
14
+ onclick
15
+ }: StatCardProperties = $props();
16
+
17
+ const isInteractive = $derived(typeof onclick === 'function');
18
+
19
+ /**
20
+ * When `deltaPositive` is explicitly provided use it; otherwise infer from
21
+ * the leading character of `delta` — '+' → positive, '-' → negative,
22
+ * anything else (e.g. '0%') → undefined (neutral, no colour applied).
23
+ */
24
+ const resolvedDeltaPositive = $derived(
25
+ typeof deltaPositive === 'boolean'
26
+ ? deltaPositive
27
+ : typeof delta === 'string' && delta.trim().length > 0
28
+ ? delta.trimStart().startsWith('+')
29
+ ? true
30
+ : delta.trimStart().startsWith('-')
31
+ ? false
32
+ : null
33
+ : null
34
+ );
35
+
36
+ const hasDelta = $derived(typeof delta === 'string' && delta.trim().length > 0);
37
+
38
+ const handleKeydown = (event: KeyboardEvent): void => {
39
+ if (event.key === 'Enter' || event.key === ' ') {
40
+ if (event.key === ' ') {
41
+ event.preventDefault();
42
+ }
43
+ if (event.currentTarget instanceof HTMLElement) {
44
+ event.currentTarget.click();
45
+ }
46
+ }
47
+ };
48
+ </script>
49
+
50
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
51
+ <div
52
+ class="statcard {classes ?? ''}"
53
+ class:statcard-interactive={isInteractive}
54
+ data-pw={typeof testId === 'string' ? testId : null}
55
+ role={isInteractive ? 'button' : null}
56
+ tabindex={isInteractive ? 0 : null}
57
+ onclick={isInteractive ? onclick : null}
58
+ onkeydown={isInteractive ? handleKeydown : null}
59
+ >
60
+ {#if typeof title === 'string' && title.length > 0}
61
+ <div class="statcard-title">{title}</div>
62
+ {/if}
63
+
64
+ <div class="statcard-value-row">
65
+ {#if typeof valueSnippet === 'function'}
66
+ <div class="statcard-value">{@render valueSnippet()}</div>
67
+ {:else if typeof value === 'string' && value.length > 0}
68
+ <div class="statcard-value">{value}</div>
69
+ {/if}
70
+
71
+ {#if hasDelta}
72
+ <div
73
+ class="statcard-delta"
74
+ class:statcard-delta-positive={resolvedDeltaPositive === true}
75
+ class:statcard-delta-negative={resolvedDeltaPositive === false}
76
+ >
77
+ {delta}
78
+ </div>
79
+ {/if}
80
+ </div>
81
+
82
+ {#if typeof subtitle === 'string' && subtitle.length > 0}
83
+ <div class="statcard-subtitle">{subtitle}</div>
84
+ {/if}
85
+
86
+ {#if typeof footer === 'function'}
87
+ <div class="statcard-footer">{@render footer()}</div>
88
+ {/if}
89
+ </div>
90
+
91
+ <style>
92
+ .statcard {
93
+ display: flex;
94
+ flex-direction: column;
95
+ gap: var(--statcard-gap, 4px);
96
+ padding: var(--statcard-padding, 16px);
97
+ background: var(--statcard-background, #ffffff);
98
+ border: var(--statcard-border, 1px solid #e5e7eb);
99
+ border-radius: var(--statcard-border-radius, 8px);
100
+ box-shadow: var(--statcard-box-shadow, none);
101
+ width: var(--statcard-width, auto);
102
+ min-width: var(--statcard-min-width, 0);
103
+ max-width: var(--statcard-max-width, none);
104
+ height: var(--statcard-height, auto);
105
+ color: var(--statcard-color, inherit);
106
+ cursor: var(--statcard-cursor, default);
107
+ box-sizing: border-box;
108
+ }
109
+
110
+ .statcard-interactive {
111
+ cursor: var(--statcard-cursor, pointer);
112
+ }
113
+
114
+ .statcard-interactive:focus-visible {
115
+ outline: var(--statcard-focus-outline, 2px solid currentColor);
116
+ outline-offset: var(--statcard-focus-outline-offset, 2px);
117
+ }
118
+
119
+ .statcard-title {
120
+ font-size: var(--statcard-title-font-size, 12px);
121
+ font-weight: var(--statcard-title-font-weight, 500);
122
+ color: var(--statcard-title-color, #6b7280);
123
+ line-height: var(--statcard-title-line-height, 1.4);
124
+ white-space: var(--statcard-title-white-space, nowrap);
125
+ overflow: hidden;
126
+ text-overflow: ellipsis;
127
+ }
128
+
129
+ .statcard-value-row {
130
+ display: flex;
131
+ align-items: var(--statcard-value-row-align, baseline);
132
+ gap: var(--statcard-value-row-gap, 8px);
133
+ flex-wrap: wrap;
134
+ }
135
+
136
+ .statcard-value {
137
+ font-size: var(--statcard-value-font-size, 24px);
138
+ font-weight: var(--statcard-value-font-weight, 600);
139
+ color: var(--statcard-value-color, inherit);
140
+ line-height: var(--statcard-value-line-height, 1.2);
141
+ }
142
+
143
+ .statcard-delta {
144
+ font-size: var(--statcard-delta-font-size, 13px);
145
+ font-weight: var(--statcard-delta-font-weight, 500);
146
+ color: var(--statcard-delta-color, #6b7280);
147
+ line-height: var(--statcard-delta-line-height, 1.4);
148
+ }
149
+
150
+ .statcard-delta-positive {
151
+ color: var(--statcard-delta-positive-color, #16a34a);
152
+ }
153
+
154
+ .statcard-delta-negative {
155
+ color: var(--statcard-delta-negative-color, #dc2626);
156
+ }
157
+
158
+ .statcard-subtitle {
159
+ font-size: var(--statcard-subtitle-font-size, 12px);
160
+ font-weight: var(--statcard-subtitle-font-weight, 400);
161
+ color: var(--statcard-subtitle-color, #9ca3af);
162
+ line-height: var(--statcard-subtitle-line-height, 1.4);
163
+ }
164
+
165
+ .statcard-footer {
166
+ margin-top: var(--statcard-footer-margin-top, 8px);
167
+ padding-top: var(--statcard-footer-padding-top, 8px);
168
+ border-top: var(--statcard-footer-border-top, 1px solid #e5e7eb);
169
+ }
170
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { StatCardProperties } from './properties';
2
+ declare const StatCard: import("svelte").Component<StatCardProperties, {}, "">;
3
+ type StatCard = ReturnType<typeof StatCard>;
4
+ export default StatCard;
@@ -0,0 +1,27 @@
1
+ import type { Snippet } from 'svelte';
2
+ export type StatCardProperties = OptionalStatCardProperties & StatCardEventProperties;
3
+ export type MandatoryStatCardProperties = Record<string, never>;
4
+ export type OptionalStatCardProperties = {
5
+ /** Card heading label. */
6
+ title?: string;
7
+ /** Pre-formatted metric value string (e.g. "₹1.23Cr", "98.4%"). */
8
+ value?: string;
9
+ /** Pre-formatted delta string (e.g. "+12.5%", "-3.2%"). Auto-infers `deltaPositive` from leading sign when `deltaPositive` is not provided. */
10
+ delta?: string;
11
+ /** Overrides automatic sign-based inference for delta colour. `true` = positive (green), `false` = negative (red). */
12
+ deltaPositive?: boolean;
13
+ /** Secondary label rendered below the value row. */
14
+ subtitle?: string;
15
+ /** Snippet rendered in the card footer area. */
16
+ footer?: Snippet;
17
+ /** Replaces the string `value` with a custom snippet for advanced value rendering. */
18
+ valueSnippet?: Snippet;
19
+ /** Renders as `data-pw` on the root element for Playwright test selection. */
20
+ testId?: string;
21
+ /** Extra CSS class names appended to the root element. */
22
+ classes?: string;
23
+ };
24
+ export type StatCardEventProperties = {
25
+ /** Makes the card interactive: adds `role="button"`, `tabindex=0`, and wires click/Enter/Space. */
26
+ onclick?: (event: MouseEvent) => void;
27
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -118,6 +118,7 @@ export function linePath(points, curve = 'linear') {
118
118
  return points.length === 1 ? `M ${points[0].x} ${points[0].y}` : '';
119
119
  }
120
120
  switch (curve) {
121
+ case 'spline':
121
122
  case 'monotone':
122
123
  case 'natural':
123
124
  return monotonePath(points);
@@ -31,7 +31,7 @@ export type BandScale = {
31
31
  bandwidth: number;
32
32
  step: number;
33
33
  };
34
- export type CurveType = 'linear' | 'monotone' | 'step' | 'natural';
34
+ export type CurveType = 'linear' | 'monotone' | 'spline' | 'step' | 'natural';
35
35
  export type AxisOrientation = 'top' | 'right' | 'bottom' | 'left';
36
36
  export type PieSliceLayout = {
37
37
  index: number;
package/dist/index.d.ts CHANGED
@@ -66,6 +66,7 @@ export { default as BarChart } from './BarChart/BarChart.svelte';
66
66
  export { default as PieChart } from './PieChart/PieChart.svelte';
67
67
  export { default as SankeyChart } from './SankeyChart/SankeyChart.svelte';
68
68
  export { default as LottiePlayer } from './LottiePlayer/LottiePlayer.svelte';
69
+ export { default as StatCard } from './StatCard/StatCard.svelte';
69
70
  export type * from './Button/properties';
70
71
  export type * from './Modal/properties';
71
72
  export type * from './Input/properties';
@@ -128,5 +129,6 @@ export type * from './BarChart/properties';
128
129
  export type * from './PieChart/properties';
129
130
  export type * from './SankeyChart/properties';
130
131
  export type * from './LottiePlayer/properties';
132
+ export type * from './StatCard/properties';
131
133
  export { validateInput } from './utils';
132
134
  export { formatNumberIndian } from './_chart/format';
package/dist/index.js CHANGED
@@ -66,5 +66,6 @@ export { default as BarChart } from './BarChart/BarChart.svelte';
66
66
  export { default as PieChart } from './PieChart/PieChart.svelte';
67
67
  export { default as SankeyChart } from './SankeyChart/SankeyChart.svelte';
68
68
  export { default as LottiePlayer } from './LottiePlayer/LottiePlayer.svelte';
69
+ export { default as StatCard } from './StatCard/StatCard.svelte';
69
70
  export { validateInput } from './utils';
70
71
  export { formatNumberIndian } from './_chart/format';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/svelte-ui-components",
3
- "version": "2.71.0",
3
+ "version": "2.73.0",
4
4
  "description": "A themeable Svelte 5 UI component library with CSS custom property driven styling",
5
5
  "keywords": [
6
6
  "svelte",