@juspay/svelte-ui-components 2.102.0 → 2.104.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.
|
@@ -36,7 +36,8 @@
|
|
|
36
36
|
nodeColorResolver,
|
|
37
37
|
minLinkWidth = 1,
|
|
38
38
|
dataLabelOffsetX = 0,
|
|
39
|
-
disableDimOnHover = false
|
|
39
|
+
disableDimOnHover = false,
|
|
40
|
+
firstColumnLabelSide = 'left'
|
|
40
41
|
}: SankeyChartProperties = $props();
|
|
41
42
|
|
|
42
43
|
// ── State ──────────────────────────────────────────────────────
|
|
@@ -54,20 +55,44 @@
|
|
|
54
55
|
let format = $derived(valueFormat ?? formatNumber);
|
|
55
56
|
let isEmpty = $derived(nodes.length === 0);
|
|
56
57
|
const MARGIN = 40;
|
|
57
|
-
// A 12px label's rendered line box measures ~16px (≈1.33em) across common
|
|
58
|
-
// font stacks; two label centres closer than this overlap visibly.
|
|
59
|
-
const LABEL_LINE_PX = 16;
|
|
60
58
|
|
|
61
59
|
// Real text measurement via the shared canvas-backed helper (exact on the
|
|
62
60
|
// client, 0.6em/char heuristic under SSR/tests). Character estimates used
|
|
63
61
|
// to both over-reserve the right label gutter (dead canvas) and under-budget
|
|
64
62
|
// uppercase-heavy labels (text sliding under the next column's bars).
|
|
63
|
+
// Weight and family are part of the spec: a 700-weight label is measurably
|
|
64
|
+
// wider than a 400 one, and the app's rendered font rarely matches the
|
|
65
|
+
// measurement default — budgets must be computed at the rendered style or
|
|
66
|
+
// borderline labels truncate (or overflow) for no visible reason.
|
|
67
|
+
// Family only enters the spec once the document's fonts have loaded: measuring
|
|
68
|
+
// a not-yet-loaded webfont silently measures its fallback (usually wider) and
|
|
69
|
+
// the width cache would pin that stale value under the loaded font's key.
|
|
70
|
+
// Until then the measurement default applies — same behaviour as before.
|
|
71
|
+
// One-shot promise subscription at init (browser only) — nothing to tear
|
|
72
|
+
// down, and the $state write re-derives every measurement consumer.
|
|
73
|
+
let fontsLoaded = $state(typeof document !== 'undefined' && document.fonts?.status === 'loaded');
|
|
74
|
+
if (typeof document !== 'undefined' && document.fonts?.status !== 'loaded') {
|
|
75
|
+
document.fonts?.ready.then(() => {
|
|
76
|
+
fontsLoaded = true;
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
let chartFontFamily = $derived(
|
|
80
|
+
fontsLoaded && containerEl ? getComputedStyle(containerEl).fontFamily : null
|
|
81
|
+
);
|
|
65
82
|
let labelFont = $derived({
|
|
66
|
-
size: containerEl ? readCssVarPx(containerEl, '--sankey-label-font-size', 12) : 12
|
|
83
|
+
size: containerEl ? readCssVarPx(containerEl, '--sankey-label-font-size', 12) : 12,
|
|
84
|
+
weight: containerEl ? readCssVarPx(containerEl, '--sankey-label-font-weight', 400) : 400,
|
|
85
|
+
family: chartFontFamily
|
|
67
86
|
});
|
|
68
87
|
let colLabelFont = $derived({
|
|
69
|
-
size: containerEl ? readCssVarPx(containerEl, '--sankey-col-label-font-size', 11) : 11
|
|
88
|
+
size: containerEl ? readCssVarPx(containerEl, '--sankey-col-label-font-size', 11) : 11,
|
|
89
|
+
weight: containerEl ? readCssVarPx(containerEl, '--sankey-col-label-font-weight', 400) : 400,
|
|
90
|
+
family: chartFontFamily
|
|
70
91
|
});
|
|
92
|
+
// A label's rendered line box measures ≈1.33em across common font stacks;
|
|
93
|
+
// two label centres closer than this overlap visibly. Derived from the
|
|
94
|
+
// tokened size so consumers that scale labels keep honest de-collision.
|
|
95
|
+
let labelLinePx = $derived(Math.ceil(labelFont.size * 1.33));
|
|
71
96
|
|
|
72
97
|
// Final-column labels render to the RIGHT of their node; the bare 40px margin is
|
|
73
98
|
// nowhere near enough for real funnel labels ("PARTIALLY_FAILED (1,234)"), so they
|
|
@@ -101,8 +126,11 @@
|
|
|
101
126
|
// the room falls below an ellipsis, vanish entirely. Mirror the sink gutter:
|
|
102
127
|
// reserve a capped left gutter sized from the source-node labels (sources are what
|
|
103
128
|
// land in the first column) and shift the diagram right by it.
|
|
129
|
+
// With `firstColumnLabelSide: 'right'` the labels render over the outgoing
|
|
130
|
+
// ribbons like every other column, so no gutter is reserved at all and the
|
|
131
|
+
// diagram gains that width.
|
|
104
132
|
let firstColumnLabelGutter = $derived.by(() => {
|
|
105
|
-
if (!showLabels || nodes.length === 0 || chartWidth <= 0) {
|
|
133
|
+
if (firstColumnLabelSide === 'right' || !showLabels || nodes.length === 0 || chartWidth <= 0) {
|
|
106
134
|
return 0;
|
|
107
135
|
}
|
|
108
136
|
const targetIds = new Set(links.map((link) => link.target));
|
|
@@ -184,7 +212,7 @@
|
|
|
184
212
|
// SVG's left edge is that gutter plus the base margin, minus the inset —
|
|
185
213
|
// symmetric with the last column's sink-gutter budget below.
|
|
186
214
|
const available =
|
|
187
|
-
column === 0
|
|
215
|
+
column === 0 && firstColumnLabelSide === 'left'
|
|
188
216
|
? Math.max(0, firstColumnLabelGutter + MARGIN - 6 - dataLabelOffsetX)
|
|
189
217
|
: column === columnCount - 1
|
|
190
218
|
? Math.max(0, lastColumnLabelGutter + MARGIN - 6 - dataLabelOffsetX)
|
|
@@ -222,7 +250,7 @@
|
|
|
222
250
|
continue;
|
|
223
251
|
}
|
|
224
252
|
const centerGap = node.y + node.height / 2 - (lastKept.y + lastKept.height / 2);
|
|
225
|
-
if (centerGap <
|
|
253
|
+
if (centerGap < labelLinePx) {
|
|
226
254
|
if (node.value > lastKept.value) {
|
|
227
255
|
hidden.add(lastKept.id);
|
|
228
256
|
lastKept = node;
|
|
@@ -490,11 +518,11 @@
|
|
|
490
518
|
<text
|
|
491
519
|
class="sankey-label"
|
|
492
520
|
class:node-dimmed={dimmed}
|
|
493
|
-
x={node.column === 0
|
|
521
|
+
x={node.column === 0 && firstColumnLabelSide === 'left'
|
|
494
522
|
? node.x - 6 - dataLabelOffsetX
|
|
495
523
|
: node.x + node.width + 6 + dataLabelOffsetX}
|
|
496
524
|
y={node.y + node.height / 2}
|
|
497
|
-
text-anchor={node.column === 0 ? 'end' : 'start'}
|
|
525
|
+
text-anchor={node.column === 0 && firstColumnLabelSide === 'left' ? 'end' : 'start'}
|
|
498
526
|
dominant-baseline="middle"
|
|
499
527
|
>{truncateLabel(
|
|
500
528
|
showValues ? `${node.label} (${format(node.value)})` : node.label,
|
|
@@ -537,13 +565,22 @@
|
|
|
537
565
|
.sankey-label {
|
|
538
566
|
fill: var(--sankey-label-color, #333);
|
|
539
567
|
font-size: var(--sankey-label-font-size, 12px);
|
|
568
|
+
font-weight: var(--sankey-label-font-weight, 400);
|
|
540
569
|
font-family: var(--chart-font-family, inherit);
|
|
570
|
+
/* Highcharts-style text outline: node labels render over link ribbons, so
|
|
571
|
+
a halo in the chart's background colour keeps them legible at any flow
|
|
572
|
+
density. Off (transparent / 0) by default. */
|
|
573
|
+
paint-order: stroke;
|
|
574
|
+
stroke: var(--sankey-label-halo-color, transparent);
|
|
575
|
+
stroke-width: var(--sankey-label-halo-width, 0);
|
|
576
|
+
stroke-linejoin: round;
|
|
541
577
|
pointer-events: none;
|
|
542
578
|
transition: opacity var(--chart-transition-duration, 0.2s) ease;
|
|
543
579
|
}
|
|
544
580
|
.sankey-col-label {
|
|
545
581
|
fill: var(--sankey-col-label-color, #666);
|
|
546
582
|
font-size: var(--sankey-col-label-font-size, 11px);
|
|
583
|
+
font-weight: var(--sankey-col-label-font-weight, 400);
|
|
547
584
|
font-family: var(--chart-font-family, inherit);
|
|
548
585
|
pointer-events: none;
|
|
549
586
|
}
|
|
@@ -84,6 +84,14 @@ export type OptionalSankeyChartProperties = {
|
|
|
84
84
|
* to read the overall structure. Defaults to `false` (standard dim-on-hover behaviour).
|
|
85
85
|
*/
|
|
86
86
|
disableDimOnHover?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Which side of a first-column node its label renders on. `'left'` (default) anchors the
|
|
89
|
+
* label into a reserved left gutter outside the diagram. `'right'` renders it like every
|
|
90
|
+
* other column — to the right of the bar, over the outgoing ribbons (pair with the
|
|
91
|
+
* `--sankey-label-halo-*` tokens for legibility); no left gutter is reserved, so the
|
|
92
|
+
* diagram gains that width.
|
|
93
|
+
*/
|
|
94
|
+
firstColumnLabelSide?: 'left' | 'right';
|
|
87
95
|
};
|
|
88
96
|
export type SankeyChartEventProperties = {
|
|
89
97
|
onnodeclick?: (event: {
|
|
@@ -288,6 +288,7 @@
|
|
|
288
288
|
}
|
|
289
289
|
|
|
290
290
|
.statcard-header {
|
|
291
|
+
order: var(--statcard-header-order, 0);
|
|
291
292
|
display: flex;
|
|
292
293
|
align-items: center;
|
|
293
294
|
justify-content: space-between;
|
|
@@ -362,6 +363,7 @@
|
|
|
362
363
|
}
|
|
363
364
|
|
|
364
365
|
.statcard-value-row {
|
|
366
|
+
order: var(--statcard-value-row-order, 0);
|
|
365
367
|
display: flex;
|
|
366
368
|
align-items: var(--statcard-value-row-align, baseline);
|
|
367
369
|
gap: var(--statcard-value-row-gap, 8px);
|
|
@@ -391,6 +393,7 @@
|
|
|
391
393
|
}
|
|
392
394
|
|
|
393
395
|
.statcard-subtitle {
|
|
396
|
+
order: var(--statcard-subtitle-order, 0);
|
|
394
397
|
font-size: var(--statcard-subtitle-font-size, 12px);
|
|
395
398
|
font-weight: var(--statcard-subtitle-font-weight, 400);
|
|
396
399
|
color: var(--statcard-subtitle-color, #9ca3af);
|
|
@@ -399,6 +402,7 @@
|
|
|
399
402
|
|
|
400
403
|
/* Multi-row layout */
|
|
401
404
|
.statcard-rows {
|
|
405
|
+
order: var(--statcard-rows-order, 0);
|
|
402
406
|
display: flex;
|
|
403
407
|
flex-direction: column;
|
|
404
408
|
gap: var(--statcard-rows-gap, 0px);
|
|
@@ -473,7 +477,7 @@
|
|
|
473
477
|
font-weight: var(--statcard-breakdown-heading-font-weight, 600);
|
|
474
478
|
color: var(--statcard-breakdown-heading-color, #6b7280);
|
|
475
479
|
letter-spacing: 0.04em;
|
|
476
|
-
text-transform: uppercase;
|
|
480
|
+
text-transform: var(--statcard-breakdown-heading-text-transform, uppercase);
|
|
477
481
|
margin-top: var(--statcard-breakdown-heading-margin-top, 6px);
|
|
478
482
|
}
|
|
479
483
|
|
|
@@ -486,7 +490,9 @@
|
|
|
486
490
|
|
|
487
491
|
.statcard-breakdown-item {
|
|
488
492
|
display: flex;
|
|
489
|
-
flex-direction: column;
|
|
493
|
+
flex-direction: var(--statcard-breakdown-item-flex-direction, column);
|
|
494
|
+
align-items: var(--statcard-breakdown-item-align-items, stretch);
|
|
495
|
+
flex-wrap: var(--statcard-breakdown-item-flex-wrap, nowrap);
|
|
490
496
|
gap: var(--statcard-breakdown-item-gap, 2px);
|
|
491
497
|
}
|
|
492
498
|
|
|
@@ -494,6 +500,7 @@
|
|
|
494
500
|
as the caption). Previously the delta was a third stacked child, so it
|
|
495
501
|
rendered on its own line beneath the number. */
|
|
496
502
|
.statcard-breakdown-value-line {
|
|
503
|
+
order: var(--statcard-breakdown-value-line-order, 0);
|
|
497
504
|
display: flex;
|
|
498
505
|
align-items: baseline;
|
|
499
506
|
gap: var(--statcard-breakdown-value-gap, 6px);
|
package/dist/_chart/measure.d.ts
CHANGED