@juspay/svelte-ui-components 4.27.6 → 4.28.1
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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { describeField } from '../_field/description';
|
|
2
3
|
import type { SliderProperties } from './properties';
|
|
3
4
|
|
|
4
5
|
let {
|
|
@@ -14,11 +15,47 @@
|
|
|
14
15
|
testId,
|
|
15
16
|
onchange,
|
|
16
17
|
oninput,
|
|
17
|
-
classes
|
|
18
|
+
classes,
|
|
19
|
+
ariaValueText,
|
|
20
|
+
name,
|
|
21
|
+
form,
|
|
22
|
+
errorMessage,
|
|
23
|
+
infoMessage,
|
|
24
|
+
invalid = false
|
|
18
25
|
}: SliderProperties = $props();
|
|
19
26
|
|
|
20
|
-
|
|
27
|
+
/* The control and the text that explains it were never linked: a screen-reader
|
|
28
|
+
user reaching this control heard its name and nothing about why it was
|
|
29
|
+
rejected. `describeField` composes the reference from the messages that are
|
|
30
|
+
actually rendered, so aria-describedby never points at an id that is not in
|
|
31
|
+
the DOM -- which passes an attribute assertion and resolves to nothing in a
|
|
32
|
+
real reader. */
|
|
33
|
+
const fieldUid = $props.id();
|
|
34
|
+
const field = $derived(
|
|
35
|
+
describeField(fieldUid, { error: errorMessage, info: infoMessage, invalid })
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// Only the paint is clamped. The value stays exactly what the consumer passed, so a
|
|
39
|
+
// slider handed a number outside its own bounds reports that number rather than
|
|
40
|
+
// being silently corrected — but the track never renders a negative, >100% or NaN
|
|
41
|
+
// fill. An unusable range (empty, reversed, non-finite, or one whose width or
|
|
42
|
+
// quotient overflows to Infinity) has no meaningful fill, so it paints none.
|
|
43
|
+
const percentage: number = $derived.by(() => {
|
|
44
|
+
const span = max - min;
|
|
45
|
+
if (![value, min, max, span].every((part) => Number.isFinite(part)) || span <= 0) {
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
const raw = ((value - min) / span) * 100;
|
|
49
|
+
return Number.isFinite(raw) ? Math.min(100, Math.max(0, raw)) : 0;
|
|
50
|
+
});
|
|
21
51
|
let displayValue = $derived(labelFormatter ? labelFormatter(value) : String(value));
|
|
52
|
+
const valueText: string | null = $derived(
|
|
53
|
+
typeof ariaValueText === 'string'
|
|
54
|
+
? ariaValueText
|
|
55
|
+
: labelFormatter
|
|
56
|
+
? labelFormatter(value)
|
|
57
|
+
: null
|
|
58
|
+
);
|
|
22
59
|
|
|
23
60
|
function handleInput(e: Event) {
|
|
24
61
|
if (e.target instanceof HTMLInputElement) {
|
|
@@ -38,12 +75,18 @@
|
|
|
38
75
|
<div class="slider-container {classes ?? ''}" class:disabled>
|
|
39
76
|
<input
|
|
40
77
|
type="range"
|
|
78
|
+
aria-describedby={field.describedBy}
|
|
79
|
+
aria-invalid={field.ariaInvalid}
|
|
41
80
|
class="slider-input"
|
|
42
81
|
{min}
|
|
43
82
|
{max}
|
|
44
83
|
{step}
|
|
45
84
|
{value}
|
|
46
85
|
{disabled}
|
|
86
|
+
{name}
|
|
87
|
+
form={typeof form === 'string' ? form : null}
|
|
88
|
+
aria-valuetext={valueText}
|
|
89
|
+
data-disabled={disabled ? '' : null}
|
|
47
90
|
aria-label={typeof ariaLabel === 'string' ? ariaLabel : null}
|
|
48
91
|
aria-labelledby={typeof ariaLabel === 'string' ? null : (ariaLabelledby ?? null)}
|
|
49
92
|
data-pw={typeof testId === 'string' ? testId : null}
|
|
@@ -57,6 +100,28 @@
|
|
|
57
100
|
{/if}
|
|
58
101
|
</div>
|
|
59
102
|
|
|
103
|
+
{#if field.showsError}
|
|
104
|
+
<div
|
|
105
|
+
id={field.errorId}
|
|
106
|
+
role="alert"
|
|
107
|
+
class="field-error"
|
|
108
|
+
data-pw={typeof testId === 'string' ? `${testId}-error-message` : null}
|
|
109
|
+
testID={typeof testId === 'string' ? `${testId}-error-message` : null}
|
|
110
|
+
>
|
|
111
|
+
{errorMessage}
|
|
112
|
+
</div>
|
|
113
|
+
{/if}
|
|
114
|
+
{#if field.showsInfo}
|
|
115
|
+
<div
|
|
116
|
+
id={field.infoId}
|
|
117
|
+
class="field-info"
|
|
118
|
+
data-pw={typeof testId === 'string' ? `${testId}-info-message` : null}
|
|
119
|
+
testID={typeof testId === 'string' ? `${testId}-info-message` : null}
|
|
120
|
+
>
|
|
121
|
+
{infoMessage}
|
|
122
|
+
</div>
|
|
123
|
+
{/if}
|
|
124
|
+
|
|
60
125
|
<style>
|
|
61
126
|
.slider-container {
|
|
62
127
|
display: flex;
|
|
@@ -109,8 +174,10 @@
|
|
|
109
174
|
opacity: var(--slider-thumb-opacity, 1);
|
|
110
175
|
cursor: pointer;
|
|
111
176
|
transition:
|
|
112
|
-
transform 0.15s
|
|
113
|
-
|
|
177
|
+
transform var(--slider-thumb-transition-duration, var(--motion-duration, 0.15s))
|
|
178
|
+
var(--slider-thumb-transition-easing, var(--motion-easing, ease)),
|
|
179
|
+
opacity var(--slider-thumb-transition-duration, var(--motion-duration, 0.15s))
|
|
180
|
+
var(--slider-thumb-transition-easing, var(--motion-easing, ease));
|
|
114
181
|
}
|
|
115
182
|
|
|
116
183
|
.slider-input::-moz-range-thumb {
|
|
@@ -123,8 +190,10 @@
|
|
|
123
190
|
opacity: var(--slider-thumb-opacity, 1);
|
|
124
191
|
cursor: pointer;
|
|
125
192
|
transition:
|
|
126
|
-
transform 0.15s
|
|
127
|
-
|
|
193
|
+
transform var(--slider-thumb-transition-duration, var(--motion-duration, 0.15s))
|
|
194
|
+
var(--slider-thumb-transition-easing, var(--motion-easing, ease)),
|
|
195
|
+
opacity var(--slider-thumb-transition-duration, var(--motion-duration, 0.15s))
|
|
196
|
+
var(--slider-thumb-transition-easing, var(--motion-easing, ease));
|
|
128
197
|
}
|
|
129
198
|
|
|
130
199
|
.slider-input:hover::-webkit-slider-thumb {
|
|
@@ -159,4 +228,16 @@
|
|
|
159
228
|
color: var(--slider-value-color, #333333);
|
|
160
229
|
white-space: nowrap;
|
|
161
230
|
}
|
|
231
|
+
|
|
232
|
+
.field-error {
|
|
233
|
+
color: var(--field-error-color, #c5120a);
|
|
234
|
+
font-size: var(--field-error-font-size, 12px);
|
|
235
|
+
margin: var(--field-error-margin, 4px 0 0 0);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.field-info {
|
|
239
|
+
color: var(--field-info-color, #6b7280);
|
|
240
|
+
font-size: var(--field-info-font-size, 12px);
|
|
241
|
+
margin: var(--field-info-margin, 4px 0 0 0);
|
|
242
|
+
}
|
|
162
243
|
</style>
|
|
@@ -3,6 +3,22 @@ export type MandatorySliderProperties = {
|
|
|
3
3
|
value: number;
|
|
4
4
|
};
|
|
5
5
|
export type OptionalSliderProperties = {
|
|
6
|
+
/**
|
|
7
|
+
* Text shown, and announced, when the control is in error. Linked to the
|
|
8
|
+
* control through `aria-describedby`, and sets `aria-invalid` while present.
|
|
9
|
+
*/
|
|
10
|
+
errorMessage?: string | null;
|
|
11
|
+
/**
|
|
12
|
+
* Persistent helper text describing the control. Linked through
|
|
13
|
+
* `aria-describedby` too, so it is read before the user trips an error rather
|
|
14
|
+
* than only after.
|
|
15
|
+
*/
|
|
16
|
+
infoMessage?: string | null;
|
|
17
|
+
/**
|
|
18
|
+
* Marks the control invalid without supplying a message, for a consumer
|
|
19
|
+
* driving validity from a server or its own rules.
|
|
20
|
+
*/
|
|
21
|
+
invalid?: boolean;
|
|
6
22
|
min?: number;
|
|
7
23
|
max?: number;
|
|
8
24
|
step?: number;
|
|
@@ -23,6 +39,16 @@ export type OptionalSliderProperties = {
|
|
|
23
39
|
ariaLabelledby?: string;
|
|
24
40
|
testId?: string;
|
|
25
41
|
classes?: string;
|
|
42
|
+
/** Spoken in place of the raw number, for a value whose meaning the digits do not
|
|
43
|
+
* carry ("Quiet", "Large"). Falls back to `labelFormatter(value)` when that is
|
|
44
|
+
* supplied, and is omitted entirely when neither is. An explicit empty string is
|
|
45
|
+
* honoured rather than treated as absent. */
|
|
46
|
+
ariaValueText?: string;
|
|
47
|
+
/** Submits the current value under this name. Omitted while disabled. */
|
|
48
|
+
name?: string;
|
|
49
|
+
/** `id` of a form elsewhere in the same document. Unavailable through
|
|
50
|
+
* `<sui-slider>`, whose input sits in a shadow root. */
|
|
51
|
+
form?: string;
|
|
26
52
|
};
|
|
27
53
|
export type SliderEventProperties = {
|
|
28
54
|
onchange?: (value: number) => void;
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
copiedLabel = 'Copied!',
|
|
15
15
|
copyResetMs = 2000,
|
|
16
16
|
oncopy,
|
|
17
|
+
onerror,
|
|
17
18
|
classes
|
|
18
19
|
}: SnippetProperties = $props();
|
|
19
20
|
|
|
@@ -29,6 +30,9 @@
|
|
|
29
30
|
},
|
|
30
31
|
get oncopy() {
|
|
31
32
|
return oncopy;
|
|
33
|
+
},
|
|
34
|
+
get onerror() {
|
|
35
|
+
return onerror;
|
|
32
36
|
}
|
|
33
37
|
});
|
|
34
38
|
onDestroy(copyState.destroy);
|
|
@@ -31,6 +31,17 @@ export type CopyStateOptions = {
|
|
|
31
31
|
copyResetMs?: number;
|
|
32
32
|
/** Called once per successful copy. Never called when the write fails. */
|
|
33
33
|
oncopy?: () => void;
|
|
34
|
+
/**
|
|
35
|
+
* Called instead of `oncopy` when a write cannot complete -- a rejection
|
|
36
|
+
* (denied permission, a non-secure context) or a Clipboard API that is
|
|
37
|
+
* absent entirely (SSR, a sandboxed iframe). Receives the rejection
|
|
38
|
+
* reason, or an `Error` when the API itself is missing.
|
|
39
|
+
*
|
|
40
|
+
* Without it the caller sees only `copy()` resolving `false` and cannot
|
|
41
|
+
* distinguish "refused" from "no clipboard here", which is the difference
|
|
42
|
+
* between a message worth showing the user and one worth suppressing.
|
|
43
|
+
*/
|
|
44
|
+
onerror?: (reason: unknown) => void;
|
|
34
45
|
};
|
|
35
46
|
export type CopyState = {
|
|
36
47
|
/** True from a successful copy until the reset delay elapses. */
|
|
@@ -38,7 +49,8 @@ export type CopyState = {
|
|
|
38
49
|
/**
|
|
39
50
|
* Writes `text` to the clipboard. Resolves `true` on success, `false` if
|
|
40
51
|
* the clipboard was unavailable or refused — it never rejects, so a caller
|
|
41
|
-
* needs no try/catch of its own.
|
|
52
|
+
* needs no try/catch of its own. A `false` outcome is also reported to
|
|
53
|
+
* `onerror` with the reason.
|
|
42
54
|
*/
|
|
43
55
|
copy: (text: string) => Promise<boolean>;
|
|
44
56
|
/** Cancels any pending reset. Call from the owner's teardown. */
|
|
@@ -20,6 +20,20 @@ function resolveResetDelay(options) {
|
|
|
20
20
|
}
|
|
21
21
|
return requested;
|
|
22
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* `onerror` is a consumer callback like `oncopy`, so it gets the same
|
|
25
|
+
* containment: a throwing reporter must not turn a handled failure into an
|
|
26
|
+
* unhandled rejection out of `copy()`, which is the one thing this factory
|
|
27
|
+
* promises never to do.
|
|
28
|
+
*/
|
|
29
|
+
function report(options, reason) {
|
|
30
|
+
try {
|
|
31
|
+
options.onerror?.(reason);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
/* A failed notification cannot make the clipboard write any less failed. */
|
|
35
|
+
}
|
|
36
|
+
}
|
|
23
37
|
export function createCopyState(options = {}) {
|
|
24
38
|
let copied = $state(false);
|
|
25
39
|
let destroyed = false;
|
|
@@ -32,14 +46,26 @@ export function createCopyState(options = {}) {
|
|
|
32
46
|
if (destroyed) {
|
|
33
47
|
return false;
|
|
34
48
|
}
|
|
49
|
+
// Checked before the call rather than relying on the catch below: with
|
|
50
|
+
// no `navigator.clipboard` at all, `writeText` is a property access on
|
|
51
|
+
// undefined, and the resulting TypeError says nothing a caller could
|
|
52
|
+
// act on. An explicit reason distinguishes "this context has no
|
|
53
|
+
// clipboard" from "the user refused". `navigator` is tested for null
|
|
54
|
+
// separately from `undefined`: a stubbed-out global is routinely the
|
|
55
|
+
// former, and `!navigator.clipboard` would throw on it.
|
|
56
|
+
if (typeof navigator === 'undefined' || navigator === null || !navigator.clipboard) {
|
|
57
|
+
report(options, new Error('Clipboard API is unavailable in this context.'));
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
35
60
|
try {
|
|
36
61
|
await navigator.clipboard.writeText(text);
|
|
37
62
|
}
|
|
38
|
-
catch {
|
|
63
|
+
catch (reason) {
|
|
39
64
|
/* Clipboard unavailable: non-secure context, sandboxed iframe, or a
|
|
40
65
|
permission refusal. Reporting success here — and flashing "Copied!"
|
|
41
66
|
over a clipboard that never received the text — is the one outcome
|
|
42
67
|
worse than showing no feedback at all. */
|
|
68
|
+
report(options, reason);
|
|
43
69
|
return false;
|
|
44
70
|
}
|
|
45
71
|
// The clipboard promise can settle after its owner has unmounted.
|
|
@@ -16,4 +16,11 @@ export type OptionalSnippetProperties = {
|
|
|
16
16
|
};
|
|
17
17
|
export type SnippetEventProperties = {
|
|
18
18
|
oncopy?: () => void;
|
|
19
|
+
/**
|
|
20
|
+
* Fires when a clipboard write fails -- rejected (denied permission, a
|
|
21
|
+
* non-secure context) or the Clipboard API being entirely absent -- with
|
|
22
|
+
* the rejection reason (an `Error` when the API itself is missing).
|
|
23
|
+
* `oncopy` does not fire for the same attempt.
|
|
24
|
+
*/
|
|
25
|
+
onerror?: (reason: unknown) => void;
|
|
19
26
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { closestInEventPath } from '../_interaction/dismissal';
|
|
1
2
|
const DEFAULT_STORAGE_KEY = 'sui-sound-enabled';
|
|
2
3
|
const DEFAULT_MASTER_GAIN = 0.32;
|
|
3
4
|
const SOUND_ATTRIBUTE = 'data-sound';
|
|
@@ -216,11 +217,11 @@ export const createSoundKit = (options = {}) => {
|
|
|
216
217
|
if (!resolveEnabled()) {
|
|
217
218
|
return;
|
|
218
219
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const overrideElement =
|
|
220
|
+
// This listener sits on `document`, so `event.target` is the shadow HOST for
|
|
221
|
+
// any click inside a `<sui-*>` element and `target.closest()` would search
|
|
222
|
+
// the light tree instead of the one that was clicked -- no sound for every
|
|
223
|
+
// custom-element click, or the wrong sound inherited from a light ancestor.
|
|
224
|
+
const overrideElement = closestInEventPath(event, `[${SOUND_ATTRIBUTE}]`);
|
|
224
225
|
if (overrideElement !== null) {
|
|
225
226
|
const overrideValue = overrideElement.getAttribute(SOUND_ATTRIBUTE) ?? '';
|
|
226
227
|
const resolved = resolveOverride(overrideValue);
|
|
@@ -229,15 +230,15 @@ export const createSoundKit = (options = {}) => {
|
|
|
229
230
|
}
|
|
230
231
|
return;
|
|
231
232
|
}
|
|
232
|
-
if (
|
|
233
|
+
if (closestInEventPath(event, TICK_SELECTOR) !== null) {
|
|
233
234
|
play('tick');
|
|
234
235
|
return;
|
|
235
236
|
}
|
|
236
|
-
if (
|
|
237
|
+
if (closestInEventPath(event, PAGE_SELECTOR) !== null) {
|
|
237
238
|
play('page');
|
|
238
239
|
return;
|
|
239
240
|
}
|
|
240
|
-
if (
|
|
241
|
+
if (closestInEventPath(event, PRESS_SELECTOR) !== null) {
|
|
241
242
|
play('press');
|
|
242
243
|
}
|
|
243
244
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { tick } from 'svelte';
|
|
3
|
+
import { describeField } from '../_field/description';
|
|
3
4
|
import Input from '../Input/Input.svelte';
|
|
4
5
|
import type { FieldConfig, SplitInputProperties } from './properties';
|
|
5
6
|
|
|
@@ -12,11 +13,34 @@
|
|
|
12
13
|
separator,
|
|
13
14
|
testId,
|
|
14
15
|
classes,
|
|
16
|
+
ariaLabel,
|
|
17
|
+
errorMessage,
|
|
18
|
+
infoMessage,
|
|
19
|
+
invalid = false,
|
|
15
20
|
onchange,
|
|
16
21
|
oninput,
|
|
17
22
|
oncomplete
|
|
18
23
|
}: SplitInputProperties = $props();
|
|
19
24
|
|
|
25
|
+
/* "That code is not right" is a fact about the whole code, not about the box
|
|
26
|
+
the caret happens to be in, so the description belongs on the group -- and
|
|
27
|
+
there was none: a screen-reader user got four unlabelled boxes and no way to
|
|
28
|
+
reach the message. `describeField` composes the reference from the messages
|
|
29
|
+
that are actually rendered, so aria-describedby never points at an id that
|
|
30
|
+
is not in the DOM -- which passes an attribute assertion and resolves to
|
|
31
|
+
nothing in a real reader.
|
|
32
|
+
|
|
33
|
+
The role is conditional so a SplitInput with no messages and no label keeps
|
|
34
|
+
the accessibility tree it had before these props existed. */
|
|
35
|
+
const fieldUid = $props.id();
|
|
36
|
+
const field = $derived(
|
|
37
|
+
describeField(fieldUid, { error: errorMessage, info: infoMessage, invalid })
|
|
38
|
+
);
|
|
39
|
+
const hasGroupLabel = $derived(typeof ariaLabel === 'string' && ariaLabel !== '');
|
|
40
|
+
const describedGroup = $derived(
|
|
41
|
+
field.describedBy !== null || field.ariaInvalid !== null || hasGroupLabel
|
|
42
|
+
);
|
|
43
|
+
|
|
20
44
|
let fieldCount = $derived(typeof fields !== 'undefined' ? fields.length : length);
|
|
21
45
|
|
|
22
46
|
let fieldConfigs: FieldConfig[] = $derived(
|
|
@@ -223,7 +247,15 @@
|
|
|
223
247
|
}
|
|
224
248
|
</script>
|
|
225
249
|
|
|
226
|
-
<div
|
|
250
|
+
<div
|
|
251
|
+
class="field-group {classes ?? ''}"
|
|
252
|
+
role={describedGroup ? 'group' : null}
|
|
253
|
+
aria-label={hasGroupLabel ? ariaLabel : null}
|
|
254
|
+
aria-describedby={field.describedBy}
|
|
255
|
+
aria-invalid={field.ariaInvalid}
|
|
256
|
+
data-pw={testId}
|
|
257
|
+
testID={testId}
|
|
258
|
+
>
|
|
227
259
|
{#each fieldConfigs as config, index (index)}
|
|
228
260
|
{#if typeof separator === 'string' && index > 0}
|
|
229
261
|
<span class="field-group-separator">{separator}</span>
|
|
@@ -256,7 +288,41 @@
|
|
|
256
288
|
{/each}
|
|
257
289
|
</div>
|
|
258
290
|
|
|
291
|
+
{#if field.showsError}
|
|
292
|
+
<div
|
|
293
|
+
id={field.errorId}
|
|
294
|
+
role="alert"
|
|
295
|
+
class="field-error"
|
|
296
|
+
data-pw={typeof testId === 'string' ? `${testId}-error-message` : null}
|
|
297
|
+
testID={typeof testId === 'string' ? `${testId}-error-message` : null}
|
|
298
|
+
>
|
|
299
|
+
{errorMessage}
|
|
300
|
+
</div>
|
|
301
|
+
{/if}
|
|
302
|
+
{#if field.showsInfo}
|
|
303
|
+
<div
|
|
304
|
+
id={field.infoId}
|
|
305
|
+
class="field-info"
|
|
306
|
+
data-pw={typeof testId === 'string' ? `${testId}-info-message` : null}
|
|
307
|
+
testID={typeof testId === 'string' ? `${testId}-info-message` : null}
|
|
308
|
+
>
|
|
309
|
+
{infoMessage}
|
|
310
|
+
</div>
|
|
311
|
+
{/if}
|
|
312
|
+
|
|
259
313
|
<style>
|
|
314
|
+
.field-error {
|
|
315
|
+
color: var(--field-error-color, #c5120a);
|
|
316
|
+
font-size: var(--field-error-font-size, 12px);
|
|
317
|
+
margin: var(--field-error-margin, 4px 0 0 0);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.field-info {
|
|
321
|
+
color: var(--field-info-color, #6b7280);
|
|
322
|
+
font-size: var(--field-info-font-size, 12px);
|
|
323
|
+
margin: var(--field-info-margin, 4px 0 0 0);
|
|
324
|
+
}
|
|
325
|
+
|
|
260
326
|
.field-group {
|
|
261
327
|
display: flex;
|
|
262
328
|
align-items: flex-start;
|
|
@@ -291,7 +357,7 @@
|
|
|
291
357
|
.field-group-label {
|
|
292
358
|
font-size: var(--field-group-label-font-size, 10px);
|
|
293
359
|
font-weight: var(--field-group-label-font-weight, 500);
|
|
294
|
-
color: var(--field-group-label-color, #
|
|
360
|
+
color: var(--field-group-label-color, #4b5563);
|
|
295
361
|
text-transform: uppercase;
|
|
296
362
|
letter-spacing: 0.05em;
|
|
297
363
|
}
|
|
@@ -12,6 +12,29 @@ export type OptionalSplitInputProperties = {
|
|
|
12
12
|
separator?: string;
|
|
13
13
|
testId?: string;
|
|
14
14
|
classes?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Names the whole group ("One-time code"). The boxes are individually
|
|
17
|
+
* meaningless, so without this a screen-reader user reaches four unlabelled
|
|
18
|
+
* fields; naming the group is what makes them one control.
|
|
19
|
+
*/
|
|
20
|
+
ariaLabel?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Text shown, and announced, when the assembled value is in error ("That code
|
|
23
|
+
* is not right"). Referenced by `aria-describedby` on the group rather than on
|
|
24
|
+
* one box, since the message is about the whole code, and sets `aria-invalid`
|
|
25
|
+
* while present.
|
|
26
|
+
*/
|
|
27
|
+
errorMessage?: string | null;
|
|
28
|
+
/**
|
|
29
|
+
* Persistent helper text describing the whole group. Referenced the same way,
|
|
30
|
+
* so it is read before the user trips an error rather than only after.
|
|
31
|
+
*/
|
|
32
|
+
infoMessage?: string | null;
|
|
33
|
+
/**
|
|
34
|
+
* Marks the group invalid without supplying a message, for a consumer driving
|
|
35
|
+
* validity from a server or its own rules.
|
|
36
|
+
*/
|
|
37
|
+
invalid?: boolean;
|
|
15
38
|
};
|
|
16
39
|
export type SplitInputEventProperties = {
|
|
17
40
|
onchange?: (values: string[]) => void;
|
|
@@ -444,7 +444,7 @@
|
|
|
444
444
|
}
|
|
445
445
|
|
|
446
446
|
.statcard-value-warning {
|
|
447
|
-
color: var(--statcard-value-warning-color, #
|
|
447
|
+
color: var(--statcard-value-warning-color, #b45309);
|
|
448
448
|
}
|
|
449
449
|
|
|
450
450
|
/* Per-row value typography override — falls back to the card-level value
|
|
@@ -465,7 +465,7 @@
|
|
|
465
465
|
}
|
|
466
466
|
|
|
467
467
|
.statcard-delta-positive {
|
|
468
|
-
color: var(--statcard-delta-positive-color, #
|
|
468
|
+
color: var(--statcard-delta-positive-color, #166534);
|
|
469
469
|
}
|
|
470
470
|
|
|
471
471
|
.statcard-delta-negative {
|
|
@@ -490,7 +490,7 @@
|
|
|
490
490
|
order: var(--statcard-subtitle-order, 0);
|
|
491
491
|
font-size: var(--statcard-subtitle-font-size, 12px);
|
|
492
492
|
font-weight: var(--statcard-subtitle-font-weight, 400);
|
|
493
|
-
color: var(--statcard-subtitle-color, #
|
|
493
|
+
color: var(--statcard-subtitle-color, #4b5563);
|
|
494
494
|
line-height: var(--statcard-subtitle-line-height, 1.4);
|
|
495
495
|
}
|
|
496
496
|
|
|
@@ -504,7 +504,7 @@
|
|
|
504
504
|
--statcard-row-subtitle-font-weight,
|
|
505
505
|
var(--statcard-subtitle-font-weight, 400)
|
|
506
506
|
);
|
|
507
|
-
color: var(--statcard-row-subtitle-color, var(--statcard-subtitle-color, #
|
|
507
|
+
color: var(--statcard-row-subtitle-color, var(--statcard-subtitle-color, #4b5563));
|
|
508
508
|
line-height: var(
|
|
509
509
|
--statcard-row-subtitle-line-height,
|
|
510
510
|
var(--statcard-subtitle-line-height, 1.4)
|
|
@@ -582,7 +582,7 @@
|
|
|
582
582
|
does not fit — matches short unit suffixes like "%" or "ms". */
|
|
583
583
|
font-size: var(--statcard-row-additional-font-size, 12px);
|
|
584
584
|
font-weight: var(--statcard-row-additional-font-weight, 400);
|
|
585
|
-
color: var(--statcard-row-additional-color, #
|
|
585
|
+
color: var(--statcard-row-additional-color, #4b5563);
|
|
586
586
|
line-height: 1.4;
|
|
587
587
|
}
|
|
588
588
|
|
|
@@ -631,7 +631,7 @@
|
|
|
631
631
|
.statcard-breakdown-label {
|
|
632
632
|
font-size: var(--statcard-breakdown-label-font-size, 11px);
|
|
633
633
|
font-weight: var(--statcard-breakdown-label-font-weight, 400);
|
|
634
|
-
color: var(--statcard-breakdown-label-color, #
|
|
634
|
+
color: var(--statcard-breakdown-label-color, #4b5563);
|
|
635
635
|
line-height: 1.4;
|
|
636
636
|
}
|
|
637
637
|
|
|
@@ -92,12 +92,12 @@
|
|
|
92
92
|
typography applies, so those two declarations must not sit on the element and win. */
|
|
93
93
|
.status-text-default {
|
|
94
94
|
font-weight: var(--status-font-weight, 600);
|
|
95
|
-
color: var(--status-description-font-color, #
|
|
95
|
+
color: var(--status-description-font-color, light-dark(#3d5a76, #9ab4cc));
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
.status-description {
|
|
99
99
|
font-weight: var(--status-font-weight, 400);
|
|
100
|
-
color: var(--status-description-font-color, #
|
|
100
|
+
color: var(--status-description-font-color, light-dark(#3d5a76, #9ab4cc));
|
|
101
101
|
padding: 0px 42px;
|
|
102
102
|
margin-bottom: 25px;
|
|
103
103
|
}
|
package/dist/Stepper/Step.svelte
CHANGED
|
@@ -138,10 +138,10 @@
|
|
|
138
138
|
tokens Step actually renders are set here; the separator is Stepper's to draw. Each
|
|
139
139
|
chain terminates in the same literal Stepper uses, so the two agree by construction. */
|
|
140
140
|
.status-completed {
|
|
141
|
-
--step-text-color: var(--step-text-completed-color, #
|
|
141
|
+
--step-text-color: var(--step-text-completed-color, #1a7a44);
|
|
142
142
|
--step-index-container-background-color: var(
|
|
143
143
|
--step-index-container-completed-background-color,
|
|
144
|
-
#
|
|
144
|
+
#1a7a44
|
|
145
145
|
);
|
|
146
146
|
--step-index-container-border: var(--step-index-container-completed-border, none);
|
|
147
147
|
}
|
|
@@ -198,7 +198,7 @@
|
|
|
198
198
|
height: var(--step-index-container-height, 30px);
|
|
199
199
|
width: var(--step-index-container-width, 30px);
|
|
200
200
|
border-radius: var(--step-index-container-radius, 50%);
|
|
201
|
-
background-color: var(--step-index-container-background-color, #
|
|
201
|
+
background-color: var(--step-index-container-background-color, #55627a);
|
|
202
202
|
/* No border today, so the fallback is `none` — an existing consumer that sets
|
|
203
203
|
nothing renders byte-identically. Shared with .step-icon-container below so
|
|
204
204
|
a status override (see Stepper.svelte) themes the circle whichever of the
|
|
@@ -215,7 +215,7 @@
|
|
|
215
215
|
height: var(--step-index-container-height, 30px);
|
|
216
216
|
width: var(--step-index-container-width, 30px);
|
|
217
217
|
border-radius: var(--step-index-container-radius, 50%);
|
|
218
|
-
background-color: var(--step-index-container-background-color, #
|
|
218
|
+
background-color: var(--step-index-container-background-color, #55627a);
|
|
219
219
|
border: var(--step-index-container-border, none);
|
|
220
220
|
flex-shrink: 0;
|
|
221
221
|
overflow: hidden;
|
|
@@ -230,7 +230,8 @@
|
|
|
230
230
|
.step-spinner {
|
|
231
231
|
width: var(--step-spinner-size, 18px);
|
|
232
232
|
height: var(--step-spinner-size, 18px);
|
|
233
|
-
animation: stepper-spin 0.8s
|
|
233
|
+
animation: stepper-spin var(--step-spinner-animation-duration, var(--motion-duration, 0.8s))
|
|
234
|
+
var(--step-spinner-animation-easing, var(--motion-easing, linear)) infinite;
|
|
234
235
|
}
|
|
235
236
|
|
|
236
237
|
@keyframes stepper-spin {
|
|
@@ -299,7 +300,7 @@
|
|
|
299
300
|
`normal` — that is the literal fallback, keeping an existing consumer
|
|
300
301
|
byte-identical. */
|
|
301
302
|
font-weight: var(--step-text-font-weight, normal);
|
|
302
|
-
color: var(--step-text-color, #
|
|
303
|
+
color: var(--step-text-color, #55627a);
|
|
303
304
|
}
|
|
304
305
|
|
|
305
306
|
.step-vertical .step-text {
|
|
@@ -318,4 +319,16 @@
|
|
|
318
319
|
.step-vertical .step-badge {
|
|
319
320
|
margin: var(--step-badge-vertical-margin, 4px 0 0 0);
|
|
320
321
|
}
|
|
322
|
+
/* An indefinite animation is the case this preference exists for: it never
|
|
323
|
+
ends, so a user who asked the OS to minimise motion gets a permanent loop.
|
|
324
|
+
The animation stops, but the element must still read as "busy" -- a guard
|
|
325
|
+
that leaves nothing on screen, or leaves a frame that means something else,
|
|
326
|
+
is worse than the motion it removed. This block lives in the component's own
|
|
327
|
+
<style> because that is the only stylesheet that reaches inside the shadow
|
|
328
|
+
root a custom-element consumer gets. */
|
|
329
|
+
@media (prefers-reduced-motion: reduce) {
|
|
330
|
+
.step-spinner {
|
|
331
|
+
animation: none;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
321
334
|
</style>
|