@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 { closestInEventPath } from '../_interaction/dismissal';
|
|
2
3
|
import type { DraggableProperties, DragPosition } from './properties';
|
|
3
4
|
|
|
4
5
|
let {
|
|
@@ -26,18 +27,22 @@
|
|
|
26
27
|
baseY: number;
|
|
27
28
|
} | null = $state(null);
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
// Searched over the event's composed path rather than from `event.target`: a
|
|
31
|
+
// `<sui-button>` nested inside a Draggable retargets to its host, so a plain
|
|
32
|
+
// `closest('button')` misses the real control and the press starts a drag
|
|
33
|
+
// instead of activating it.
|
|
34
|
+
function isInteractive(event: Event): boolean {
|
|
30
35
|
return (
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
closestInEventPath(event, 'input, textarea, select, button, a, [contenteditable="true"]') !==
|
|
37
|
+
null
|
|
33
38
|
);
|
|
34
39
|
}
|
|
35
40
|
|
|
36
|
-
function withinHandle(
|
|
41
|
+
function withinHandle(event: Event): boolean {
|
|
37
42
|
if (typeof handle !== 'string' || handle.length === 0) {
|
|
38
|
-
return !isInteractive(
|
|
43
|
+
return !isInteractive(event);
|
|
39
44
|
}
|
|
40
|
-
return
|
|
45
|
+
return closestInEventPath(event, handle) !== null;
|
|
41
46
|
}
|
|
42
47
|
|
|
43
48
|
function clamp(node: HTMLElement, nextX: number, nextY: number): DragPosition {
|
|
@@ -61,7 +66,7 @@
|
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
function startDrag(event: PointerEvent & { currentTarget: HTMLElement }): void {
|
|
64
|
-
if (disabled || !withinHandle(event
|
|
69
|
+
if (disabled || !withinHandle(event)) {
|
|
65
70
|
return;
|
|
66
71
|
}
|
|
67
72
|
event.preventDefault();
|
|
@@ -94,7 +94,11 @@
|
|
|
94
94
|
if (axisSeries.length === 0) {
|
|
95
95
|
return [0, 1];
|
|
96
96
|
}
|
|
97
|
-
|
|
97
|
+
// Non-finite values (NaN/Infinity) mark gap points; exclude them so one
|
|
98
|
+
// bad point cannot poison the axis extent (Infinity in particular passes
|
|
99
|
+
// right through a Math.max comparison). Matches the LineChart/AreaChart
|
|
100
|
+
// call-site filter.
|
|
101
|
+
const allValues = axisSeries.flatMap((s) => s.data).filter((v) => Number.isFinite(v));
|
|
98
102
|
if (allValues.length === 0) {
|
|
99
103
|
return [0, 1];
|
|
100
104
|
}
|
|
@@ -651,7 +655,8 @@
|
|
|
651
655
|
}
|
|
652
656
|
|
|
653
657
|
.bar-shape {
|
|
654
|
-
transition: opacity var(--chart-transition-duration, 0.2s)
|
|
658
|
+
transition: opacity var(--chart-transition-duration, var(--motion-duration, 0.2s))
|
|
659
|
+
var(--chart-transition-easing, var(--motion-easing, ease));
|
|
655
660
|
cursor: pointer;
|
|
656
661
|
}
|
|
657
662
|
|
|
@@ -672,8 +677,10 @@
|
|
|
672
677
|
|
|
673
678
|
.line-dot {
|
|
674
679
|
transition:
|
|
675
|
-
r var(--chart-transition-duration, 0.2s)
|
|
676
|
-
|
|
680
|
+
r var(--chart-transition-duration, var(--motion-duration, 0.2s))
|
|
681
|
+
var(--chart-transition-easing, var(--motion-easing, ease)),
|
|
682
|
+
opacity var(--chart-transition-duration, var(--motion-duration, 0.2s))
|
|
683
|
+
var(--chart-transition-easing, var(--motion-easing, ease));
|
|
677
684
|
cursor: pointer;
|
|
678
685
|
}
|
|
679
686
|
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
.empty-state-description {
|
|
112
112
|
font-size: var(--empty-state-description-font-size, 14px);
|
|
113
113
|
color: var(--empty-state-description-color, inherit);
|
|
114
|
-
opacity: var(--empty-state-description-opacity, 0.
|
|
114
|
+
opacity: var(--empty-state-description-opacity, 0.8);
|
|
115
115
|
max-width: var(--empty-state-description-max-width, 360px);
|
|
116
116
|
margin-top: 4px;
|
|
117
117
|
}
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
|
|
82
82
|
.file-dropzone-trigger-caption {
|
|
83
83
|
margin: var(--file-dropzone-trigger-caption-margin, 0);
|
|
84
|
-
color: var(--file-dropzone-trigger-caption-color, #
|
|
84
|
+
color: var(--file-dropzone-trigger-caption-color, #f1f5f9);
|
|
85
85
|
font-size: var(--file-dropzone-trigger-caption-font-size, 0.85em);
|
|
86
86
|
}
|
|
87
87
|
</style>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { describeField } from '../_field/description';
|
|
2
3
|
import type { FileInputProperties } from './properties';
|
|
3
4
|
|
|
4
5
|
let {
|
|
@@ -10,9 +11,23 @@
|
|
|
10
11
|
testId,
|
|
11
12
|
classes,
|
|
12
13
|
onfiles,
|
|
13
|
-
onerror
|
|
14
|
+
onerror,
|
|
15
|
+
errorMessage,
|
|
16
|
+
infoMessage,
|
|
17
|
+
invalid
|
|
14
18
|
}: FileInputProperties = $props();
|
|
15
19
|
|
|
20
|
+
/* The drop zone is a `role="button"` with no linked text, so a rejected
|
|
21
|
+
file (wrong type, too large) had nowhere to be announced. It takes
|
|
22
|
+
`aria-describedby` but deliberately not `aria-invalid`: ARIA does not
|
|
23
|
+
support that attribute on `button`, and an unsupported attribute is not a
|
|
24
|
+
harmless extra -- it is one a reader is entitled to ignore. The error text
|
|
25
|
+
itself is the announcement, through `role="alert"`. */
|
|
26
|
+
const fieldUid = $props.id();
|
|
27
|
+
const field = $derived(
|
|
28
|
+
describeField(fieldUid, { error: errorMessage, info: infoMessage, invalid })
|
|
29
|
+
);
|
|
30
|
+
|
|
16
31
|
let dragOver = $state(false);
|
|
17
32
|
let inputEl: HTMLInputElement | null = $state(null);
|
|
18
33
|
let pickerBusy = false;
|
|
@@ -143,6 +158,7 @@
|
|
|
143
158
|
role="button"
|
|
144
159
|
tabindex={disabled ? -1 : 0}
|
|
145
160
|
aria-disabled={disabled}
|
|
161
|
+
aria-describedby={field.describedBy}
|
|
146
162
|
ondragover={handleDragOver}
|
|
147
163
|
ondragleave={handleDragLeave}
|
|
148
164
|
ondrop={handleDrop}
|
|
@@ -166,7 +182,40 @@
|
|
|
166
182
|
{@render trigger({ openFilePicker, dragOver, disabled })}
|
|
167
183
|
</div>
|
|
168
184
|
|
|
185
|
+
{#if field.showsError}
|
|
186
|
+
<div
|
|
187
|
+
id={field.errorId}
|
|
188
|
+
role="alert"
|
|
189
|
+
class="field-error"
|
|
190
|
+
data-pw={typeof testId === 'string' ? `${testId}-error-message` : null}
|
|
191
|
+
testID={typeof testId === 'string' ? `${testId}-error-message` : null}
|
|
192
|
+
>
|
|
193
|
+
{errorMessage}
|
|
194
|
+
</div>
|
|
195
|
+
{/if}
|
|
196
|
+
{#if field.showsInfo}
|
|
197
|
+
<div
|
|
198
|
+
id={field.infoId}
|
|
199
|
+
class="field-info"
|
|
200
|
+
data-pw={typeof testId === 'string' ? `${testId}-info-message` : null}
|
|
201
|
+
testID={typeof testId === 'string' ? `${testId}-info-message` : null}
|
|
202
|
+
>
|
|
203
|
+
{infoMessage}
|
|
204
|
+
</div>
|
|
205
|
+
{/if}
|
|
206
|
+
|
|
169
207
|
<style>
|
|
208
|
+
.field-error {
|
|
209
|
+
color: var(--file-input-error-color, #b3261e);
|
|
210
|
+
font-size: var(--file-input-error-font-size, 12px);
|
|
211
|
+
margin-top: var(--file-input-error-margin-top, 4px);
|
|
212
|
+
}
|
|
213
|
+
.field-info {
|
|
214
|
+
color: var(--file-input-info-color, #5f6368);
|
|
215
|
+
font-size: var(--file-input-info-font-size, 12px);
|
|
216
|
+
margin-top: var(--file-input-info-margin-top, 4px);
|
|
217
|
+
}
|
|
218
|
+
|
|
170
219
|
.file-input {
|
|
171
220
|
display: var(--file-input-display, inline-flex);
|
|
172
221
|
flex-direction: var(--file-input-flex-direction, column);
|
|
@@ -13,6 +13,13 @@ export type FileInputProperties = {
|
|
|
13
13
|
disabled?: boolean;
|
|
14
14
|
testId?: string;
|
|
15
15
|
classes?: string;
|
|
16
|
+
/** Text shown, and announced, when the control is in error. Linked through
|
|
17
|
+
* `aria-describedby`, and sets `aria-invalid` while present. */
|
|
18
|
+
errorMessage?: string | null;
|
|
19
|
+
/** Persistent helper text describing the control, linked the same way. */
|
|
20
|
+
infoMessage?: string | null;
|
|
21
|
+
/** Marks the control invalid without supplying a message. */
|
|
22
|
+
invalid?: boolean;
|
|
16
23
|
onfiles?: (files: File[]) => void;
|
|
17
24
|
onerror?: (message: string) => void;
|
|
18
25
|
};
|
|
@@ -56,6 +56,17 @@
|
|
|
56
56
|
const MARGIN_BOTTOM = 8;
|
|
57
57
|
const MARGIN_LEFT = 8;
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Funnel bars are fixed structural columns (one per stage, always drawn),
|
|
61
|
+
* not sparse samples that can be omitted like a LineChart/AreaChart gap
|
|
62
|
+
* point — so a non-finite stage value is treated as a 0 contribution
|
|
63
|
+
* (this file's own computeStackedValues precedent in geometry.ts for
|
|
64
|
+
* value-accumulating charts), not excluded from the dataset.
|
|
65
|
+
*/
|
|
66
|
+
function safeValue(value: number): number {
|
|
67
|
+
return Number.isFinite(value) ? value : 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
59
70
|
let innerWidth = $derived(Math.max(0, chartWidth - MARGIN_LEFT - MARGIN_RIGHT));
|
|
60
71
|
let innerHeight = $derived(Math.max(0, chartHeight - MARGIN_TOP - MARGIN_BOTTOM));
|
|
61
72
|
|
|
@@ -65,8 +76,14 @@
|
|
|
65
76
|
}
|
|
66
77
|
let max = 0;
|
|
67
78
|
for (const stage of data) {
|
|
68
|
-
|
|
69
|
-
|
|
79
|
+
// A non-finite value contributes nothing to the max (the same "0
|
|
80
|
+
// contribution" treatment computeStackedValues gives a non-finite or
|
|
81
|
+
// negative point) rather than a gap: `Infinity > max` is true, so an
|
|
82
|
+
// unguarded comparison lets one bad stage collapse every bar to the
|
|
83
|
+
// 2px floor.
|
|
84
|
+
const value = safeValue(stage.value);
|
|
85
|
+
if (value > max) {
|
|
86
|
+
max = value;
|
|
70
87
|
}
|
|
71
88
|
}
|
|
72
89
|
return max === 0 ? 1 : max;
|
|
@@ -123,7 +140,10 @@
|
|
|
123
140
|
* Clamped to at least 2px so zero-value stages remain visible.
|
|
124
141
|
*/
|
|
125
142
|
function barHeight(stageValue: number, expandPixels: number = 0): number {
|
|
126
|
-
|
|
143
|
+
// Sanitize before the division: an unguarded NaN/Infinity here reaches
|
|
144
|
+
// barY and connectorPoints (both call this), producing "NaN" SVG
|
|
145
|
+
// coordinates instead of the 2px-floor bar a 0 contribution renders as.
|
|
146
|
+
return Math.max(2, (safeValue(stageValue) / maxValue) * barAreaHeight + expandPixels);
|
|
127
147
|
}
|
|
128
148
|
|
|
129
149
|
/**
|
|
@@ -166,11 +186,15 @@
|
|
|
166
186
|
* otherwise renders `"<value> | <pct>%"`.
|
|
167
187
|
*/
|
|
168
188
|
function formatLabel(stage: FunnelStage): string {
|
|
189
|
+
// Feeds the in-bar label, tooltip, and aria-label alike: sanitizing once
|
|
190
|
+
// here keeps all three reading "0" for a non-finite stage instead of
|
|
191
|
+
// only some of them being patched and the rest still leaking "NaN".
|
|
192
|
+
const value = safeValue(stage.value);
|
|
169
193
|
if (typeof valueFormat === 'function') {
|
|
170
|
-
return valueFormat(
|
|
194
|
+
return valueFormat(value, maxValue);
|
|
171
195
|
}
|
|
172
|
-
const pct = formatPercent(
|
|
173
|
-
return `${formatNumber(
|
|
196
|
+
const pct = formatPercent(value, maxValue);
|
|
197
|
+
return `${formatNumber(value)} | ${pct}`;
|
|
174
198
|
}
|
|
175
199
|
|
|
176
200
|
// ── Label fitting ──────────────────────────────────────────────
|
|
@@ -189,7 +213,7 @@
|
|
|
189
213
|
if (bh >= fullSize.height + 4 && stageColumnWidth >= fullSize.width + 8) {
|
|
190
214
|
return full;
|
|
191
215
|
}
|
|
192
|
-
const compact = formatPercent(stage.value, maxValue);
|
|
216
|
+
const compact = formatPercent(safeValue(stage.value), maxValue);
|
|
193
217
|
const compactSize = measureText(compact, valueFont);
|
|
194
218
|
if (bh >= compactSize.height + 4 && stageColumnWidth >= compactSize.width + 8) {
|
|
195
219
|
return compact;
|
|
@@ -407,9 +431,12 @@
|
|
|
407
431
|
|
|
408
432
|
.funnel-bar {
|
|
409
433
|
transition:
|
|
410
|
-
opacity var(--chart-transition-duration, 0.2s)
|
|
411
|
-
|
|
412
|
-
|
|
434
|
+
opacity var(--chart-transition-duration, var(--motion-duration, 0.2s))
|
|
435
|
+
var(--chart-transition-easing, var(--motion-easing, ease)),
|
|
436
|
+
y var(--chart-transition-duration, var(--motion-duration, 0.2s))
|
|
437
|
+
var(--chart-transition-easing, var(--motion-easing, ease)),
|
|
438
|
+
height var(--chart-transition-duration, var(--motion-duration, 0.2s))
|
|
439
|
+
var(--chart-transition-easing, var(--motion-easing, ease));
|
|
413
440
|
cursor: pointer;
|
|
414
441
|
}
|
|
415
442
|
|
|
@@ -436,12 +463,13 @@
|
|
|
436
463
|
}
|
|
437
464
|
|
|
438
465
|
.funnel-connector {
|
|
439
|
-
transition: d var(--chart-transition-duration, 0.2s)
|
|
466
|
+
transition: d var(--chart-transition-duration, var(--motion-duration, 0.2s))
|
|
467
|
+
var(--chart-transition-easing, var(--motion-easing, ease));
|
|
440
468
|
}
|
|
441
469
|
|
|
442
470
|
.chart-empty {
|
|
443
471
|
padding: var(--chart-empty-padding, 32px 24px);
|
|
444
|
-
color: var(--chart-empty-color, light-dark(#
|
|
472
|
+
color: var(--chart-empty-color, light-dark(#6b7280, #9ca3af));
|
|
445
473
|
text-align: center;
|
|
446
474
|
}
|
|
447
475
|
</style>
|
|
@@ -63,7 +63,13 @@ export type OptionalFunnelChartProperties = {
|
|
|
63
63
|
maxHeight?: number;
|
|
64
64
|
/** Lower bound (px) on the rendered chart height (defaults to `0`). */
|
|
65
65
|
minHeight?: number;
|
|
66
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* Render the tooltip into the chart's own root (its shadow root when hosted as a
|
|
68
|
+
* custom element, `document.body` otherwise) with `position: fixed`, clamped to
|
|
69
|
+
* the viewport, instead of positioned inside the chart. Use it when the chart
|
|
70
|
+
* sits inside a container with `overflow: hidden`/`scroll` that would otherwise
|
|
71
|
+
* clip the tooltip.
|
|
72
|
+
*/
|
|
67
73
|
tooltipPortal?: boolean;
|
|
68
74
|
/** Value for the `data-pw` attribute on the chart root element. */
|
|
69
75
|
testId?: string;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { getActiveElement } from '../_interaction/focus';
|
|
3
|
+
import { lockBodyScroll, unlockBodyScroll } from '../utils';
|
|
2
4
|
import { tick } from 'svelte';
|
|
3
5
|
import type { Snippet } from 'svelte';
|
|
4
6
|
import { fade } from 'svelte/transition';
|
|
@@ -35,7 +37,8 @@
|
|
|
35
37
|
onclose,
|
|
36
38
|
onchange,
|
|
37
39
|
onkeydown,
|
|
38
|
-
classes
|
|
40
|
+
classes,
|
|
41
|
+
lightboxTransitionDuration
|
|
39
42
|
}: GalleryProperties = $props();
|
|
40
43
|
|
|
41
44
|
let lightboxDiv: HTMLDivElement | null = $state(null);
|
|
@@ -54,16 +57,20 @@
|
|
|
54
57
|
let showItemActions = $derived(showEditButton || showDeleteButton);
|
|
55
58
|
|
|
56
59
|
function lightboxAction(node: HTMLElement) {
|
|
57
|
-
|
|
58
|
-
|
|
60
|
+
const opener = getActiveElement(node);
|
|
61
|
+
if (openerElement === null && opener instanceof HTMLElement) {
|
|
62
|
+
openerElement = opener;
|
|
59
63
|
}
|
|
60
|
-
|
|
64
|
+
// Reference-counted: a lightbox opened from inside another scroll-locking
|
|
65
|
+
// surface (Modal, Sheet, CommandMenu) must not restore scroll out from
|
|
66
|
+
// under it on close. See lockBodyScroll's own doc comment in utils.ts.
|
|
67
|
+
lockBodyScroll();
|
|
61
68
|
tick().then(() => {
|
|
62
69
|
node.focus();
|
|
63
70
|
});
|
|
64
71
|
return {
|
|
65
72
|
destroy() {
|
|
66
|
-
|
|
73
|
+
unlockBodyScroll();
|
|
67
74
|
if (openerElement !== null) {
|
|
68
75
|
openerElement.focus();
|
|
69
76
|
openerElement = null;
|
|
@@ -93,7 +100,7 @@
|
|
|
93
100
|
activeIndex = index;
|
|
94
101
|
onchange?.(activeIndex);
|
|
95
102
|
await tick();
|
|
96
|
-
if (lightboxDiv !== null && !lightboxDiv.contains(
|
|
103
|
+
if (lightboxDiv !== null && !lightboxDiv.contains(getActiveElement(lightboxDiv))) {
|
|
97
104
|
lightboxDiv.focus();
|
|
98
105
|
}
|
|
99
106
|
}
|
|
@@ -138,11 +145,11 @@
|
|
|
138
145
|
}
|
|
139
146
|
if (
|
|
140
147
|
event.shiftKey &&
|
|
141
|
-
(
|
|
148
|
+
(getActiveElement(lightboxDiv) === first || getActiveElement(lightboxDiv) === lightboxDiv)
|
|
142
149
|
) {
|
|
143
150
|
event.preventDefault();
|
|
144
151
|
last.focus();
|
|
145
|
-
} else if (!event.shiftKey &&
|
|
152
|
+
} else if (!event.shiftKey && getActiveElement(lightboxDiv) === last) {
|
|
146
153
|
event.preventDefault();
|
|
147
154
|
first.focus();
|
|
148
155
|
}
|
|
@@ -266,7 +273,7 @@
|
|
|
266
273
|
use:lightboxAction
|
|
267
274
|
onclick={handleBackdropClick}
|
|
268
275
|
onkeydown={handleLightboxKeydown}
|
|
269
|
-
transition:fade={{ duration: 200 }}
|
|
276
|
+
transition:fade={{ duration: lightboxTransitionDuration ?? 200 }}
|
|
270
277
|
>
|
|
271
278
|
<div class="lightbox-close" bind:this={closeButtonWrap}>
|
|
272
279
|
<Button ariaLabel="Close gallery" onclick={closeLightbox}>
|
|
@@ -450,13 +457,13 @@
|
|
|
450
457
|
color: var(--gallery-list-title-color, inherit);
|
|
451
458
|
font-size: var(--gallery-list-title-font-size, 14px);
|
|
452
459
|
font-weight: var(--gallery-list-title-font-weight, 500);
|
|
453
|
-
font-family: var(--gallery-list-title-font-family);
|
|
460
|
+
font-family: var(--gallery-list-title-font-family, inherit);
|
|
454
461
|
}
|
|
455
462
|
|
|
456
463
|
.list-caption {
|
|
457
464
|
color: var(--gallery-list-caption-color, inherit);
|
|
458
465
|
font-size: var(--gallery-list-caption-font-size, 12px);
|
|
459
|
-
font-family: var(--gallery-list-caption-font-family);
|
|
466
|
+
font-family: var(--gallery-list-caption-font-family, inherit);
|
|
460
467
|
}
|
|
461
468
|
|
|
462
469
|
.gallery-item-actions {
|
|
@@ -542,7 +549,7 @@
|
|
|
542
549
|
.lightbox-caption {
|
|
543
550
|
color: var(--gallery-lightbox-caption-color, #ffffff);
|
|
544
551
|
font-size: var(--gallery-lightbox-caption-font-size, 14px);
|
|
545
|
-
font-family: var(--gallery-lightbox-caption-font-family);
|
|
552
|
+
font-family: var(--gallery-lightbox-caption-font-family, inherit);
|
|
546
553
|
text-align: center;
|
|
547
554
|
max-width: var(--gallery-lightbox-image-width, 85vw);
|
|
548
555
|
}
|
|
@@ -554,7 +561,7 @@
|
|
|
554
561
|
transform: translateX(-50%);
|
|
555
562
|
color: var(--gallery-lightbox-counter-color, #ffffff);
|
|
556
563
|
font-size: var(--gallery-lightbox-counter-font-size, 13px);
|
|
557
|
-
font-family: var(--gallery-lightbox-counter-font-family);
|
|
564
|
+
font-family: var(--gallery-lightbox-counter-font-family, inherit);
|
|
558
565
|
}
|
|
559
566
|
|
|
560
567
|
.lightbox-close,
|
|
@@ -27,6 +27,14 @@ export type OptionalGalleryProperties = {
|
|
|
27
27
|
itemFooter?: Snippet<[GalleryImage, number]>;
|
|
28
28
|
testId?: string;
|
|
29
29
|
classes?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Duration (ms) of the lightbox's `fade` transition. A CSS custom property
|
|
32
|
+
* cannot reach a Svelte transition directive's parameters, so this prop is
|
|
33
|
+
* the token-contract equivalent for Gallery's motion — same shape as
|
|
34
|
+
* Toast's `inAnimationDuration`/`outAnimationDuration`. Defaults to the
|
|
35
|
+
* library's existing 200ms so omitting it renders identically to before.
|
|
36
|
+
*/
|
|
37
|
+
lightboxTransitionDuration?: number | null;
|
|
30
38
|
};
|
|
31
39
|
export type GalleryEventProperties = {
|
|
32
40
|
onimageclick?: (index: number, event: MouseEvent) => void;
|
|
@@ -16,12 +16,27 @@
|
|
|
16
16
|
showLoader = !showLoader;
|
|
17
17
|
onclick?.(event);
|
|
18
18
|
}
|
|
19
|
+
|
|
20
|
+
// The tile carries role="button" and sits in the tab order, so Enter and Space have
|
|
21
|
+
// to do what a click does. Clicking the element itself rather than calling the
|
|
22
|
+
// handler keeps one activation path, and gives `onclick` the MouseEvent its type
|
|
23
|
+
// promises instead of a synthesized stand-in.
|
|
24
|
+
function handleKeydown(event: KeyboardEvent): void {
|
|
25
|
+
onkeydown?.(event);
|
|
26
|
+
if (event.key !== 'Enter' && event.key !== ' ') {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
event.preventDefault();
|
|
30
|
+
if (event.currentTarget instanceof HTMLElement) {
|
|
31
|
+
event.currentTarget.click();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
19
34
|
</script>
|
|
20
35
|
|
|
21
36
|
<div
|
|
22
37
|
class="container {classes ?? ''}"
|
|
23
38
|
onclick={handleClick}
|
|
24
|
-
{
|
|
39
|
+
onkeydown={handleKeydown}
|
|
25
40
|
role="button"
|
|
26
41
|
tabindex="0"
|
|
27
42
|
data-pw={typeof testId === 'string' ? testId : null}
|
|
@@ -132,4 +147,19 @@
|
|
|
132
147
|
clip-path: polygon(50% 50%, 0 0, 100% 0, 100% 100%, 0 100%, 0 0);
|
|
133
148
|
}
|
|
134
149
|
}
|
|
150
|
+
/* An indefinite animation is the case this preference exists for: it never
|
|
151
|
+
ends, so a user who asked the OS to minimise motion gets a permanent loop.
|
|
152
|
+
The animation stops, but the element must still read as "busy" -- a guard
|
|
153
|
+
that leaves nothing on screen, or leaves a frame that means something else,
|
|
154
|
+
is worse than the motion it removed. This block lives in the component's own
|
|
155
|
+
<style> because that is the only stylesheet that reaches inside the shadow
|
|
156
|
+
root a custom-element consumer gets. */
|
|
157
|
+
@media (prefers-reduced-motion: reduce) {
|
|
158
|
+
.grid-body-loader::before {
|
|
159
|
+
/* clip-path is what the animation drives; clearing it reveals the whole
|
|
160
|
+
border ring, which is the frame the animation ends on anyway. */
|
|
161
|
+
animation: none;
|
|
162
|
+
clip-path: none;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
135
165
|
</style>
|
package/dist/HITL/HITL.svelte
CHANGED
|
@@ -450,7 +450,8 @@
|
|
|
450
450
|
width: 100%;
|
|
451
451
|
max-width: var(--hitl-max-width, 100%);
|
|
452
452
|
margin: var(--hitl-margin, 0);
|
|
453
|
-
animation: hitl-slide-in 0.3s
|
|
453
|
+
animation: hitl-slide-in var(--hitl-slide-in-animation-duration, var(--motion-duration, 0.3s))
|
|
454
|
+
var(--hitl-slide-in-animation-easing, var(--motion-easing, ease-out)) forwards;
|
|
454
455
|
contain: layout style;
|
|
455
456
|
}
|
|
456
457
|
|
|
@@ -465,7 +466,7 @@
|
|
|
465
466
|
font-size: var(--hitl-badge-font-size, 0.6875rem);
|
|
466
467
|
font-weight: var(--hitl-badge-font-weight, 600);
|
|
467
468
|
letter-spacing: var(--hitl-badge-letter-spacing, 0.06em);
|
|
468
|
-
color: var(--hitl-badge-color, #
|
|
469
|
+
color: var(--hitl-badge-color, #595959);
|
|
469
470
|
text-transform: uppercase;
|
|
470
471
|
}
|
|
471
472
|
|
|
@@ -485,7 +486,7 @@
|
|
|
485
486
|
padding: var(--hitl-description-padding, 0.25rem 0);
|
|
486
487
|
font-size: var(--hitl-description-font-size, 0.8125rem);
|
|
487
488
|
line-height: var(--hitl-description-line-height, inherit);
|
|
488
|
-
color: var(--hitl-description-color, #
|
|
489
|
+
color: var(--hitl-description-color, #5c5c5c);
|
|
489
490
|
}
|
|
490
491
|
|
|
491
492
|
.confirmation-body {
|
|
@@ -505,7 +506,7 @@
|
|
|
505
506
|
font-size: var(--hitl-param-label-font-size, 0.6875rem);
|
|
506
507
|
font-weight: var(--hitl-param-label-font-weight, 600);
|
|
507
508
|
letter-spacing: var(--hitl-param-label-letter-spacing, 0.04em);
|
|
508
|
-
color: var(--hitl-param-label-color, #
|
|
509
|
+
color: var(--hitl-param-label-color, #595959);
|
|
509
510
|
word-spacing: var(--hitl-param-label-word-spacing, normal);
|
|
510
511
|
text-transform: uppercase;
|
|
511
512
|
}
|
|
@@ -552,7 +553,12 @@
|
|
|
552
553
|
overflow: hidden;
|
|
553
554
|
--progress-track-background: transparent;
|
|
554
555
|
--progress-bar-background: transparent;
|
|
555
|
-
|
|
556
|
+
/* Named so a consumer can reach it. HITL sets the Progress child's token
|
|
557
|
+
directly, which beats anything declared at :root, so a `<sui-hitl>`
|
|
558
|
+
consumer had no way to slow or stop the countdown sweep -- custom
|
|
559
|
+
properties are the only thing that crosses a shadow boundary, and this
|
|
560
|
+
one was spoken for. */
|
|
561
|
+
--progress-bar-transition: var(--hitl-countdown-transition, width 0.1s linear);
|
|
556
562
|
--progress-track-height: 100%;
|
|
557
563
|
--progress-container-padding: 0;
|
|
558
564
|
}
|
|
@@ -573,17 +579,24 @@
|
|
|
573
579
|
background: var(--hitl-completion-background, #f4f4f5);
|
|
574
580
|
border-radius: var(--hitl-border-radius, 0.5rem);
|
|
575
581
|
padding: var(--hitl-completion-padding, 1rem);
|
|
576
|
-
animation: hitl-fade-in 0.3s
|
|
582
|
+
animation: hitl-fade-in var(--hitl-completion-animation-duration, var(--motion-duration, 0.3s))
|
|
583
|
+
var(--hitl-completion-animation-easing, var(--motion-easing, ease-in-out));
|
|
577
584
|
}
|
|
578
585
|
|
|
579
586
|
.completion-icon {
|
|
580
587
|
display: inline-flex;
|
|
581
588
|
width: var(--hitl-completion-icon-size, 1.25rem);
|
|
582
589
|
height: var(--hitl-completion-icon-size, 1.25rem);
|
|
583
|
-
color: var(--hitl-approved-color, #
|
|
590
|
+
color: var(--hitl-approved-color, #166534);
|
|
584
591
|
}
|
|
585
592
|
|
|
586
|
-
|
|
593
|
+
/* :global() rather than the plain scoped selector: sui-hitl's wrapper mirrors
|
|
594
|
+
this file's default SVG into a <slot> fallback so the icon survives when a
|
|
595
|
+
consumer slots nothing, and that markup is written in a different .svelte
|
|
596
|
+
file -- Svelte's scoped-style hash is only ever added to elements written in
|
|
597
|
+
THIS file's own template, so an unscoped selector is the only one that also
|
|
598
|
+
matches the mirrored copy. Same idiom as Checkbox.wc.svelte's `.icon :global(svg)`. */
|
|
599
|
+
.completion-icon :global(svg) {
|
|
587
600
|
width: 100%;
|
|
588
601
|
height: 100%;
|
|
589
602
|
}
|
|
@@ -595,7 +608,7 @@
|
|
|
595
608
|
.completion-text {
|
|
596
609
|
font-size: var(--hitl-completion-font-size, 0.875rem);
|
|
597
610
|
font-weight: var(--hitl-completion-font-weight, 600);
|
|
598
|
-
color: var(--hitl-approved-color, #
|
|
611
|
+
color: var(--hitl-approved-color, light-dark(#15803d, #4ade80));
|
|
599
612
|
}
|
|
600
613
|
|
|
601
614
|
.completion-text.halted {
|
|
@@ -623,4 +636,17 @@
|
|
|
623
636
|
opacity: 1;
|
|
624
637
|
}
|
|
625
638
|
}
|
|
639
|
+
|
|
640
|
+
/* Two entrance animations: a slide-in and a fade-in. `animation: none` is safe
|
|
641
|
+
for both -- an entrance animates FROM an offset TO the resting state, so
|
|
642
|
+
removing it shows the resting state immediately, which is the frame the
|
|
643
|
+
animation was travelling towards anyway. That is the opposite of the timed
|
|
644
|
+
loaders in Button and ListItem, where the base rule is the FINISHED state and
|
|
645
|
+
stopping the animation would claim completion that has not happened. */
|
|
646
|
+
@media (prefers-reduced-motion: reduce) {
|
|
647
|
+
.hitl-container,
|
|
648
|
+
.hitl-completion {
|
|
649
|
+
animation: none;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
626
652
|
</style>
|
package/dist/Icon/Icon.svelte
CHANGED
|
@@ -3,14 +3,30 @@
|
|
|
3
3
|
import type { IconProperties } from './properties';
|
|
4
4
|
|
|
5
5
|
let { icon, svg, text, onclick, onkeydown, classes, testId }: IconProperties = $props();
|
|
6
|
+
|
|
7
|
+
// A decorative icon is not a control. Without a click handler it keeps no button
|
|
8
|
+
// role and stays out of the tab order, rather than being a focusable no-op that
|
|
9
|
+
// assistive technology announces as a button.
|
|
10
|
+
const interactive = $derived(typeof onclick === 'function');
|
|
11
|
+
|
|
12
|
+
function handleKeydown(event: KeyboardEvent): void {
|
|
13
|
+
onkeydown?.(event);
|
|
14
|
+
if (!interactive || (event.key !== 'Enter' && event.key !== ' ')) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
event.preventDefault();
|
|
18
|
+
if (event.currentTarget instanceof HTMLElement) {
|
|
19
|
+
event.currentTarget.click();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
6
22
|
</script>
|
|
7
23
|
|
|
8
24
|
<div
|
|
9
25
|
class="icon-container {classes ?? ''}"
|
|
10
26
|
{onclick}
|
|
11
|
-
{
|
|
12
|
-
role=
|
|
13
|
-
tabindex=
|
|
27
|
+
onkeydown={handleKeydown}
|
|
28
|
+
role={interactive ? 'button' : null}
|
|
29
|
+
tabindex={interactive ? 0 : null}
|
|
14
30
|
data-pw={typeof testId === 'string' ? testId : null}
|
|
15
31
|
testID={typeof testId === 'string' ? testId : null}
|
|
16
32
|
>
|