@juspay/svelte-ui-components 2.81.2 → 2.82.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.
@@ -13,7 +13,11 @@
13
13
  import ChartTooltip from '../_chart/ChartTooltip.svelte';
14
14
  import Legend from '../_chart/Legend.svelte';
15
15
  import { createBandScale, createLinearScale, niceLinearDomain } from '../_chart/scales';
16
- import { computeChartDimensions } from '../_chart/geometry';
16
+ import {
17
+ computeChartDimensions,
18
+ computeHorizontalCategoryGutter,
19
+ measureTextWidth
20
+ } from '../_chart/geometry';
17
21
  import { getColor } from '../_chart/colors';
18
22
  import { formatNumber } from '../_chart/format';
19
23
  import { roundedRectPath } from '../_chart/paths';
@@ -110,17 +114,6 @@
110
114
 
111
115
  let format = $derived(valueFormat ?? formatNumber);
112
116
  let isVertical = $derived(orientation === 'vertical');
113
- // When an axis is hidden its gutter (Y = 50px for tick labels, X = 40px) is dead
114
- // space that squeezes the plot into the centre. Collapse the tick-label gutter but
115
- // keep a symmetric breathing-room inset so the edge bars (and their value labels)
116
- // don't sit flush against the container edges.
117
- let dims = $derived(
118
- computeChartDimensions(chartWidth, chartHeight, {
119
- left: showYAxis ? 50 : 28,
120
- right: 28,
121
- bottom: showXAxis ? 40 : 8
122
- })
123
- );
124
117
 
125
118
  let isMulti = $derived(Array.isArray(series) && series.length > 0);
126
119
 
@@ -197,6 +190,58 @@
197
190
  return first.map((d) => d.label);
198
191
  });
199
192
 
193
+ /**
194
+ * Widest category label in the axis tick font, for the horizontal-orientation
195
+ * gutter below. Horizontal charts put category text (not short numeric ticks)
196
+ * on the Y axis, so the gutter must fit real words like "Submitted Address" —
197
+ * a fixed gutter clips them. Resolved from the mounted container so CSS-var
198
+ * theming (--chart-axis-font-size / --chart-axis-font-family) is honoured.
199
+ * Null when unmeasurable (SSR, no canvas) — the gutter then keeps its legacy
200
+ * fixed width.
201
+ */
202
+ let widestCategoryLabelWidth = $derived.by(() => {
203
+ if (isVertical || !showYAxis || labels.length === 0 || containerEl === null) {
204
+ return null;
205
+ }
206
+ const containerStyle = getComputedStyle(containerEl);
207
+ const axisFontSize = containerStyle.getPropertyValue('--chart-axis-font-size').trim() || '11px';
208
+ const axisFontFamilyToken = containerStyle.getPropertyValue('--chart-axis-font-family').trim();
209
+ const axisFontFamily =
210
+ axisFontFamilyToken === '' || axisFontFamilyToken === 'inherit'
211
+ ? containerStyle.fontFamily || 'sans-serif'
212
+ : axisFontFamilyToken;
213
+ const axisFont = `${axisFontSize} ${axisFontFamily}`;
214
+ let widest: number | null = null;
215
+ for (const label of labels) {
216
+ const labelWidth = measureTextWidth(label, axisFont);
217
+ if (labelWidth === null) {
218
+ return null;
219
+ }
220
+ if (widest === null || labelWidth > widest) {
221
+ widest = labelWidth;
222
+ }
223
+ }
224
+ return widest;
225
+ });
226
+
227
+ // When an axis is hidden its gutter (Y = 50px for tick labels, X = 40px) is dead
228
+ // space that squeezes the plot into the centre. Collapse the tick-label gutter but
229
+ // keep a symmetric breathing-room inset so the edge bars (and their value labels)
230
+ // don't sit flush against the container edges. In horizontal orientation the
231
+ // visible Y axis carries category text, so its gutter grows to fit the widest
232
+ // label (never shrinking below the legacy 50px, capped at 45% of the width).
233
+ let dims = $derived(
234
+ computeChartDimensions(chartWidth, chartHeight, {
235
+ left: showYAxis
236
+ ? isVertical
237
+ ? 50
238
+ : computeHorizontalCategoryGutter(widestCategoryLabelWidth, chartWidth)
239
+ : 28,
240
+ right: 28,
241
+ bottom: showXAxis ? 40 : 8
242
+ })
243
+ );
244
+
200
245
  // ── Effective highlighted index: merge declarative + imperative ─
