@juspay/svelte-ui-components 2.106.0 → 2.106.2
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.
- package/dist/LineChart/LineChart.svelte +43 -20
- package/dist/_chart/paths.js +40 -3
- package/dist/_chart/scales.js +33 -5
- package/package.json +1 -1
|
@@ -131,9 +131,12 @@
|
|
|
131
131
|
if (xDomain) {
|
|
132
132
|
return xDomain;
|
|
133
133
|
}
|
|
134
|
+
// Non-finite coordinates mark gap points in sparse series; they must not
|
|
135
|
+
// poison the auto-domain (Math.min/max propagate NaN).
|
|
134
136
|
const allX = series
|
|
135
137
|
.filter((_, si) => !hiddenSeries.has(si))
|
|
136
|
-
.flatMap((s) => s.data.map((d) => d.x))
|
|
138
|
+
.flatMap((s) => s.data.map((d) => d.x))
|
|
139
|
+
.filter((x) => Number.isFinite(x));
|
|
137
140
|
if (allX.length === 0) {
|
|
138
141
|
return [0, 1];
|
|
139
142
|
}
|
|
@@ -145,7 +148,8 @@
|
|
|
145
148
|
}
|
|
146
149
|
const allY = series
|
|
147
150
|
.filter((_, si) => !hiddenSeries.has(si))
|
|
148
|
-
.flatMap((s) => s.data.map((d) => d.y))
|
|
151
|
+
.flatMap((s) => s.data.map((d) => d.y))
|
|
152
|
+
.filter((y) => Number.isFinite(y));
|
|
149
153
|
if (allY.length === 0) {
|
|
150
154
|
return [0, 1];
|
|
151
155
|
}
|
|
@@ -170,7 +174,8 @@
|
|
|
170
174
|
});
|
|
171
175
|
|
|
172
176
|
let yTickCount = $derived(Math.max(2, Math.min(6, Math.floor(chartHeight / 70))));
|
|
173
|
-
|
|
177
|
+
// Capped at 6 per the design-system line-chart spec ("Max no. of ticks should be 6").
|
|
178
|
+
let xTickCount = $derived(Math.max(2, Math.min(6, Math.floor(chartWidth / 90))));
|
|
174
179
|
// Category positions are whole numbers — fractional tick steps would repeat labels.
|
|
175
180
|
let xIntegerTicks = $derived(Boolean(xAxisCategories && xAxisCategories.length > 0));
|
|
176
181
|
|
|
@@ -222,9 +227,14 @@
|
|
|
222
227
|
hovered === null ? null : (series[hovered.si]?.data[hovered.pi] ?? null)
|
|
223
228
|
);
|
|
224
229
|
|
|
225
|
-
let hoverLineX = $derived(
|
|
226
|
-
|
|
227
|
-
|
|
230
|
+
let hoverLineX = $derived.by<number | null>(() => {
|
|
231
|
+
if (hovered === null) {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
const x = lines[hovered.si]?.points[hovered.pi]?.x ?? null;
|
|
235
|
+
// A gap point (non-finite) anchors no crosshair.
|
|
236
|
+
return x !== null && Number.isFinite(x) ? x : null;
|
|
237
|
+
});
|
|
228
238
|
|
|
229
239
|
// When a highlight index is active (imperative or prop), show the vertical
|
|
230
240
|
// crosshair at that point even without a mouse hover.
|
|
@@ -238,7 +248,7 @@
|
|
|
238
248
|
continue;
|
|
239
249
|
}
|
|
240
250
|
const point = line.points[effectiveHighlight];
|
|
241
|
-
if (point) {
|
|
251
|
+
if (point && Number.isFinite(point.x)) {
|
|
242
252
|
return point.x;
|
|
243
253
|
}
|
|
244
254
|
}
|
|
@@ -280,7 +290,9 @@
|
|
|
280
290
|
({ si }) =>
|
|
281
291
|
!hiddenSeries.has(si) &&
|
|
282
292
|
(shared || si === hovered?.si) &&
|
|
283
|
-
series[si]?.data[hovered!.pi] != null
|
|
293
|
+
series[si]?.data[hovered!.pi] != null &&
|
|
294
|
+
// A gap point (non-finite y) has nothing to report in the tooltip.
|
|
295
|
+
Number.isFinite(series[si].data[hovered!.pi].y)
|
|
284
296
|
)
|
|
285
297
|
.map(({ p }) => ({ label: p.name, value: formatNumber(p.y), color: p.color }))
|
|
286
298
|
};
|
|
@@ -296,7 +308,10 @@
|
|
|
296
308
|
return [];
|
|
297
309
|
}
|
|
298
310
|
const p = line.points[hovered!.pi];
|
|
299
|
-
|
|
311
|
+
// A gap point (non-finite) has no marker, so it gets no halo either.
|
|
312
|
+
return p && Number.isFinite(p.x) && Number.isFinite(p.y)
|
|
313
|
+
? [{ x: p.x, y: p.y, color: line.color }]
|
|
314
|
+
: [];
|
|
300
315
|
});
|
|
301
316
|
});
|
|
302
317
|
|
|
@@ -408,6 +423,11 @@
|
|
|
408
423
|
let nearestPi = 0;
|
|
409
424
|
let nearestXDist = Infinity;
|
|
410
425
|
for (let i = 0; i < reference.length; i++) {
|
|
426
|
+
// Gap points (non-finite) are not hoverable — NaN distances would never
|
|
427
|
+
// win the comparison, but skipping keeps nearestPi from defaulting to one.
|
|
428
|
+
if (!Number.isFinite(reference[i].x) || !Number.isFinite(reference[i].y)) {
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
411
431
|
const px = xScale(reference[i].x);
|
|
412
432
|
const dist = Math.abs(px - plotX);
|
|
413
433
|
if (dist < nearestXDist) {
|
|
@@ -619,7 +639,7 @@
|
|
|
619
639
|
stroke-width={strokeWidth}
|
|
620
640
|
fill="none"
|
|
621
641
|
/>
|
|
622
|
-
{#if line.points.length === 1 && !showDots}
|
|
642
|
+
{#if line.points.length === 1 && !showDots && Number.isFinite(line.points[0].x) && Number.isFinite(line.points[0].y)}
|
|
623
643
|
<circle
|
|
624
644
|
class="single-point"
|
|
625
645
|
class:dimmed={isLineDimmed(si)}
|
|
@@ -631,21 +651,24 @@
|
|
|
631
651
|
{/if}
|
|
632
652
|
{#if showDots}
|
|
633
653
|
{#each line.points as point, pi (pi)}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
654
|
+
<!-- Gap points (non-finite) render no marker. -->
|
|
655
|
+
{#if Number.isFinite(point.x) && Number.isFinite(point.y)}
|
|
656
|
+
<circle
|
|
657
|
+
class="dot"
|
|
658
|
+
class:dimmed={isDotDimmed(si, pi)}
|
|
659
|
+
class:highlighted={isHighlightedDot(si, pi)}
|
|
660
|
+
cx={point.x}
|
|
661
|
+
cy={point.y}
|
|
662
|
+
r={isHighlightedDot(si, pi) ? dotRadius * 1.5 : dotRadius}
|
|
663
|
+
fill={line.color}
|
|
664
|
+
/>
|
|
665
|
+
{/if}
|
|
643
666
|
{/each}
|
|
644
667
|
{/if}
|
|
645
668
|
{#if showValues && pointLabelPlacements[si]}
|
|
646
669
|
{#each line.points as _point, pi (pi)}
|
|
647
670
|
{@const pl = pointLabelPlacements[si][pi]}
|
|
648
|
-
{#if pl?.visible}
|
|
671
|
+
{#if pl?.visible && Number.isFinite(pl.x) && Number.isFinite(pl.y)}
|
|
649
672
|
<text
|
|
650
673
|
class="point-value"
|
|
651
674
|
x={pl.x}
|
package/dist/_chart/paths.js
CHANGED
|
@@ -30,6 +30,31 @@ export function arcPath(cx, cy, innerR, outerR, startAngle, endAngle) {
|
|
|
30
30
|
}
|
|
31
31
|
return `M ${cx} ${cy}` + `L ${x1} ${y1}` + `A ${outerR} ${outerR} 0 ${largeArc} 1 ${x2} ${y2}Z`;
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Splits a point run into finite sub-runs. A non-finite coordinate (NaN /
|
|
35
|
+
* Infinity — typically a missing value in a sparse series) acts as a break:
|
|
36
|
+
* the line renders as separate segments with a gap at the missing point
|
|
37
|
+
* instead of one poisoned path — a single NaN coordinate in `d` makes the
|
|
38
|
+
* browser drop everything from that command onward, visually cutting the
|
|
39
|
+
* line mid-chart.
|
|
40
|
+
*/
|
|
41
|
+
function finiteSegments(points) {
|
|
42
|
+
const segments = [];
|
|
43
|
+
let current = [];
|
|
44
|
+
for (const point of points) {
|
|
45
|
+
if (Number.isFinite(point.x) && Number.isFinite(point.y)) {
|
|
46
|
+
current.push(point);
|
|
47
|
+
}
|
|
48
|
+
else if (current.length > 0) {
|
|
49
|
+
segments.push(current);
|
|
50
|
+
current = [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (current.length > 0) {
|
|
54
|
+
segments.push(current);
|
|
55
|
+
}
|
|
56
|
+
return segments;
|
|
57
|
+
}
|
|
33
58
|
function linearPath(points) {
|
|
34
59
|
if (points.length === 0) {
|
|
35
60
|
return '';
|
|
@@ -113,7 +138,7 @@ function monotonePath(points) {
|
|
|
113
138
|
}
|
|
114
139
|
return d;
|
|
115
140
|
}
|
|
116
|
-
|
|
141
|
+
function linePathSegment(points, curve) {
|
|
117
142
|
if (points.length < 2) {
|
|
118
143
|
return points.length === 1 ? `M ${points[0].x} ${points[0].y}` : '';
|
|
119
144
|
}
|
|
@@ -128,15 +153,27 @@ export function linePath(points, curve = 'linear') {
|
|
|
128
153
|
return linearPath(points);
|
|
129
154
|
}
|
|
130
155
|
}
|
|
131
|
-
export function
|
|
156
|
+
export function linePath(points, curve = 'linear') {
|
|
157
|
+
return finiteSegments(points)
|
|
158
|
+
.map((segment) => linePathSegment(segment, curve))
|
|
159
|
+
.filter((d) => d !== '')
|
|
160
|
+
.join(' ');
|
|
161
|
+
}
|
|
162
|
+
function areaPathSegment(points, baseline, curve) {
|
|
132
163
|
if (points.length === 0) {
|
|
133
164
|
return '';
|
|
134
165
|
}
|
|
135
|
-
const topPath =
|
|
166
|
+
const topPath = linePathSegment(points, curve);
|
|
136
167
|
const lastPoint = points[points.length - 1];
|
|
137
168
|
const firstPoint = points[0];
|
|
138
169
|
return topPath + ` L ${lastPoint.x} ${baseline} L ${firstPoint.x} ${baseline} Z`;
|
|
139
170
|
}
|
|
171
|
+
export function areaPath(points, baseline, curve = 'linear') {
|
|
172
|
+
return finiteSegments(points)
|
|
173
|
+
.map((segment) => areaPathSegment(segment, baseline, curve))
|
|
174
|
+
.filter((d) => d !== '')
|
|
175
|
+
.join(' ');
|
|
176
|
+
}
|
|
140
177
|
/**
|
|
141
178
|
* Builds an SVG path string for a rectangle with independently-controlled
|
|
142
179
|
* corner radii. Each radius is clamped to Math.min(r, w/2, h/2) so the
|
package/dist/_chart/scales.js
CHANGED
|
@@ -36,11 +36,39 @@ export function computeLinearTicks(domain, count = 5, integer = false) {
|
|
|
36
36
|
if (integer && step < 1) {
|
|
37
37
|
step = 1;
|
|
38
38
|
}
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
const buildTicks = (tickStep) => {
|
|
40
|
+
const ticks = [];
|
|
41
|
+
let tick = Math.ceil(min / tickStep) * tickStep;
|
|
42
|
+
while (tick <= max + tickStep * 0.001) {
|
|
43
|
+
ticks.push(Math.round(tick * 1e10) / 1e10);
|
|
44
|
+
tick += tickStep;
|
|
45
|
+
}
|
|
46
|
+
return ticks;
|
|
47
|
+
};
|
|
48
|
+
// The next rung up the 1-2-5-10 ladder, e.g. 0.2 → 0.5, 2 → 5, 5 → 10.
|
|
49
|
+
const nextNiceStep = (current) => {
|
|
50
|
+
const stepExp = Math.floor(Math.log10(current) + 1e-10);
|
|
51
|
+
const stepBase = Math.pow(10, stepExp);
|
|
52
|
+
const mantissa = current / stepBase;
|
|
53
|
+
if (mantissa < 1.5) {
|
|
54
|
+
return stepBase * 2;
|
|
55
|
+
}
|
|
56
|
+
if (mantissa < 3.5) {
|
|
57
|
+
return stepBase * 5;
|
|
58
|
+
}
|
|
59
|
+
return stepBase * 10;
|
|
60
|
+
};
|
|
61
|
+
// `count` is a hard maximum (design-system chart spec: "max 6 ticks"), not a
|
|
62
|
+
// hint — the nice-step ladder can overshoot it (range 14 at count 6 picks
|
|
63
|
+
// step 2 → 7 ticks). Escalate the step until the ticks fit: category axes
|
|
64
|
+
// step through whole numbers (any integer stride is a valid category step,
|
|
65
|
+
// so 15 days at max 6 lands on the natural every-3rd-day), numeric axes
|
|
66
|
+
// climb the ladder so tick values stay round.
|
|
67
|
+
let ticks = buildTicks(step);
|
|
68
|
+
const maxTicks = Math.max(2, count);
|
|
69
|
+
while (ticks.length > maxTicks) {
|
|
70
|
+
step = integer ? Math.floor(step) + 1 : nextNiceStep(step);
|
|
71
|
+
ticks = buildTicks(step);
|
|
44
72
|
}
|
|
45
73
|
return ticks;
|
|
46
74
|
}
|