@juspay/svelte-ui-components 4.27.6 → 4.28.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.
- package/README.md +140 -36
- package/dist/Animations/ModalAnimation.svelte +15 -1
- package/dist/Animations/OverlayAnimation.svelte +7 -1
- package/dist/AreaChart/AreaChart.svelte +366 -85
- package/dist/AspectRatio/AspectRatio.svelte +51 -0
- package/dist/AspectRatio/AspectRatio.svelte.d.ts +3 -0
- package/dist/AspectRatio/properties.d.ts +27 -0
- package/dist/AspectRatio/properties.js +1 -0
- package/dist/Banner/Banner.svelte +10 -4
- package/dist/Banner/properties.d.ts +8 -0
- package/dist/BarChart/BarChart.svelte +156 -49
- package/dist/Book/Book.svelte +14 -1
- package/dist/BrandLoader/BrandLoader.svelte +76 -12
- package/dist/Button/Button.svelte +56 -11
- package/dist/Calendar/Calendar.svelte +88 -61
- package/dist/Card/Card.svelte +1 -1
- package/dist/Carousel/Carousel.svelte +6 -3
- package/dist/ChatComposer/ChatComposer.svelte +54 -4
- package/dist/ChatComposer/dictationState.d.ts +15 -0
- package/dist/ChatComposer/dictationState.js +9 -0
- package/dist/ChatComposer/properties.d.ts +28 -1
- package/dist/ChatMessage/ChatMessage.svelte +27 -1
- package/dist/CheckListItem/CheckListItem.svelte +3 -3
- package/dist/Checkbox/Checkbox.svelte +84 -3
- package/dist/Checkbox/properties.d.ts +45 -13
- package/dist/ChipInput/ChipInput.svelte +70 -3
- package/dist/ChipInput/properties.d.ts +16 -0
- package/dist/Choicebox/Choicebox.svelte +156 -1
- package/dist/Choicebox/group.d.ts +63 -0
- package/dist/Choicebox/group.js +127 -0
- package/dist/Choicebox/properties.d.ts +46 -0
- package/dist/ColorPicker/ColorPicker.svelte +169 -20
- package/dist/ColorPicker/properties.d.ts +16 -0
- package/dist/Combobox/Combobox.svelte +87 -25
- package/dist/Combobox/properties.d.ts +23 -0
- package/dist/CommandMenu/CommandMenu.svelte +27 -11
- package/dist/ContextMenu/ContextMenu.svelte +31 -37
- package/dist/DateRangePicker/DateRangePicker.svelte +171 -73
- package/dist/DeltaIndicator/DeltaIndicator.svelte +3 -3
- package/dist/Draggable/Draggable.svelte +12 -7
- package/dist/DualAxisBarChart/DualAxisBarChart.svelte +11 -4
- package/dist/EmptyState/EmptyState.svelte +1 -1
- package/dist/FileDropzoneTrigger/FileDropzoneTrigger.svelte +1 -1
- package/dist/FileInput/FileInput.svelte +50 -1
- package/dist/FileInput/properties.d.ts +7 -0
- package/dist/FunnelChart/FunnelChart.svelte +40 -12
- package/dist/FunnelChart/properties.d.ts +7 -1
- package/dist/Gallery/Gallery.svelte +20 -13
- package/dist/Gallery/properties.d.ts +8 -0
- package/dist/GridItem/GridItem.svelte +31 -1
- package/dist/HITL/HITL.svelte +35 -9
- package/dist/Icon/Icon.svelte +19 -3
- package/dist/Input/Input.svelte +24 -10
- package/dist/Input/properties.d.ts +37 -1
- package/dist/InputButton/InputButton.svelte +86 -11
- package/dist/InputButton/properties.d.ts +10 -1
- package/dist/Label/Label.svelte +54 -0
- package/dist/Label/Label.svelte.d.ts +3 -0
- package/dist/Label/properties.d.ts +26 -0
- package/dist/Label/properties.js +1 -0
- package/dist/LineChart/LineChart.svelte +385 -94
- package/dist/ListItem/ListItem.svelte +121 -15
- package/dist/Loader/Loader.svelte +22 -2
- package/dist/LoadingDots/LoadingDots.svelte +21 -0
- package/dist/MediaUpload/MediaUpload.svelte +13 -3
- package/dist/Menu/Menu.svelte +56 -28
- package/dist/Modal/Modal.svelte +77 -42
- package/dist/Pagination/Pagination.svelte +9 -2
- package/dist/Phone/Phone.svelte +4 -0
- package/dist/PieChart/PieChart.svelte +282 -38
- package/dist/PieChart/properties.d.ts +9 -0
- package/dist/Pill/Pill.svelte +14 -5
- package/dist/Progress/Progress.svelte +20 -0
- package/dist/Radio/Radio.svelte +58 -1
- package/dist/Radio/properties.d.ts +23 -0
- package/dist/RatingGroup/RatingGroup.svelte +283 -0
- package/dist/RatingGroup/RatingGroup.svelte.d.ts +4 -0
- package/dist/RatingGroup/properties.d.ts +73 -0
- package/dist/RatingGroup/properties.js +1 -0
- package/dist/RelativeTime/RelativeTime.svelte +1 -1
- package/dist/SankeyChart/SankeyChart.svelte +272 -50
- package/dist/SankeyChart/properties.d.ts +9 -0
- package/dist/Scroller/Scroller.svelte +37 -2
- package/dist/Select/Select.svelte +166 -46
- package/dist/Select/properties.d.ts +7 -0
- package/dist/Separator/Separator.svelte +48 -0
- package/dist/Separator/Separator.svelte.d.ts +3 -0
- package/dist/Separator/properties.d.ts +22 -0
- package/dist/Separator/properties.js +1 -0
- package/dist/Sheet/Sheet.svelte +60 -21
- package/dist/Shimmer/Shimmer.svelte +15 -0
- package/dist/Slider/Slider.svelte +87 -6
- package/dist/Slider/properties.d.ts +26 -0
- package/dist/Snippet/Snippet.svelte +4 -0
- package/dist/Snippet/copyState.svelte.d.ts +13 -1
- package/dist/Snippet/copyState.svelte.js +27 -1
- package/dist/Snippet/properties.d.ts +7 -0
- package/dist/SoundKit/SoundKit.js +9 -8
- package/dist/SplitInput/SplitInput.svelte +68 -2
- package/dist/SplitInput/properties.d.ts +23 -0
- package/dist/StatCard/StatCard.svelte +6 -6
- package/dist/Status/Status.svelte +2 -2
- package/dist/Stepper/Step.svelte +19 -6
- package/dist/Stepper/Stepper.svelte +108 -35
- package/dist/Table/BuiltinCell.svelte +2 -2
- package/dist/Table/Table.svelte +373 -55
- package/dist/Table/properties.d.ts +134 -3
- package/dist/Tabs/Tabs.svelte +263 -65
- package/dist/Tabs/properties.d.ts +23 -0
- package/dist/TaskList/TaskList.svelte +14 -5
- package/dist/ThemeSwitcher/ThemeSwitcher.svelte +23 -0
- package/dist/ThinkingIndicator/ThinkingIndicator.svelte +13 -5
- package/dist/Toast/Toast.svelte +27 -2
- package/dist/Toggle/Toggle.svelte +79 -7
- package/dist/Toggle/Toggle.svelte.d.ts +1 -1
- package/dist/Toggle/properties.d.ts +30 -7
- package/dist/ToolCallLog/ToolCallLog.svelte +59 -21
- package/dist/ToolCallLog/properties.d.ts +10 -0
- package/dist/Toolbar/Toolbar.svelte +1 -1
- package/dist/Tooltip/Tooltip.svelte +20 -2
- package/dist/Tooltip/tooltip-action.js +39 -4
- package/dist/TypewriterText/TypewriterText.svelte +34 -2
- package/dist/TypewriterText/properties.d.ts +18 -5
- package/dist/_chart/ChartContainer.svelte +12 -0
- package/dist/_chart/ChartTooltip.svelte +18 -4
- package/dist/_chart/Legend.svelte +21 -0
- package/dist/_chart/Legend.svelte.d.ts +15 -0
- package/dist/_chart/geometry.d.ts +54 -1
- package/dist/_chart/geometry.js +122 -14
- package/dist/_chart/interactions.d.ts +5 -1
- package/dist/_chart/interactions.js +9 -5
- package/dist/_chart/scales.js +16 -0
- package/dist/_chart/temporal.d.ts +45 -0
- package/dist/_chart/temporal.js +116 -0
- package/dist/_field/description.d.ts +50 -0
- package/dist/_field/description.js +51 -0
- package/dist/_interaction/dismissal.d.ts +67 -0
- package/dist/_interaction/dismissal.js +137 -0
- package/dist/_interaction/focus.d.ts +73 -0
- package/dist/_interaction/focus.js +143 -0
- package/dist/assets/star.svg +5 -0
- package/dist/index.d.ts +13 -1
- package/dist/index.js +21 -1
- package/dist/reduced-motion.svelte.d.ts +37 -0
- package/dist/reduced-motion.svelte.js +44 -0
- package/dist/styles/theme-dark.css +778 -0
- package/dist/types.d.ts +4 -2
- package/dist/utils.d.ts +62 -1
- package/dist/utils.js +80 -21
- package/dist-codemod/{bin.js → codemod/bin.js} +13 -2
- package/dist-codemod/{cli.js → codemod/cli.js} +48 -3
- package/dist-codemod/codemod/migrate-audit.js +395 -0
- package/dist-codemod/codemod/migration-assets/legacy-display.css +120 -0
- package/dist-codemod/codemod/migration-assets/legacy-palette.css +106 -0
- package/dist-codemod/codemod/migration-assets/wc-display.json +492 -0
- package/dist-codemod/migrate/analyze.js +804 -0
- package/dist-codemod/migrate/chart-tooltip-selector.js +301 -0
- package/dist-wc/index.js +13992 -4323
- package/package.json +13 -6
- package/dist/Modal/focus-trap.d.ts +0 -42
- package/dist/Modal/focus-trap.js +0 -57
- /package/dist-codemod/{legacy-pairs.js → codemod/legacy-pairs.js} +0 -0
- /package/dist-codemod/{transform.js → codemod/transform.js} +0 -0
- /package/dist-codemod/{wc-children.js → codemod/wc-children.js} +0 -0
package/dist/Input/Input.svelte
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { prefersReducedMotion } from '../utils';
|
|
3
|
+
import { getActiveElement } from '../_interaction/focus';
|
|
2
4
|
import { validateInput } from '../utils';
|
|
3
5
|
import type { InputProperties } from './properties';
|
|
4
6
|
import type { ValidationState } from '../types';
|
|
@@ -53,6 +55,7 @@
|
|
|
53
55
|
leftIconLabel = 'Leading action',
|
|
54
56
|
rightIconLabel = 'Trailing action',
|
|
55
57
|
mandatory = false,
|
|
58
|
+
required,
|
|
56
59
|
forceError = false,
|
|
57
60
|
rows,
|
|
58
61
|
autoResize = false,
|
|
@@ -62,6 +65,12 @@
|
|
|
62
65
|
showCount = false
|
|
63
66
|
}: InputProperties = $props();
|
|
64
67
|
|
|
68
|
+
// `required` decides when it is supplied at all, so a caller migrating one call site
|
|
69
|
+
// at a time gets an unambiguous direction; `mandatory` answers only when `required`
|
|
70
|
+
// was not passed. Checked with typeof rather than a default, so an explicit
|
|
71
|
+
// `required={false}` beside a legacy `mandatory` really does turn the field optional.
|
|
72
|
+
const isRequired = $derived(typeof required === 'boolean' ? required : mandatory);
|
|
73
|
+
|
|
65
74
|
/* `for` on a <label> resolves against an element's id, never its name. The label
|
|
66
75
|
was emitted with for={name} while the field itself carried only name={name},
|
|
67
76
|
so the association never completed for any caller. Derive the id from name so
|
|
@@ -75,7 +84,12 @@
|
|
|
75
84
|
export function focus() {
|
|
76
85
|
try {
|
|
77
86
|
inputElement?.focus();
|
|
78
|
-
|
|
87
|
+
// A scrollIntoView `behavior` is not a style, so no media query reaches it.
|
|
88
|
+
// 'auto' still brings the field into view -- it just arrives instead of gliding.
|
|
89
|
+
inputElement?.scrollIntoView({
|
|
90
|
+
behavior: prefersReducedMotion() ? 'auto' : 'smooth',
|
|
91
|
+
block: 'center'
|
|
92
|
+
});
|
|
79
93
|
} catch (error) {
|
|
80
94
|
console.error('Error focusing or scrolling inputElement:', error);
|
|
81
95
|
}
|
|
@@ -107,7 +121,7 @@
|
|
|
107
121
|
valueValidation === 'InProgress' &&
|
|
108
122
|
value.length > 0 &&
|
|
109
123
|
inputElement !== null &&
|
|
110
|
-
inputElement !==
|
|
124
|
+
inputElement !== getActiveElement(inputElement)
|
|
111
125
|
) {
|
|
112
126
|
return 'Invalid';
|
|
113
127
|
}
|
|
@@ -302,7 +316,7 @@
|
|
|
302
316
|
<div class="input-container {classes ?? ''}" class:input-error={showError && !actionInput}>
|
|
303
317
|
{#if hasVisibleLabel}
|
|
304
318
|
<label class="label" for={effectiveId}>
|
|
305
|
-
{label}{#if
|
|
319
|
+
{label}{#if isRequired}<span class="input-mandatory-asterisk" aria-hidden="true">*</span>{/if}
|
|
306
320
|
</label>
|
|
307
321
|
{/if}
|
|
308
322
|
|
|
@@ -321,10 +335,10 @@
|
|
|
321
335
|
aria-autocomplete={ariaAutocomplete}
|
|
322
336
|
aria-controls={ariaControls}
|
|
323
337
|
aria-activedescendant={ariaActivedescendant}
|
|
324
|
-
aria-required={
|
|
338
|
+
aria-required={isRequired || null}
|
|
325
339
|
aria-invalid={showError && !actionInput ? 'true' : null}
|
|
326
340
|
aria-describedby={describedBy || null}
|
|
327
|
-
required={
|
|
341
|
+
required={isRequired || null}
|
|
328
342
|
{onfocus}
|
|
329
343
|
onfocusout={_onFocusOut}
|
|
330
344
|
oninput={handleOnInput}
|
|
@@ -359,10 +373,10 @@
|
|
|
359
373
|
aria-autocomplete={ariaAutocomplete}
|
|
360
374
|
aria-controls={ariaControls}
|
|
361
375
|
aria-activedescendant={ariaActivedescendant}
|
|
362
|
-
aria-required={
|
|
376
|
+
aria-required={isRequired || null}
|
|
363
377
|
aria-invalid={showError && !actionInput ? 'true' : null}
|
|
364
378
|
aria-describedby={describedBy || null}
|
|
365
|
-
required={
|
|
379
|
+
required={isRequired || null}
|
|
366
380
|
{onfocus}
|
|
367
381
|
onfocusout={_onFocusOut}
|
|
368
382
|
oninput={handleOnInput}
|
|
@@ -487,7 +501,7 @@
|
|
|
487
501
|
visibility: var(--input-visibility, visible);
|
|
488
502
|
text-align: var(--input-text-align, left);
|
|
489
503
|
text-transform: var(--input-text-transform, none);
|
|
490
|
-
color: var(--input-text-color);
|
|
504
|
+
color: var(--input-text-color, #333333);
|
|
491
505
|
}
|
|
492
506
|
|
|
493
507
|
textarea:focus,
|
|
@@ -619,7 +633,7 @@
|
|
|
619
633
|
.input-char-count {
|
|
620
634
|
align-self: flex-end;
|
|
621
635
|
font-size: var(--input-char-count-size, 12px);
|
|
622
|
-
color: var(--input-char-count-color, #
|
|
636
|
+
color: var(--input-char-count-color, #5c6b7a);
|
|
623
637
|
margin: var(--input-char-count-margin, 4px 0 0);
|
|
624
638
|
font-variant-numeric: tabular-nums;
|
|
625
639
|
}
|
|
@@ -629,6 +643,6 @@
|
|
|
629
643
|
}
|
|
630
644
|
|
|
631
645
|
::placeholder {
|
|
632
|
-
color: var(--input-placeholder-color);
|
|
646
|
+
color: var(--input-placeholder-color, #5c6b7a);
|
|
633
647
|
}
|
|
634
648
|
</style>
|
|
@@ -7,6 +7,29 @@ export type MandatoryInputProperties = {
|
|
|
7
7
|
};
|
|
8
8
|
export type OptionalInputProperties = {
|
|
9
9
|
placeholder?: string | null;
|
|
10
|
+
/**
|
|
11
|
+
* Native input type, and the switch `validateInput` runs on.
|
|
12
|
+
*
|
|
13
|
+
* **Only `text`, `tel`, `password` and `email` are validated.** `number`,
|
|
14
|
+
* `time`, `date`, `search` and `url` have no branch at all: they are always
|
|
15
|
+
* reported Valid, `onErrorMessage` never shows for them, and the browser's own
|
|
16
|
+
* constraint validation (`min`/`max`/`step`, the type's own parsing) is the
|
|
17
|
+
* only thing checking the value. Supply `validators` if you need more.
|
|
18
|
+
*
|
|
19
|
+
* `tel` with no `validationPattern` checks only the characters a phone number
|
|
20
|
+
* may contain -- digits, spaces, `+`, `-`, `.`, `()` -- and nothing about any
|
|
21
|
+
* national format, so `+33 6 12 34 56 78` and `(555) 123-4567` are accepted.
|
|
22
|
+
* Before 4.20 it silently required a ten-digit Indian mobile number. To ask for
|
|
23
|
+
* that rule, or any other market's, pass the preset's patterns:
|
|
24
|
+
*
|
|
25
|
+
* ```svelte
|
|
26
|
+
* <Input
|
|
27
|
+
* dataType="tel"
|
|
28
|
+
* validationPattern={TEL_PRESET_IN_MOBILE.valid}
|
|
29
|
+
* inProgressPattern={TEL_PRESET_IN_MOBILE.inProgress}
|
|
30
|
+
* />
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
10
33
|
dataType?: InputDataType;
|
|
11
34
|
label?: string | null;
|
|
12
35
|
onErrorMessage?: string | null;
|
|
@@ -94,7 +117,20 @@ export type OptionalInputProperties = {
|
|
|
94
117
|
leftIconLabel?: string;
|
|
95
118
|
/** Accessible label for the clickable rightIcon button (defaults to a generic label). */
|
|
96
119
|
rightIconLabel?: string;
|
|
97
|
-
/**
|
|
120
|
+
/**
|
|
121
|
+
* Appends a required asterisk beside the label and sets `aria-required` and the
|
|
122
|
+
* native `required` attribute on the field. This is the platform's own name and the
|
|
123
|
+
* one every other form control in this library uses, so it is the spelling to reach
|
|
124
|
+
* for. Wins over `mandatory` when both are supplied.
|
|
125
|
+
*/
|
|
126
|
+
required?: boolean;
|
|
127
|
+
/**
|
|
128
|
+
* @deprecated Use `required`. This is the original spelling of the same concept and
|
|
129
|
+
* keeps working unchanged, but the library settled on the platform's name once the
|
|
130
|
+
* other form controls gained it. The `--input-mandatory-*` tokens and the
|
|
131
|
+
* `.input-mandatory-asterisk` class keep their names: those are a separate public
|
|
132
|
+
* surface, and renaming them would break every consumer theming the asterisk.
|
|
133
|
+
*/
|
|
98
134
|
mandatory?: boolean;
|
|
99
135
|
/** Forces the error border independent of validationPattern (server/runtime-driven errors). */
|
|
100
136
|
forceError?: boolean;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { describeField } from '../_field/description';
|
|
2
3
|
import Button from '../Button/Button.svelte';
|
|
3
4
|
import Input from '../Input/Input.svelte';
|
|
4
5
|
import type { InputButtonProperties } from './properties';
|
|
@@ -18,6 +19,7 @@
|
|
|
18
19
|
rightIcon,
|
|
19
20
|
classes,
|
|
20
21
|
mandatory,
|
|
22
|
+
required,
|
|
21
23
|
size,
|
|
22
24
|
error,
|
|
23
25
|
testId
|
|
@@ -53,6 +55,59 @@
|
|
|
53
55
|
inputEventProperties?.onstatechange?.(state);
|
|
54
56
|
}
|
|
55
57
|
|
|
58
|
+
/* `for` resolves against an element's id and never its name, so `for={name}`
|
|
59
|
+
never completed the association for any caller -- the same defect Input
|
|
60
|
+
fixed for itself. InputButton cannot read the id Input derives internally,
|
|
61
|
+
so it derives the identical one here and passes it down, which both
|
|
62
|
+
completes the label and keeps the two components agreeing on one id.
|
|
63
|
+
An explicit id still wins, for repeated rows that need uniqueness. */
|
|
64
|
+
const uid = $props.id();
|
|
65
|
+
const fieldId = $derived(
|
|
66
|
+
inputProperties.id ||
|
|
67
|
+
(typeof inputProperties.name === 'string' && inputProperties.name !== ''
|
|
68
|
+
? `${inputProperties.name}-${uid}`
|
|
69
|
+
: uid)
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
/* `mandatory` drew an asterisk and stopped there: the Input underneath never
|
|
73
|
+
received it, so the field was not actually required and announced nothing.
|
|
74
|
+
Input maps both spellings to native `required` plus `aria-required`, and a
|
|
75
|
+
component whose own marker contradicts the control inside it is the worse
|
|
76
|
+
of the two behaviours to keep. `required` is the settled name; `mandatory`
|
|
77
|
+
stays as the deprecated alias. */
|
|
78
|
+
const isRequired = $derived(typeof required === 'boolean' ? required : mandatory === true);
|
|
79
|
+
|
|
80
|
+
/* InputButton renders the error and helper text ITSELF -- it passes
|
|
81
|
+
`actionInput={true}`, which is what suppresses Input's own copies -- and
|
|
82
|
+
those two divs carried no id, so nothing referenced them. A screen-reader
|
|
83
|
+
user heard the field and never the sentence explaining why it was rejected.
|
|
84
|
+
|
|
85
|
+
`describeField` composes the reference from the messages that are actually
|
|
86
|
+
rendered, so aria-describedby never points at an id that is not in the DOM.
|
|
87
|
+
It is keyed on `fieldId`, the id Input is given below, and that cannot
|
|
88
|
+
collide with Input's own `${id}-error` / `${id}-info`: `actionInput` is
|
|
89
|
+
hard-coded true here, so Input renders neither.
|
|
90
|
+
|
|
91
|
+
The existing precedence is preserved exactly -- an external `error` wins
|
|
92
|
+
over the internal validation message, which itself only shows once
|
|
93
|
+
validation has actually failed. */
|
|
94
|
+
const resolvedError = $derived(
|
|
95
|
+
typeof error === 'string' && error.length > 0
|
|
96
|
+
? error
|
|
97
|
+
: validationState === 'Invalid' &&
|
|
98
|
+
typeof inputProperties.onErrorMessage === 'string' &&
|
|
99
|
+
inputProperties.onErrorMessage !== ''
|
|
100
|
+
? inputProperties.onErrorMessage
|
|
101
|
+
: null
|
|
102
|
+
);
|
|
103
|
+
const field = $derived(
|
|
104
|
+
describeField(fieldId, {
|
|
105
|
+
error: resolvedError,
|
|
106
|
+
info: inputProperties.infoMessage,
|
|
107
|
+
invalid: validationState === 'Invalid'
|
|
108
|
+
})
|
|
109
|
+
);
|
|
110
|
+
|
|
56
111
|
export function focus() {
|
|
57
112
|
inputRef?.focus();
|
|
58
113
|
}
|
|
@@ -62,15 +117,22 @@
|
|
|
62
117
|
}
|
|
63
118
|
</script>
|
|
64
119
|
|
|
120
|
+
<!-- The described element has to carry a role for the description to reach the
|
|
121
|
+
accessibility tree, and the field itself is inside `Input`, which exposes no
|
|
122
|
+
`aria-describedby` seam. The role is conditional so an InputButton with no
|
|
123
|
+
messages keeps the accessibility tree it had before. -->
|
|
65
124
|
<div
|
|
66
125
|
class="container {classes ?? ''}"
|
|
126
|
+
role={field.describedBy !== null || field.ariaInvalid !== null ? 'group' : null}
|
|
127
|
+
aria-describedby={field.describedBy}
|
|
128
|
+
aria-invalid={field.ariaInvalid}
|
|
67
129
|
data-pw={typeof testId === 'string' ? testId : null}
|
|
68
130
|
testID={typeof testId === 'string' ? testId : null}
|
|
69
131
|
>
|
|
70
132
|
{#if inputProperties.label && inputProperties.label !== ''}
|
|
71
|
-
<label class="label" for={
|
|
72
|
-
{inputProperties.label}{#if
|
|
73
|
-
|
|
133
|
+
<label class="label" for={fieldId}>
|
|
134
|
+
{inputProperties.label}{#if isRequired}<span class="mandatory-marker" aria-hidden="true"
|
|
135
|
+
>*</span
|
|
74
136
|
>{/if}
|
|
75
137
|
</label>
|
|
76
138
|
{/if}
|
|
@@ -87,6 +149,8 @@
|
|
|
87
149
|
<Input
|
|
88
150
|
{...inputProperties}
|
|
89
151
|
{...inputEventProperties}
|
|
152
|
+
id={fieldId}
|
|
153
|
+
required={isRequired}
|
|
90
154
|
bind:value
|
|
91
155
|
bind:this={inputRef}
|
|
92
156
|
onstatechange={handleStateChange}
|
|
@@ -110,15 +174,26 @@
|
|
|
110
174
|
</div>
|
|
111
175
|
{/if}
|
|
112
176
|
</div>
|
|
113
|
-
{#if
|
|
114
|
-
<div
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
{
|
|
177
|
+
{#if field.showsError}
|
|
178
|
+
<div
|
|
179
|
+
id={field.errorId}
|
|
180
|
+
role="alert"
|
|
181
|
+
class={typeof error === 'string' && error.length > 0
|
|
182
|
+
? 'external-error-message'
|
|
183
|
+
: 'error-message'}
|
|
184
|
+
data-pw={typeof testId === 'string' ? `${testId}-error-message` : null}
|
|
185
|
+
testID={typeof testId === 'string' ? `${testId}-error-message` : null}
|
|
186
|
+
>
|
|
187
|
+
{resolvedError}
|
|
118
188
|
</div>
|
|
119
189
|
{/if}
|
|
120
|
-
{#if
|
|
121
|
-
<div
|
|
190
|
+
{#if field.showsInfo}
|
|
191
|
+
<div
|
|
192
|
+
id={field.infoId}
|
|
193
|
+
class="info-message"
|
|
194
|
+
data-pw={typeof testId === 'string' ? `${testId}-info-message` : null}
|
|
195
|
+
testID={typeof testId === 'string' ? `${testId}-info-message` : null}
|
|
196
|
+
>
|
|
122
197
|
{inputProperties.infoMessage}
|
|
123
198
|
</div>
|
|
124
199
|
{/if}
|
|
@@ -182,7 +257,7 @@
|
|
|
182
257
|
font-weight: var(--input-label-msg-text-weight, 400);
|
|
183
258
|
font-size: var(--input-label-msg-text-size, 12px);
|
|
184
259
|
color: var(--input-label-msg-text-color, #4d6174);
|
|
185
|
-
line-height: var(--input-label-msg-text-line-height);
|
|
260
|
+
line-height: var(--input-label-msg-text-line-height, inherit);
|
|
186
261
|
margin: var(--input-label-msg-text-margin, 0px 0px 6px 0px);
|
|
187
262
|
}
|
|
188
263
|
|
|
@@ -22,7 +22,16 @@ export type OptionalInputButtonProperties = {
|
|
|
22
22
|
leftIcon?: Snippet;
|
|
23
23
|
rightIcon?: Snippet;
|
|
24
24
|
classes?: string;
|
|
25
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Marks the field required: native `required` and `aria-required` on the
|
|
27
|
+
* input the user reaches, plus a decorative asterisk beside the label.
|
|
28
|
+
*/
|
|
29
|
+
required?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* @deprecated Use `required`, the platform's name for this. Kept working as
|
|
32
|
+
* an alias. It previously drew the asterisk without making the field
|
|
33
|
+
* required; it now does both, matching the `Input` it wraps.
|
|
34
|
+
*/
|
|
26
35
|
mandatory?: boolean;
|
|
27
36
|
/** Preset height/padding size variant. Overridable per-size via CSS variables. */
|
|
28
37
|
size?: InputButtonSize;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { LabelProperties } from './properties';
|
|
3
|
+
|
|
4
|
+
// `for` can't be a destructured binding name -- it's a reserved word -- so
|
|
5
|
+
// it's pulled in under `htmlFor` and re-emitted as the real `for` attribute
|
|
6
|
+
// below, which is what actually wires up the native label/control
|
|
7
|
+
// association.
|
|
8
|
+
let { for: htmlFor, required = false, children, testId, classes }: LabelProperties = $props();
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<label
|
|
12
|
+
class="label {classes ?? ''}"
|
|
13
|
+
for={htmlFor}
|
|
14
|
+
data-pw={typeof testId === 'string' ? testId : null}
|
|
15
|
+
testID={typeof testId === 'string' ? testId : null}
|
|
16
|
+
>
|
|
17
|
+
{@render children?.()}
|
|
18
|
+
{#if required}
|
|
19
|
+
<span class="required-marker" aria-hidden="true">*</span>
|
|
20
|
+
<span class="sr-only">required</span>
|
|
21
|
+
{/if}
|
|
22
|
+
</label>
|
|
23
|
+
|
|
24
|
+
<style>
|
|
25
|
+
.label {
|
|
26
|
+
display: var(--label-display, inline-flex);
|
|
27
|
+
align-items: var(--label-align-items, center);
|
|
28
|
+
gap: var(--label-gap, 4px);
|
|
29
|
+
font-size: var(--label-font-size, 14px);
|
|
30
|
+
font-weight: var(--label-font-weight, 500);
|
|
31
|
+
color: var(--label-color, #333);
|
|
32
|
+
font-family: var(--label-font-family, inherit);
|
|
33
|
+
cursor: var(--label-cursor, pointer);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.required-marker {
|
|
37
|
+
color: var(--label-required-color, #d32f2f);
|
|
38
|
+
font-size: var(--label-required-font-size, inherit);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* Visually hidden but still read by assistive technology -- same pattern
|
|
42
|
+
Table's caption uses. */
|
|
43
|
+
.sr-only {
|
|
44
|
+
position: absolute;
|
|
45
|
+
width: 1px;
|
|
46
|
+
height: 1px;
|
|
47
|
+
padding: 0;
|
|
48
|
+
overflow: hidden;
|
|
49
|
+
clip: rect(0, 0, 0, 0);
|
|
50
|
+
clip-path: inset(50%);
|
|
51
|
+
white-space: nowrap;
|
|
52
|
+
border-width: 0;
|
|
53
|
+
}
|
|
54
|
+
</style>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
export type LabelProperties = OptionalLabelProperties;
|
|
3
|
+
export type MandatoryLabelProperties = Record<string, never>;
|
|
4
|
+
export type OptionalLabelProperties = {
|
|
5
|
+
/**
|
|
6
|
+
* Id of the form control this label names. Rendered as the real `for`
|
|
7
|
+
* attribute on a real `<label>` element, so a click anywhere on the label
|
|
8
|
+
* focuses (and, for a checkbox/radio, activates) the control with this id
|
|
9
|
+
* -- the native association a `<span>` styled to look like a label can
|
|
10
|
+
* never give you. Omit it when the label instead wraps its control
|
|
11
|
+
* (implicit association), or has no associated control at all.
|
|
12
|
+
*/
|
|
13
|
+
for?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Marks the named field as required. Shown as a visual asterisk, but the
|
|
16
|
+
* asterisk alone is `aria-hidden` -- a screen reader would otherwise either
|
|
17
|
+
* skip it silently or read the bare glyph "asterisk", neither of which says
|
|
18
|
+
* "required". A visually-hidden `"required"` string is rendered alongside
|
|
19
|
+
* it so the requirement is actually announced, not just conveyed by shape.
|
|
20
|
+
*/
|
|
21
|
+
required?: boolean;
|
|
22
|
+
/** The label's text/content. */
|
|
23
|
+
children?: Snippet;
|
|
24
|
+
testId?: string;
|
|
25
|
+
classes?: string;
|
|
26
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|