201
246
 
202
247
  /**
@@ -22,6 +22,7 @@
22
22
  actionInput = false,
23
23
  useTextArea = false,
24
24
  autoComplete = 'on',
25
+ inputMode,
25
26
  name = '',
26
27
  testId = '',
27
28
  textTransformers = [],
@@ -256,6 +257,7 @@
256
257
  {value}
257
258
  {placeholder}
258
259
  autocomplete={autoComplete}
260
+ inputmode={inputMode}
259
261
  {name}
260
262
  {role}
261
263
  aria-expanded={ariaExpanded}
@@ -286,6 +288,7 @@
286
288
  {value}
287
289
  {placeholder}
288
290
  autocomplete={autoComplete}
291
+ inputmode={inputMode}
289
292
  {name}
290
293
  {role}
291
294
  aria-expanded={ariaExpanded}
@@ -41,6 +41,11 @@ export type OptionalInputProperties = {
41
41
  /** Show a live `current / maxLength` character counter beneath the field. */
42
42
  showCount?: boolean;
43
43
  autoComplete?: HTMLInputAttributes['autocomplete'];
44
+ /**
45
+ * Virtual-keyboard hint rendered as the native `inputmode` attribute
46
+ * (e.g. `'numeric'` for OTP/PIN fields). Left off by default.
47
+ */
48
+ inputMode?: HTMLInputAttributes['inputmode'];
44
49
  name?: string;
45
50
  textTransformers?: TextTransformer[];
46
51
  textViewPresentation?: TextTransformer[];
@@ -159,6 +159,8 @@
159
159
  placeholder={config.placeholder}
160
160
  validationPattern={config.validationPattern ?? null}
161
161
  validators={config.validators ?? []}
162
+ autoComplete={config.autoComplete ?? 'on'}
163
+ inputMode={config.inputMode}
162
164
  disable={disabled}
163
165
  actionInput={true}
164
166
  classes="field-group-input"
@@ -1,5 +1,5 @@
1
1
  import type { OptionalInputProperties } from '../Input/properties';
2
- export type FieldConfig = Pick<OptionalInputProperties, 'dataType' | 'maxLength' | 'min' | 'max' | 'placeholder' | 'validationPattern' | 'validators' | 'label'>;
2
+ export type FieldConfig = Pick<OptionalInputProperties, 'dataType' | 'maxLength' | 'min' | 'max' | 'placeholder' | 'validationPattern' | 'validators' | 'label' | 'autoComplete' | 'inputMode'>;
3
3
  export type SplitInputProperties = MandatorySplitInputProperties & OptionalSplitInputProperties & SplitInputEventProperties;
