@juspay/svelte-ui-components 2.123.0 → 2.124.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.
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
ariaExpanded,
|
|
25
25
|
ariaHaspopup,
|
|
26
26
|
ariaSelected,
|
|
27
|
+
ariaBusy,
|
|
27
28
|
role,
|
|
29
|
+
title,
|
|
28
30
|
onclick,
|
|
29
31
|
onkeydown = () => {},
|
|
30
32
|
onkeyup = () => {},
|
|
@@ -91,7 +93,8 @@
|
|
|
91
93
|
aria-expanded={ariaExpanded ?? null}
|
|
92
94
|
aria-haspopup={ariaHaspopup ?? null}
|
|
93
95
|
aria-selected={ariaSelected ?? null}
|
|
94
|
-
aria-busy={isBusy || null}
|
|
96
|
+
aria-busy={isBusy || ariaBusy || null}
|
|
97
|
+
title={title ?? null}
|
|
95
98
|
type={href ? null : type}
|
|
96
99
|
disabled={href ? null : isDisabled}
|
|
97
100
|
href={href ? (isDisabled ? null : href) : null}
|
|
@@ -59,7 +59,20 @@ export type OptionalButtonProperties = {
|
|
|
59
59
|
*/
|
|
60
60
|
ariaHaspopup?: 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | boolean;
|
|
61
61
|
ariaSelected?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Native `aria-busy`, for a control that stays usable while related data loads.
|
|
64
|
+
* Deliberately separate from `loading`, which also renders the spinner and
|
|
65
|
+
* disables the button — a trigger whose *contents* are still loading must stay
|
|
66
|
+
* clickable, so it cannot express that state through `loading`.
|
|
67
|
+
*/
|
|
68
|
+
ariaBusy?: boolean;
|
|
62
69
|
role?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Native `title`, rendered as the browser's own hover tooltip. Distinct from
|
|
72
|
+
* `ariaLabel`, which names the control for assistive tech without any visible
|
|
73
|
+
* affordance; an icon-only button generally wants both.
|
|
74
|
+
*/
|
|
75
|
+
title?: string;
|
|
63
76
|
disabled?: boolean;
|
|
64
77
|
classes?: string;
|
|
65
78
|
/**
|
package/dist/Input/Input.svelte
CHANGED
|
@@ -115,6 +115,9 @@
|
|
|
115
115
|
const hasRightIcon = $derived(typeof rightIcon === 'function');
|
|
116
116
|
|
|
117
117
|
const charCount = $derived(value?.length ?? 0);
|
|
118
|
+
// Every numeric use below is either tel-only normalisation or the character
|
|
119
|
+
// counter; `null` means "no attribute", not "no ceiling on those paths".
|
|
120
|
+
const effectiveMaxLength = $derived(maxLength ?? 1000);
|
|
118
121
|
const effectiveResize = $derived(autoResize ? 'none' : resize);
|
|
119
122
|
|
|
120
123
|
// Grow the textarea to fit its content between minRows and maxRows.
|
|
@@ -130,8 +133,16 @@
|
|
|
130
133
|
const border = parseFloat(styles.borderTopWidth) + parseFloat(styles.borderBottomWidth);
|
|
131
134
|
const lower = minRows ?? rows ?? 2;
|
|
132
135
|
const minHeight = lower * lineHeight + verticalPadding + border;
|
|
133
|
-
const
|
|
136
|
+
const rowsCeiling =
|
|
134
137
|
maxRows != null ? maxRows * lineHeight + verticalPadding + border : Number.POSITIVE_INFINITY;
|
|
138
|
+
// --input-max-height is a ceiling too. Reading only maxRows left it at Infinity, so the
|
|
139
|
+
// inline height grew past the CSS clamp and overflowY was set to `hidden` — the box
|
|
140
|
+
// stopped at the right size but its overflow became unreachable instead of scrollable.
|
|
141
|
+
const styleCeiling = parseFloat(styles.maxHeight);
|
|
142
|
+
const maxHeight = Math.min(
|
|
143
|
+
rowsCeiling,
|
|
144
|
+
Number.isFinite(styleCeiling) ? styleCeiling : Number.POSITIVE_INFINITY
|
|
145
|
+
);
|
|
135
146
|
const nextHeight = Math.min(Math.max(el.scrollHeight, minHeight), maxHeight);
|
|
136
147
|
el.style.height = `${nextHeight}px`;
|
|
137
148
|
el.style.overflowY = el.scrollHeight > maxHeight ? 'auto' : 'hidden';
|
|
@@ -163,16 +174,16 @@
|
|
|
163
174
|
inputElement.value = value;
|
|
164
175
|
return;
|
|
165
176
|
}
|
|
166
|
-
if (numberLength >
|
|
177
|
+
if (numberLength > effectiveMaxLength) {
|
|
167
178
|
const existingInput = value;
|
|
168
|
-
if (existingInput.length ===
|
|
179
|
+
if (existingInput.length === effectiveMaxLength) {
|
|
169
180
|
inputElement.value = applyTextPresentation(value);
|
|
170
181
|
return;
|
|
171
182
|
}
|
|
172
183
|
/**
|
|
173
184
|
* choose last max length number of digits if length is bigger than max length passed in props
|
|
174
185
|
*/
|
|
175
|
-
currentValue = currentValue.substring(numberLength -
|
|
186
|
+
currentValue = currentValue.substring(numberLength - effectiveMaxLength);
|
|
176
187
|
}
|
|
177
188
|
currentValue = applyTextPresentation(currentValue);
|
|
178
189
|
inputElement.value = currentValue;
|
|
@@ -221,12 +232,12 @@
|
|
|
221
232
|
/**
|
|
222
233
|
* user pasted 10+ digit number , overrides all cases
|
|
223
234
|
*/
|
|
224
|
-
if (filteredNumber.length >
|
|
235
|
+
if (filteredNumber.length > effectiveMaxLength) {
|
|
225
236
|
/**
|
|
226
237
|
* choose last max length number of digits if length is bigger than max length passed in props
|
|
227
238
|
*/
|
|
228
239
|
const finalValue = applyTextPresentation(
|
|
229
|
-
filteredNumber.substring(filteredNumberLength -
|
|
240
|
+
filteredNumber.substring(filteredNumberLength - effectiveMaxLength)
|
|
230
241
|
);
|
|
231
242
|
// Adding reactivity
|
|
232
243
|
value = finalValue;
|
|
@@ -390,8 +401,8 @@
|
|
|
390
401
|
</div>
|
|
391
402
|
{/if}
|
|
392
403
|
{#if useTextArea && showCount && !actionInput}
|
|
393
|
-
<div class="input-char-count" class:at-limit={charCount >=
|
|
394
|
-
{charCount}/{
|
|
404
|
+
<div class="input-char-count" class:at-limit={charCount >= effectiveMaxLength}>
|
|
405
|
+
{charCount}/{effectiveMaxLength}
|
|
395
406
|
</div>
|
|
396
407
|
{/if}
|
|
397
408
|
</div>
|
|
@@ -401,6 +412,13 @@
|
|
|
401
412
|
input {
|
|
402
413
|
box-sizing: var(--input-box-sizing, border-box);
|
|
403
414
|
height: var(--input-height, fit-content);
|
|
415
|
+
|
|
416
|
+
/* Both default to the CSS initial value, so a consumer that sets neither is
|
|
417
|
+
byte-identical to before. A textarea that grows with its content needs a
|
|
418
|
+
ceiling before it can scroll, and one used as a paste target needs a floor;
|
|
419
|
+
neither was reachable through the --input-* surface. */
|
|
420
|
+
min-height: var(--input-min-height, auto);
|
|
421
|
+
max-height: var(--input-max-height, none);
|
|
404
422
|
background-color: var(--input-background, white);
|
|
405
423
|
font-size: var(--input-font-size, 16px) !important;
|
|
406
424
|
font-family: var(--input-font-family, inherit);
|
|
@@ -408,6 +426,12 @@
|
|
|
408
426
|
outline: none;
|
|
409
427
|
padding: var(--input-padding, 16px);
|
|
410
428
|
font-weight: var(--input-font-weight, 500);
|
|
429
|
+
|
|
430
|
+
/* `normal` is what a textarea/input computes today regardless of any inherited
|
|
431
|
+
value — the UA sheet sets it, and inheritance loses to a UA declaration on the
|
|
432
|
+
element itself. So the default here is byte-identical for existing consumers,
|
|
433
|
+
and this is the only way a consumer can set it at all. */
|
|
434
|
+
line-height: var(--input-line-height, normal);
|
|
411
435
|
width: var(--input-width, fit-content);
|
|
412
436
|
margin: var(--input-margin, 0);
|
|
413
437
|
appearance: none !important;
|
|
@@ -29,7 +29,12 @@ export type OptionalInputProperties = {
|
|
|
29
29
|
validationPattern?: RegExp | null;
|
|
30
30
|
inProgressPattern?: RegExp | null;
|
|
31
31
|
addFocusColor?: boolean;
|
|
32
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Native `maxlength`. Defaults to 1000. Pass `null` for no limit — a composer or
|
|
34
|
+
* paste target that silently truncates long input is worse than an unbounded one,
|
|
35
|
+
* and the attribute is rendered unconditionally otherwise.
|
|
36
|
+
*/
|
|
37
|
+
maxLength?: number | null;
|
|
33
38
|
minLength?: number;
|
|
34
39
|
min?: number;
|
|
35
40
|
max?: number;
|
package/dist/Menu/Menu.svelte
CHANGED
|
@@ -200,9 +200,28 @@
|
|
|
200
200
|
focusedIndex = -1;
|
|
201
201
|
typeaheadQuery = '';
|
|
202
202
|
onclose?.();
|
|
203
|
-
|
|
204
|
-
|
|
203
|
+
focusTrigger();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Returns focus to whatever is actually focusable for this trigger. Under
|
|
208
|
+
* `interactiveTrigger` the wrapper carries no tabindex, so focusing it is a no-op and
|
|
209
|
+
* focus falls to <body> — the control the snippet rendered is the real target.
|
|
210
|
+
*/
|
|
211
|
+
function focusTrigger() {
|
|
212
|
+
if (triggerEl === null) {
|
|
213
|
+
return;
|
|
205
214
|
}
|
|
215
|
+
if (interactiveTrigger) {
|
|
216
|
+
const control = triggerEl.querySelector(
|
|
217
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
218
|
+
);
|
|
219
|
+
if (control instanceof HTMLElement) {
|
|
220
|
+
control.focus({ preventScroll: true });
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
triggerEl.focus({ preventScroll: true });
|
|
206
225
|
}
|
|
207
226
|
|
|
208
227
|
function selectItem(item: MenuItem) {
|
|
@@ -240,6 +259,23 @@
|
|
|
240
259
|
}
|
|
241
260
|
}
|
|
242
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Keydown wiring handed to an `interactiveTrigger` snippet. Enter and Space are
|
|
264
|
+
* deliberately NOT handled here: the snippet owns a real `<button>`, which already
|
|
265
|
+
* synthesises a click from both, and that click is already wired to `toggle`. Handling
|
|
266
|
+
* them again would open a menu the click then immediately closes. Only the arrow keys —
|
|
267
|
+
* which no native button implements — are added.
|
|
268
|
+
*/
|
|
269
|
+
function handleInteractiveTriggerKeydown(event: KeyboardEvent) {
|
|
270
|
+
if (event.key === 'ArrowDown') {
|
|
271
|
+
event.preventDefault();
|
|
272
|
+
openMenu();
|
|
273
|
+
} else if (event.key === 'ArrowUp') {
|
|
274
|
+
event.preventDefault();
|
|
275
|
+
openMenu(selectableItems.length - 1);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
243
279
|
function handleMenuKeydown(event: KeyboardEvent) {
|
|
244
280
|
switch (event.key) {
|
|
245
281
|
case 'ArrowDown': {
|
|
@@ -358,7 +394,7 @@
|
|
|
358
394
|
{#if typeof trigger === 'function'}
|
|
359
395
|
{@render trigger({
|
|
360
396
|
onclick: toggle,
|
|
361
|
-
onkeydown:
|
|
397
|
+
onkeydown: handleInteractiveTriggerKeydown,
|
|
362
398
|
ariaHaspopup: 'menu',
|
|
363
399
|
ariaExpanded: open
|
|
364
400
|
})}
|