@juspay/svelte-ui-components 2.106.1 → 2.107.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.
@@ -78,6 +78,7 @@
78
78
  stackNormalize = false,
79
79
  scrollable = false,
80
80
  minBandWidth = 48,
81
+ marginX,
81
82
  tooltipSnippet,
82
83
  empty,
83
84
  renderOverlay,
@@ -309,7 +310,19 @@
309
310
  base: { top: 20, left: showYAxis ? 50 : 28, right: 28, bottom: showXAxis ? 40 : 8 }
310
311
  });
311
312
  });
312
- let dims = $derived(layout);
313
+ let dims = $derived.by(() => {
314
+ if (marginX == null) {
315
+ return layout;
316
+ }
317
+ // Fixed horizontal inset: overrides the auto layout's tick-label padding
318
+ // so the plot (and its edge bars) runs to the requested inset.
319
+ const inset = Math.max(0, marginX);
320
+ return {
321
+ ...layout,
322
+ margin: { ...layout.margin, left: inset, right: inset },
323
+ innerWidth: Math.max(0, chartWidth - inset * 2)
324
+ };
325
+ });
313
326
 
314
327
  let catScale = $derived(
315
328
  createBandScale(labels, isVertical ? [0, dims.innerWidth] : [0, dims.innerHeight], barPadding)
@@ -610,6 +623,38 @@
610
623
  }
611
624
  }
612
625
 
626
+ // Grouped/single bars round only their value end (design-system bar spec):
627
+ // a vertical bar rounds its top (bottom when the value is negative), a
628
+ // horizontal bar its right (left when negative). The old all-corner rect
629
+ // rounding let the backdrop/track behind a bar peek through the notches at
630
+ // its baseline corners. Floating [low, high] range bars round both ends.
631
+ function valueEndBarPath(bar: BarRect): string {
632
+ if (barRadius <= 0) {
633
+ return roundedRectPath(bar.x, bar.y, bar.width, bar.height, 0, 0, 0, 0);
634
+ }
635
+ if (bar.isFloating) {
636
+ return roundedRectPath(
637
+ bar.x,
638
+ bar.y,
639
+ bar.width,
640
+ bar.height,
641
+ barRadius,
642
+ barRadius,
643
+ barRadius,
644
+ barRadius
645
+ );
646
+ }
647
+ const isNegative = (bar.dataPoint.value ?? 0) < 0;
648
+ if (isVertical) {
649
+ return isNegative
650
+ ? roundedRectPath(bar.x, bar.y, bar.width, bar.height, 0, 0, barRadius, barRadius)
651
+ : roundedRectPath(bar.x, bar.y, bar.width, bar.height, barRadius, barRadius, 0, 0);
652
+ }
653
+ return isNegative
654
+ ? roundedRectPath(bar.x, bar.y, bar.width, bar.height, barRadius, 0, 0, barRadius)
655
+ : roundedRectPath(bar.x, bar.y, bar.width, bar.height, 0, barRadius, barRadius, 0);
656
+ }
657
+
613
658
  // ── Fill attribute helper ──────────────────────────────────────
614
659
 
615
660
  function barFillAttr(bar: BarRect): string {
@@ -918,7 +963,7 @@
918
963
  onclick={() => handleClick(bar)}
919
964
  />
920
965
  {:else}
921
- <rect
966
+ <path
922
967
  class="bar"
923
968
  class:hovered={hovered?.si === bar.si && hovered?.pi === bar.pi}
924
969
  class:highlighted={effectiveHighlightedIndex !== null &&
@@ -927,12 +972,7 @@
927
972
  (hovered.si !== bar.si || hovered.pi !== bar.pi)) ||
928
973
  (effectiveHighlightedIndex !== null &&
929
974
  effectiveHighlightedIndex !== bar.pi)}
930
- x={bar.x}
931
- y={bar.y}
932
- width={bar.width}
933
- height={bar.height}
934
- rx={barRadius}
935
- ry={barRadius}
975
+ d={valueEndBarPath(bar)}
936
976
  fill={barFillAttr(bar)}
937
977
  data-pw={`bar-${i}`}
938
978
  tabindex="0"
@@ -161,6 +161,14 @@ export type OptionalBarChartProperties = {
161
161
  * is `false`. Default is `48`.
162
162
  */
163
163
  minBandWidth?: number;
164
+ /**
165
+ * Fixed horizontal inset (px) between the svg edges and the plot, overriding
166
+ * the auto-computed left/right margins. Use for edge-to-edge funnels where
167
+ * the auto layout's tick-label padding leaves dead space beside the first and
168
+ * last bars. Category tick labels near the edges may clip if they are wider
169
+ * than their band, so pair small values with short labels.
170
+ */
171
+ marginX?: number;
164
172
  tooltipSnippet?: Snippet<[BarChartDataPoint, number]>;
165
173
  empty?: Snippet;
166
174
  /**
@@ -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 ticks = [];
40
- let tick = Math.ceil(min / step) * step;
41
- while (tick <= max + step * 0.001) {
42
- ticks.push(Math.round(tick * 1e10) / 1e10);
43
- tick += step;
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/svelte-ui-components",
3
- "version": "2.106.1",
3
+ "version": "2.107.0",
4
4
  "description": "A themeable Svelte 5 UI component library with CSS custom property driven styling",
5
5
  "keywords": [
6
6
  "svelte",