4
4
  export type MandatorySplitInputProperties = {
5
5
  values: string[];
@@ -1,5 +1,25 @@
1
1
  import type { Margin, ChartDimensions, PieSliceLayout, ComputedSankeyNode, ComputedSankeyLink, StackedPoint } from './types';
2
2
  export declare function computeChartDimensions(width: number, height: number, margin?: Partial<Margin>): ChartDimensions;
3
+ /**
4
+ * Measures rendered text width via a shared offscreen canvas context.
5
+ * Returns null when measurement is unavailable (SSR, or the environment
6
+ * provides no working 2D canvas — e.g. jsdom) so callers can fall back to
7
+ * a fixed layout instead of acting on a bogus 0.
8
+ */
9
+ export declare function measureTextWidth(text: string, font: string): number | null;
10
+ /**
11
+ * Left margin for a horizontal bar chart's category axis, sized to fit the
12
+ * widest category label. Category tick labels render right-aligned 10px left
13
+ * of the axis line (tick mark 6px + 4px gap), so any label wider than
14
+ * `margin.left - 10` bleeds out of the SVG and gets clipped by the page.
15
+ *
16
+ * - Never shrinks below `fallback` (the legacy fixed gutter), so charts whose
17
+ * labels already fit keep their exact current layout.
18
+ * - Caps at 45% of the chart width so one pathological label cannot crush the
19
+ * plot area; past the cap the label bleeds as before, but the plot survives.
20
+ * - `widestLabelWidth === null` (SSR / unmeasurable) keeps the legacy gutter.
21
+ */
22
+ export declare function computeHorizontalCategoryGutter(widestLabelWidth: number | null, chartWidth: number, fallback?: number): number;
3
23
  export declare function computePieLayout(data: Array<{
4
24
  label: string;
5
25
  value: number;
@@ -13,6 +13,51 @@ export function computeChartDimensions(width, height, margin = {}) {
13
13
  innerHeight: Math.max(0, height - m.top - m.bottom)
14
14
  };
15
15
  }
16
+ // ── Text measurement ────────────────────────────────────────────
17
+ let textMeasurementContext = null;
18
+ /**
19
+ * Measures rendered text width via a shared offscreen canvas context.
20
+ * Returns null when measurement is unavailable (SSR, or the environment
21
+ * provides no working 2D canvas — e.g. jsdom) so callers can fall back to
22
+ * a fixed layout instead of acting on a bogus 0.
23
+ */
24
+ export function measureTextWidth(text, font) {
25
+ if (typeof document === 'undefined') {
26
+ return null;
27
+ }
28
+ if (textMeasurementContext === null) {
29
+ textMeasurementContext = document.createElement('canvas').getContext('2d');
30
+ }
31
+ if (textMeasurementContext === null) {
32
+ return null;
33
+ }
34
+ textMeasurementContext.font = font;
35
+ const width = textMeasurementContext.measureText(text).width;
36
+ // jsdom's canvas stub reports 0 for any text; treat that as "cannot measure".
37
+ return width > 0 ? width : null;
38
+ }
39
+ /**
40
+ * Left margin for a horizontal bar chart's category axis, sized to fit the
41
+ * widest category label. Category tick labels render right-aligned 10px left
42
+ * of the axis line (tick mark 6px + 4px gap), so any label wider than
43
+ * `margin.left - 10` bleeds out of the SVG and gets clipped by the page.
44
+ *
45
+ * - Never shrinks below `fallback` (the legacy fixed gutter), so charts whose
46
+ * labels already fit keep their exact current layout.
47
+ * - Caps at 45% of the chart width so one pathological label cannot crush the
48
+ * plot area; past the cap the label bleeds as before, but the plot survives.
49
+ * - `widestLabelWidth === null` (SSR / unmeasurable) keeps the legacy gutter.
50
+ */
51
+ export function computeHorizontalCategoryGutter(widestLabelWidth, chartWidth, fallback = 50) {
52
+ if (widestLabelWidth === null) {
53
+ return fallback;
54
+ }
55
+ const tickLabelInset = 10;
56
+ const breathingPad = 4;
57
+ const cap = Math.max(fallback, chartWidth * 0.45);
58
+ const fitted = widestLabelWidth + tickLabelInset + breathingPad;
59
+ return Math.round(Math.min(Math.max(fallback, fitted), cap));
60
+ }
16
61
  // ── Pie layout ──────────────────────────────────────────────────
17
62
  export function computePieLayout(data, startAngle = -Math.PI / 2, padAngle = 0) {
18
63
  const total = data.reduce((sum, d) => sum + Math.max(0, d.value), 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/svelte-ui-components",
3
- "version": "2.81.2",
3
+ "version": "2.82.0",
4
4
  "description": "A themeable Svelte 5 UI component library with CSS custom property driven styling",
5
5
  "keywords": [
6
6
  "svelte",