@juspay/svelte-ui-components 2.135.0 → 2.136.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/dist/Chat/Chat.svelte +12 -0
- package/dist/Chat/properties.d.ts +7 -0
- package/dist/ChatMessageList/ChatMessageList.svelte +54 -1
- package/dist/CheckListItem/CheckListItem.svelte +4 -1
- package/dist/ChipInput/ChipInput.svelte +150 -10
- package/dist/ChipInput/properties.d.ts +18 -1
- package/dist/CommandMenu/CommandMenu.svelte +7 -3
- package/dist/EmptyState/EmptyState.svelte +17 -3
- package/dist/Input/Input.svelte +1 -0
- package/dist/ListItem/ListItem.svelte +14 -12
- package/dist/ListItem/properties.d.ts +7 -0
- package/dist/Menu/Menu.svelte +2 -1
- package/dist/Menu/properties.d.ts +2 -0
- package/dist/Modal/Modal.svelte +3 -3
- package/dist/Pill/Pill.svelte +2 -0
- package/dist/Pill/properties.d.ts +1 -0
- package/dist/Sheet/Sheet.svelte +3 -23
- package/dist/StatCard/StatCard.svelte +64 -1
- package/dist/StatCard/properties.d.ts +44 -2
- package/dist/Status/Status.svelte +2 -1
- package/dist/Status/properties.d.ts +10 -0
- package/dist/Stepper/Step.svelte +95 -2
- package/dist/Stepper/Stepper.svelte +52 -7
- package/dist/Stepper/properties.d.ts +34 -1
- package/dist/Table/BuiltinCell.svelte +46 -23
- package/dist/Table/Table.svelte +63 -40
- package/dist/Table/cellData.js +3 -0
- package/dist/Table/properties.d.ts +70 -0
- package/dist/TypewriterText/TypewriterText.svelte +67 -2
- package/dist/TypewriterText/properties.d.ts +97 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +30 -0
- package/dist-wc/index.js +1186 -926
- package/package.json +1 -1
package/dist/Sheet/Sheet.svelte
CHANGED
|
@@ -1,27 +1,7 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
// Reference-counted so two Sheets open at once (or a Sheet nested inside
|
|
3
|
-
// another scroll-locking surface) don't have the second one's close clobber
|
|
4
|
-
// the first one's still-needed lock.
|
|
5
|
-
let scrollLockCount = 0;
|
|
6
|
-
|
|
7
|
-
function lockScroll() {
|
|
8
|
-
if (scrollLockCount === 0) {
|
|
9
|
-
document.body.style.overflow = 'hidden';
|
|
10
|
-
}
|
|
11
|
-
scrollLockCount += 1;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function unlockScroll() {
|
|
15
|
-
scrollLockCount = Math.max(0, scrollLockCount - 1);
|
|
16
|
-
if (scrollLockCount === 0) {
|
|
17
|
-
document.body.style.overflow = '';
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
</script>
|
|
21
|
-
|
|
22
1
|
<script lang="ts">
|
|
23
2
|
import type { SheetProperties } from './properties';
|
|
24
3
|
import { fly, fade } from 'svelte/transition';
|
|
4
|
+
import { lockBodyScroll, unlockBodyScroll } from '../utils';
|
|
25
5
|
import { tick } from 'svelte';
|
|
26
6
|
import Button from '../Button/Button.svelte';
|
|
27
7
|
|
|
@@ -97,7 +77,7 @@
|
|
|
97
77
|
}
|
|
98
78
|
|
|
99
79
|
function scrollLockAction(_node: HTMLElement) {
|
|
100
|
-
|
|
80
|
+
lockBodyScroll();
|
|
101
81
|
tick().then(() => {
|
|
102
82
|
if (sheetPanel !== null) {
|
|
103
83
|
sheetPanel.focus();
|
|
@@ -105,7 +85,7 @@
|
|
|
105
85
|
});
|
|
106
86
|
return {
|
|
107
87
|
destroy() {
|
|
108
|
-
|
|
88
|
+
unlockBodyScroll();
|
|
109
89
|
}
|
|
110
90
|
};
|
|
111
91
|
}
|
|
@@ -185,6 +185,8 @@
|
|
|
185
185
|
<div class="statcard-row-value-line">
|
|
186
186
|
<div
|
|
187
187
|
class="statcard-value"
|
|
188
|
+
class:statcard-value-success={row.valueVariant === 'success'}
|
|
189
|
+
class:statcard-value-warning={row.valueVariant === 'warning'}
|
|
188
190
|
data-pw={typeof testId === 'string' ? `${testId}-value-${rowIndex}` : null}
|
|
189
191
|
testID={typeof testId === 'string' ? `${testId}-value-${rowIndex}` : null}
|
|
190
192
|
>
|
|
@@ -217,9 +219,25 @@
|
|
|
217
219
|
</div>
|
|
218
220
|
{/if}
|
|
219
221
|
{#if typeof row.additionalContent === 'string' && row.additionalContent.length > 0}
|
|
220
|
-
<div
|
|
222
|
+
<div
|
|
223
|
+
class="statcard-row-additional"
|
|
224
|
+
class:statcard-row-additional-break={row.additionalContentBreak === true}
|
|
225
|
+
data-pw={typeof testId === 'string' ? `${testId}-additional-${rowIndex}` : null}
|
|
226
|
+
testID={typeof testId === 'string' ? `${testId}-additional-${rowIndex}` : null}
|
|
227
|
+
>
|
|
228
|
+
{row.additionalContent}
|
|
229
|
+
</div>
|
|
221
230
|
{/if}
|
|
222
231
|
</div>
|
|
232
|
+
{#if typeof row.subtitle === 'string' && row.subtitle.length > 0}
|
|
233
|
+
<div
|
|
234
|
+
class="statcard-row-subtitle"
|
|
235
|
+
data-pw={typeof testId === 'string' ? `${testId}-subtitle-${rowIndex}` : null}
|
|
236
|
+
testID={typeof testId === 'string' ? `${testId}-subtitle-${rowIndex}` : null}
|
|
237
|
+
>
|
|
238
|
+
{row.subtitle}
|
|
239
|
+
</div>
|
|
240
|
+
{/if}
|
|
223
241
|
{#if Array.isArray(row.breakdown) && row.breakdown.length > 0}
|
|
224
242
|
{#if typeof row.breakdownHeading === 'string' && row.breakdownHeading.length > 0}
|
|
225
243
|
<div class="statcard-breakdown-heading">{row.breakdownHeading}</div>
|
|
@@ -421,6 +439,24 @@
|
|
|
421
439
|
line-height: var(--statcard-value-line-height, 1.2);
|
|
422
440
|
}
|
|
423
441
|
|
|
442
|
+
.statcard-value-success {
|
|
443
|
+
color: var(--statcard-value-success-color, #16a34a);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.statcard-value-warning {
|
|
447
|
+
color: var(--statcard-value-warning-color, #f59e0b);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/* Per-row value typography override — falls back to the card-level value
|
|
451
|
+
tokens so an unset row matches today's shared appearance exactly, then to
|
|
452
|
+
the same literals as the last resort. Scoped to the row's value line so
|
|
453
|
+
the single (non-rows) card value is unaffected. Mirrors the fallback
|
|
454
|
+
shape of --statcard-row-subtitle-font-size below. */
|
|
455
|
+
.statcard-row-value-line .statcard-value {
|
|
456
|
+
font-size: var(--statcard-row-value-font-size, var(--statcard-value-font-size, 24px));
|
|
457
|
+
font-weight: var(--statcard-row-value-font-weight, var(--statcard-value-font-weight, 600));
|
|
458
|
+
}
|
|
459
|
+
|
|
424
460
|
.statcard-delta {
|
|
425
461
|
font-size: var(--statcard-delta-font-size, 13px);
|
|
426
462
|
font-weight: var(--statcard-delta-font-weight, 500);
|
|
@@ -458,6 +494,23 @@
|
|
|
458
494
|
line-height: var(--statcard-subtitle-line-height, 1.4);
|
|
459
495
|
}
|
|
460
496
|
|
|
497
|
+
/* Per-row subtitle — falls back to the card-level subtitle tokens so a
|
|
498
|
+
consumer that already themes `.statcard-subtitle` gets a matching look
|
|
499
|
+
with no extra wiring, then to the same literals as the last resort. */
|
|
500
|
+
.statcard-row-subtitle {
|
|
501
|
+
order: var(--statcard-row-subtitle-order, 0);
|
|
502
|
+
font-size: var(--statcard-row-subtitle-font-size, var(--statcard-subtitle-font-size, 12px));
|
|
503
|
+
font-weight: var(
|
|
504
|
+
--statcard-row-subtitle-font-weight,
|
|
505
|
+
var(--statcard-subtitle-font-weight, 400)
|
|
506
|
+
);
|
|
507
|
+
color: var(--statcard-row-subtitle-color, var(--statcard-subtitle-color, #9ca3af));
|
|
508
|
+
line-height: var(
|
|
509
|
+
--statcard-row-subtitle-line-height,
|
|
510
|
+
var(--statcard-subtitle-line-height, 1.4)
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
|
|
461
514
|
/* Multi-row layout */
|
|
462
515
|
.statcard-rows {
|
|
463
516
|
order: var(--statcard-rows-order, 0);
|
|
@@ -498,6 +551,7 @@
|
|
|
498
551
|
}
|
|
499
552
|
|
|
500
553
|
.statcard-row-heading-wrap {
|
|
554
|
+
order: var(--statcard-row-heading-order, 0);
|
|
501
555
|
display: flex;
|
|
502
556
|
align-items: center;
|
|
503
557
|
}
|
|
@@ -516,6 +570,7 @@
|
|
|
516
570
|
}
|
|
517
571
|
|
|
518
572
|
.statcard-row-value-line {
|
|
573
|
+
order: var(--statcard-row-value-line-order, 0);
|
|
519
574
|
display: flex;
|
|
520
575
|
align-items: baseline;
|
|
521
576
|
gap: var(--statcard-row-value-gap, 8px);
|
|
@@ -523,12 +578,20 @@
|
|
|
523
578
|
}
|
|
524
579
|
|
|
525
580
|
.statcard-row-additional {
|
|
581
|
+
/* Default: flows inline in the wrapping flex row and only breaks if it
|
|
582
|
+
does not fit — matches short unit suffixes like "%" or "ms". */
|
|
526
583
|
font-size: var(--statcard-row-additional-font-size, 12px);
|
|
527
584
|
font-weight: var(--statcard-row-additional-font-weight, 400);
|
|
528
585
|
color: var(--statcard-row-additional-color, #9ca3af);
|
|
529
586
|
line-height: 1.4;
|
|
530
587
|
}
|
|
531
588
|
|
|
589
|
+
.statcard-row-additional-break {
|
|
590
|
+
/* Opt-in: forces its own line regardless of available width, for
|
|
591
|
+
descriptive text that should never sit beside the value/delta. */
|
|
592
|
+
flex-basis: var(--statcard-row-additional-flex-basis, 100%);
|
|
593
|
+
}
|
|
594
|
+
|
|
532
595
|
/* Breakdown grid */
|
|
533
596
|
.statcard-breakdown-heading {
|
|
534
597
|
font-size: var(--statcard-breakdown-heading-font-size, 11px);
|
|
@@ -43,15 +43,57 @@ export type StatCardRow = {
|
|
|
43
43
|
change?: number | null;
|
|
44
44
|
/** Invert delta colors for lower-is-better metrics on this row. */
|
|
45
45
|
invertChangeColors?: boolean;
|
|
46
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Additional descriptive text rendered after the value/delta. By default it
|
|
48
|
+
* flows inline and only wraps if it does not fit (e.g. a short unit suffix
|
|
49
|
+
* like "%" or "ms"). Set `additionalContentBreak` to force it onto its own
|
|
50
|
+
* line regardless of available width.
|
|
51
|
+
*/
|
|
47
52
|
additionalContent?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Forces `additionalContent` onto its own line below the value/delta,
|
|
55
|
+
* regardless of available width. Omit to keep the default inline flow.
|
|
56
|
+
*/
|
|
57
|
+
additionalContentBreak?: boolean;
|
|
58
|
+
/** Tints this row's value text for a warning or success state. Omit for the default color. */
|
|
59
|
+
valueVariant?: 'success' | 'warning';
|
|
60
|
+
/**
|
|
61
|
+
* Secondary label rendered below this row's value line (e.g. a per-row
|
|
62
|
+
* comparison-period caption). Independent of the card-level `subtitle` —
|
|
63
|
+
* both may be set at once, e.g. when different rows compare against
|
|
64
|
+
* different baselines.
|
|
65
|
+
*
|
|
66
|
+
* Renders after the value line by default (heading → value → subtitle in
|
|
67
|
+
* markup order). A consumer that needs title → subtitle → value can
|
|
68
|
+
* reorder without touching markup via `--statcard-row-subtitle-order` /
|
|
69
|
+
* `--statcard-row-value-line-order` — see `testId` below.
|
|
70
|
+
*/
|
|
71
|
+
subtitle?: string;
|
|
48
72
|
/** Tooltip shown on the row heading. */
|
|
49
73
|
tooltip?: StatCardTooltip;
|
|
50
74
|
/** Heading rendered above the breakdown grid. */
|
|
51
75
|
breakdownHeading?: string;
|
|
52
76
|
/** Breakdown items rendered in a grid below the row value. */
|
|
53
77
|
breakdown?: StatCardBreakdownItem[];
|
|
54
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* Test selector for the row root element, rendered as `data-pw`/`testID`.
|
|
80
|
+
* Also the CSS scoping anchor for per-row typography and layout: target
|
|
81
|
+
* `[data-pw="<testId>"]` to set `--statcard-row-value-font-size`,
|
|
82
|
+
* `--statcard-row-value-font-weight`, `--statcard-row-heading-font-size`,
|
|
83
|
+
* `--statcard-row-heading-font-weight`, or the row's flex `order` overrides
|
|
84
|
+
* (`--statcard-row-heading-order`, `--statcard-row-value-line-order`,
|
|
85
|
+
* `--statcard-row-subtitle-order`) for just this row, independent of its
|
|
86
|
+
* siblings — see the CSS Variables table in the component docs.
|
|
87
|
+
*
|
|
88
|
+
* The row's three sub-elements (heading wrap, value line, subtitle) render
|
|
89
|
+
* in that markup order and each default to `order: 0`, so an untouched row
|
|
90
|
+
* is byte-identical to today. Set the three order hooks to reorder them —
|
|
91
|
+
* e.g. subtitle before the value line for a title → subtitle → value
|
|
92
|
+
* layout — the same pattern the card-level `--statcard-subtitle-order` /
|
|
93
|
+
* `--statcard-value-row-order` hooks already use for the card's own
|
|
94
|
+
* sections. `.statcard-row` is a `display: flex; flex-direction: column`
|
|
95
|
+
* container, so `order` applies directly with no other CSS needed.
|
|
96
|
+
*/
|
|
55
97
|
testId?: string;
|
|
56
98
|
};
|
|
57
99
|
export type OptionalStatCardProperties = {
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
let {
|
|
7
7
|
statusIcon = 'icons/order-success-icon.svg',
|
|
8
8
|
statusText = '',
|
|
9
|
+
statusTextTag = 'div',
|
|
9
10
|
statusDescription = '',
|
|
10
11
|
buttonProperties,
|
|
11
12
|
classes,
|
|
@@ -30,7 +31,7 @@
|
|
|
30
31
|
<Img inlineSvg src={statusIcon} alt="status" />
|
|
31
32
|
{/if}
|
|
32
33
|
</div>
|
|
33
|
-
<
|
|
34
|
+
<svelte:element this={statusTextTag} class="status-text">{statusText}</svelte:element>
|
|
34
35
|
<div class="status-description">
|
|
35
36
|
{#if typeof descriptionSnippet === 'function'}
|
|
36
37
|
{@render descriptionSnippet()}
|
|
@@ -4,6 +4,16 @@ export type StatusProperties = StatusEventProperties & {
|
|
|
4
4
|
statusIcon?: string;
|
|
5
5
|
statusText: string;
|
|
6
6
|
statusDescription: string;
|
|
7
|
+
/**
|
|
8
|
+
* Element `statusText` renders as. Defaults to `'div'`, matching every existing
|
|
9
|
+
* consumer's render output exactly. Reach for `'h1'`..`'h6'` when `statusText`
|
|
10
|
+
* carries a real page/section heading: an app that wires typography to semantic
|
|
11
|
+
* tags (rather than setting `font-size` in component styles) can only pick up
|
|
12
|
+
* that tag's size, weight and colour, and only a heading tag joins the
|
|
13
|
+
* document outline for assistive tech — a `div` does neither, no matter what
|
|
14
|
+
* CSS targets it.
|
|
15
|
+
*/
|
|
16
|
+
statusTextTag?: 'div' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
|
7
17
|
buttonProperties?: ButtonProperties;
|
|
8
18
|
classes?: string;
|
|
9
19
|
/**
|
package/dist/Stepper/Step.svelte
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
orientation = 'horizontal',
|
|
11
11
|
classes,
|
|
12
12
|
ariaLabel,
|
|
13
|
+
testId,
|
|
14
|
+
suppressRoleAndTabindex,
|
|
13
15
|
onclick,
|
|
14
16
|
onkeydown
|
|
15
17
|
}: StepProperties = $props();
|
|
@@ -33,12 +35,29 @@
|
|
|
33
35
|
);
|
|
34
36
|
</script>
|
|
35
37
|
|
|
38
|
+
<!-- role and tabindex are suppressed together by suppressRoleAndTabindex, so this
|
|
39
|
+
can never end up non-interactive with a tab stop; matches ListItem's precedent. -->
|
|
40
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
41
|
+
<!-- The per-status colour tokens are remapped by Stepper's own `.status-<name>` wrapper.
|
|
42
|
+
A Step used on its own — outside a Stepper — never gets that wrapper, so every status
|
|
43
|
+
rendered identically at the neutral default and the component could not express its
|
|
44
|
+
own state. Carrying the same classes here lets a bare Step theme itself; inside a
|
|
45
|
+
Stepper both set the same custom properties to the same values, so nothing about an
|
|
46
|
+
existing consumer changes. These are `class:` directives rather than an interpolated
|
|
47
|
+
string so the compiler can see the selectors are used and does not prune the rules. -->
|
|
36
48
|
<div
|
|
37
49
|
class={stepClass}
|
|
50
|
+
class:status-completed={status === 'completed'}
|
|
51
|
+
class:status-active={status === 'active'}
|
|
52
|
+
class:status-failure={status === 'failure'}
|
|
53
|
+
class:status-in-progress={status === 'in-progress'}
|
|
54
|
+
class:status-muted={status === 'muted'}
|
|
55
|
+
data-pw={typeof testId === 'string' ? testId : null}
|
|
56
|
+
testID={typeof testId === 'string' ? testId : null}
|
|
38
57
|
onclick={handleStepClick}
|
|
39
58
|
onkeydown={handleKeydown}
|
|
40
|
-
role=
|
|
41
|
-
tabindex=
|
|
59
|
+
role={suppressRoleAndTabindex ? null : 'button'}
|
|
60
|
+
tabindex={suppressRoleAndTabindex ? null : 0}
|
|
42
61
|
aria-label={ariaLabel ?? null}
|
|
43
62
|
aria-labelledby={ariaLabel ? null : `step-label-${stepIndex}`}
|
|
44
63
|
aria-current={status === 'active' ? 'step' : null}
|
|
@@ -98,6 +117,10 @@
|
|
|
98
117
|
display: flex;
|
|
99
118
|
flex-direction: var(--step-flex-direction, row);
|
|
100
119
|
align-items: center;
|
|
120
|
+
/* Lets a step's own box grow to fill a --step-container-flex share of the
|
|
121
|
+
Stepper row — a prerequisite for the separator below to have any free
|
|
122
|
+
space to grow into. Default 0 keeps a step sized to its content. */
|
|
123
|
+
flex-grow: var(--step-flex-grow, 0);
|
|
101
124
|
}
|
|
102
125
|
|
|
103
126
|
.step-vertical {
|
|
@@ -105,6 +128,63 @@
|
|
|
105
128
|
align-items: flex-start;
|
|
106
129
|
}
|
|
107
130
|
|
|
131
|
+
/* Mirrors Stepper.svelte's per-status remapping so a bare Step themes itself. Only the
|
|
132
|
+
tokens Step actually renders are set here; the separator is Stepper's to draw. Each
|
|
133
|
+
chain terminates in the same literal Stepper uses, so the two agree by construction. */
|
|
134
|
+
.status-completed {
|
|
135
|
+
--step-text-color: var(--step-text-completed-color, #24aa5a);
|
|
136
|
+
--step-index-container-background-color: var(
|
|
137
|
+
--step-index-container-completed-background-color,
|
|
138
|
+
#24aa5a
|
|
139
|
+
);
|
|
140
|
+
--step-index-container-border: var(--step-index-container-completed-border, none);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.status-active {
|
|
144
|
+
--step-text-color: var(--step-text-active-color, #2f3841);
|
|
145
|
+
--step-index-container-background-color: var(
|
|
146
|
+
--step-index-container-active-background-color,
|
|
147
|
+
#2f3841
|
|
148
|
+
);
|
|
149
|
+
--step-index-container-border: var(--step-index-container-active-border, none);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.status-failure {
|
|
153
|
+
--step-text-color: var(--step-text-failure-color, #e53935);
|
|
154
|
+
--step-index-container-background-color: var(
|
|
155
|
+
--step-index-container-failure-background-color,
|
|
156
|
+
var(--stepper-status-failure-color, #e53935)
|
|
157
|
+
);
|
|
158
|
+
--step-index-container-border: var(--step-index-container-failure-border, none);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.status-in-progress {
|
|
162
|
+
--step-text-color: var(--step-text-in-progress-color, #f59e0b);
|
|
163
|
+
--step-index-container-background-color: var(
|
|
164
|
+
--step-index-container-in-progress-background-color,
|
|
165
|
+
var(--stepper-status-in-progress-color, #f59e0b)
|
|
166
|
+
);
|
|
167
|
+
--step-index-container-border: var(--step-index-container-in-progress-border, none);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.status-muted {
|
|
171
|
+
--step-text-color: var(--step-text-muted-color, #667080);
|
|
172
|
+
--step-index-container-background-color: var(
|
|
173
|
+
--step-index-container-muted-background-color,
|
|
174
|
+
#c9d2db
|
|
175
|
+
);
|
|
176
|
+
/* The circle is pale, so the numeral cannot inherit the white used on every other
|
|
177
|
+
status — that measured 1.53:1. #2f3841 is the same literal the active status
|
|
178
|
+
already uses for text, and reads at 7.79:1 here. */
|
|
179
|
+
--step-index-color: var(--step-index-muted-color, #2f3841);
|
|
180
|
+
--step-index-container-border: var(--step-index-container-muted-border, none);
|
|
181
|
+
--step-index-container-height: var(--step-index-container-muted-height, 20px);
|
|
182
|
+
--step-index-container-width: var(--step-index-container-muted-width, 20px);
|
|
183
|
+
--step-index-font-size: var(--step-index-muted-font-size, 10px);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/* status-pending sets nothing — it is the neutral default already in .step-index-container. */
|
|
187
|
+
|
|
108
188
|
.step-index-container {
|
|
109
189
|
display: flex;
|
|
110
190
|
justify-content: center;
|
|
@@ -113,6 +193,11 @@
|
|
|
113
193
|
width: var(--step-index-container-width, 30px);
|
|
114
194
|
border-radius: var(--step-index-container-radius, 50%);
|
|
115
195
|
background-color: var(--step-index-container-background-color, #798fa5cc);
|
|
196
|
+
/* No border today, so the fallback is `none` — an existing consumer that sets
|
|
197
|
+
nothing renders byte-identically. Shared with .step-icon-container below so
|
|
198
|
+
a status override (see Stepper.svelte) themes the circle whichever of the
|
|
199
|
+
two mutually-exclusive renders is showing. */
|
|
200
|
+
border: var(--step-index-container-border, none);
|
|
116
201
|
color: var(--step-index-color, white);
|
|
117
202
|
flex-shrink: 0;
|
|
118
203
|
}
|
|
@@ -125,6 +210,7 @@
|
|
|
125
210
|
width: var(--step-index-container-width, 30px);
|
|
126
211
|
border-radius: var(--step-index-container-radius, 50%);
|
|
127
212
|
background-color: var(--step-index-container-background-color, #798fa5cc);
|
|
213
|
+
border: var(--step-index-container-border, none);
|
|
128
214
|
flex-shrink: 0;
|
|
129
215
|
overflow: hidden;
|
|
130
216
|
}
|
|
@@ -154,6 +240,9 @@
|
|
|
154
240
|
display: var(--stepper-separator-display, var(--separator-display, block));
|
|
155
241
|
height: var(--stepper-separator-height, var(--separator-height, 1px));
|
|
156
242
|
width: var(--stepper-separator-width, var(--separator-width, 50px));
|
|
243
|
+
/* 0 keeps the separator pinned to its width above; set alongside --step-flex-grow
|
|
244
|
+
and --step-container-flex to let it stretch into a card's remaining width. */
|
|
245
|
+
flex-grow: var(--stepper-separator-flex-grow, 0);
|
|
157
246
|
margin: var(--stepper-separator-margin, var(--separator-margin, 0px 12px 0px 12px));
|
|
158
247
|
background-image: var(
|
|
159
248
|
--stepper-separator-background-image,
|
|
@@ -200,6 +289,10 @@
|
|
|
200
289
|
.step-text {
|
|
201
290
|
margin: var(--step-text-margin, 0px 0px 0px 12px);
|
|
202
291
|
font-size: var(--step-text-font-size, 12px);
|
|
292
|
+
/* No font-weight is set today, so the div inherits the browser default
|
|
293
|
+
`normal` — that is the literal fallback, keeping an existing consumer
|
|
294
|
+
byte-identical. */
|
|
295
|
+
font-weight: var(--step-text-font-weight, normal);
|
|
203
296
|
color: var(--step-text-color, #798fa5cc);
|
|
204
297
|
}
|
|
205
298
|
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
orientation = 'horizontal',
|
|
9
9
|
classes,
|
|
10
10
|
testId,
|
|
11
|
+
suppressRoleAndTabindex,
|
|
12
|
+
suppressContainerTestId,
|
|
11
13
|
onstepclick,
|
|
12
14
|
onhandleStepClick
|
|
13
15
|
}: StepperProperties = $props();
|
|
@@ -28,6 +30,12 @@
|
|
|
28
30
|
// Support the deprecated onhandleStepClick alias — onstepclick takes priority.
|
|
29
31
|
const effectiveStepClick = $derived(onstepclick ?? onhandleStepClick);
|
|
30
32
|
|
|
33
|
+
// Per-step ids always derive from testId, even when the container itself is
|
|
34
|
+
// opted out of claiming it — only the container's own data-pw/testID is suppressed.
|
|
35
|
+
const containerTestId = $derived(
|
|
36
|
+
typeof testId === 'string' && !suppressContainerTestId ? testId : null
|
|
37
|
+
);
|
|
38
|
+
|
|
31
39
|
let containerClass = $derived(
|
|
32
40
|
['container', orientation === 'vertical' ? 'container-vertical' : '', classes ?? '']
|
|
33
41
|
.filter((c) => c.length > 0)
|
|
@@ -35,14 +43,11 @@
|
|
|
35
43
|
);
|
|
36
44
|
</script>
|
|
37
45
|
|
|
38
|
-
<div
|
|
39
|
-
class={containerClass}
|
|
40
|
-
data-pw={typeof testId === 'string' ? testId : null}
|
|
41
|
-
testID={typeof testId === 'string' ? testId : null}
|
|
42
|
-
role="list"
|
|
43
|
-
>
|
|
46
|
+
<div class={containerClass} data-pw={containerTestId} testID={containerTestId} role="list">
|
|
44
47
|
{#each steps as currentStep, stepIndex (stepIndex)}
|
|
45
48
|
{@const effectiveStatus = resolveStatus(stepIndex, currentStep.status ?? null)}
|
|
49
|
+
{@const stepTestId =
|
|
50
|
+
currentStep.testId ?? (typeof testId === 'string' ? `${testId}-step-${stepIndex + 1}` : null)}
|
|
46
51
|
<div
|
|
47
52
|
role="listitem"
|
|
48
53
|
class="step-container status-{effectiveStatus} {effectiveStatus === 'active'
|
|
@@ -56,7 +61,9 @@
|
|
|
56
61
|
stepIndex={stepIndex + 1}
|
|
57
62
|
status={effectiveStatus}
|
|
58
63
|
badge={currentStep.badge}
|
|
64
|
+
testId={stepTestId}
|
|
59
65
|
{orientation}
|
|
66
|
+
{suppressRoleAndTabindex}
|
|
60
67
|
/>
|
|
61
68
|
</div>
|
|
62
69
|
{/each}
|
|
@@ -66,6 +73,7 @@
|
|
|
66
73
|
.container {
|
|
67
74
|
display: flex;
|
|
68
75
|
flex-direction: var(--container-flex-direction, row);
|
|
76
|
+
flex-wrap: var(--container-flex-wrap, nowrap);
|
|
69
77
|
align-items: center;
|
|
70
78
|
}
|
|
71
79
|
|
|
@@ -81,6 +89,7 @@
|
|
|
81
89
|
.step-container {
|
|
82
90
|
display: flex;
|
|
83
91
|
align-items: center;
|
|
92
|
+
flex: var(--step-container-flex, 0 1 auto);
|
|
84
93
|
}
|
|
85
94
|
|
|
86
95
|
/* status-completed */
|
|
@@ -94,6 +103,10 @@
|
|
|
94
103
|
--step-index-container-completed-background-color,
|
|
95
104
|
#24aa5a
|
|
96
105
|
);
|
|
106
|
+
/* No border existed for any status before this, so every one of these
|
|
107
|
+
per-status hooks falls back to `none` — byte-identical for a consumer
|
|
108
|
+
that sets nothing. */
|
|
109
|
+
--step-index-container-border: var(--step-index-container-completed-border, none);
|
|
97
110
|
}
|
|
98
111
|
|
|
99
112
|
/* status-active */
|
|
@@ -107,9 +120,10 @@
|
|
|
107
120
|
--step-index-container-active-background-color,
|
|
108
121
|
#2f3841
|
|
109
122
|
);
|
|
123
|
+
--step-index-container-border: var(--step-index-container-active-border, none);
|
|
110
124
|
}
|
|
111
125
|
|
|
112
|
-
/* status-pending: no override — falls through to #798fa5cc grey default in Step.svelte */
|
|
126
|
+
/* status-pending: no override — falls through to #798fa5cc grey (and no border) default in Step.svelte */
|
|
113
127
|
|
|
114
128
|
/* status-failure */
|
|
115
129
|
.status-failure {
|
|
@@ -122,6 +136,7 @@
|
|
|
122
136
|
--step-index-container-failure-background-color,
|
|
123
137
|
var(--stepper-status-failure-color, #e53935)
|
|
124
138
|
);
|
|
139
|
+
--step-index-container-border: var(--step-index-container-failure-border, none);
|
|
125
140
|
}
|
|
126
141
|
|
|
127
142
|
/* status-in-progress */
|
|
@@ -135,6 +150,36 @@
|
|
|
135
150
|
--step-index-container-in-progress-background-color,
|
|
136
151
|
var(--stepper-status-in-progress-color, #f59e0b)
|
|
137
152
|
);
|
|
153
|
+
--step-index-container-border: var(--step-index-container-in-progress-border, none);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* status-muted: a smaller, subtly-tinted marker for a de-emphasized or
|
|
157
|
+
supplementary step (e.g. an informational marker riding alongside a
|
|
158
|
+
primary rail). New opt-in status value — none of the statuses above are
|
|
159
|
+
touched, and a step only reaches these rules by setting status:'muted'
|
|
160
|
+
explicitly (resolveStatus never derives it). Reuses the same
|
|
161
|
+
--step-index-container-height/-width/-background-color, --step-text-color,
|
|
162
|
+
--stepper-separator-background-image-color and --step-index-container-border
|
|
163
|
+
hooks the other statuses already theme through, so a call site can further
|
|
164
|
+
override any of them the same way. */
|
|
165
|
+
.status-muted {
|
|
166
|
+
--step-text-color: var(--step-text-muted-color, #667080);
|
|
167
|
+
--stepper-separator-background-image-color: var(
|
|
168
|
+
--stepper-separator-background-image-muted-color,
|
|
169
|
+
#c9d2db
|
|
170
|
+
);
|
|
171
|
+
--step-index-container-background-color: var(
|
|
172
|
+
--step-index-container-muted-background-color,
|
|
173
|
+
#c9d2db
|
|
174
|
+
);
|
|
175
|
+
/* The circle is pale, so the numeral cannot inherit the white used on every other
|
|
176
|
+
status — that measured 1.53:1. #2f3841 is the same literal the active status
|
|
177
|
+
already uses for text, and reads at 7.79:1 here. */
|
|
178
|
+
--step-index-color: var(--step-index-muted-color, #2f3841);
|
|
179
|
+
--step-index-container-border: var(--step-index-container-muted-border, none);
|
|
180
|
+
--step-index-container-height: var(--step-index-container-muted-height, 20px);
|
|
181
|
+
--step-index-container-width: var(--step-index-container-muted-width, 20px);
|
|
182
|
+
--step-index-font-size: var(--step-index-muted-font-size, 10px);
|
|
138
183
|
}
|
|
139
184
|
|
|
140
185
|
:global([theme='dark']) .status-failure {
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* `muted` is an opt-in, purely additive status: a smaller, subtly-tinted circle
|
|
4
|
+
* for a de-emphasized or supplementary step (e.g. an informational marker
|
|
5
|
+
* riding alongside a primary rail). It is only ever reached by setting a
|
|
6
|
+
* step's `status` explicitly — `resolveStatus` in Stepper.svelte never derives
|
|
7
|
+
* it from `currentStepIndex` — so no existing step changes how it renders.
|
|
8
|
+
*/
|
|
9
|
+
export type StepStatus = 'completed' | 'active' | 'pending' | 'failure' | 'in-progress' | 'muted';
|
|
3
10
|
export type Step = {
|
|
4
11
|
label: string;
|
|
5
12
|
/**
|
|
@@ -15,6 +22,13 @@ export type Step = {
|
|
|
15
22
|
* "New" label.
|
|
16
23
|
*/
|
|
17
24
|
badge?: Snippet;
|
|
25
|
+
/**
|
|
26
|
+
* Test selector for this step's root element, rendered as `data-pw`. When
|
|
27
|
+
* omitted and the Stepper itself has a `testId`, falls back to
|
|
28
|
+
* `${stepperTestId}-step-${n}` (1-based). When neither is set, no attribute
|
|
29
|
+
* is rendered.
|
|
30
|
+
*/
|
|
31
|
+
testId?: string;
|
|
18
32
|
};
|
|
19
33
|
export type MandatoryStepperProperties = {
|
|
20
34
|
steps: Array<Step>;
|
|
@@ -24,6 +38,18 @@ export type OptionalStepperProperties = {
|
|
|
24
38
|
orientation?: 'horizontal' | 'vertical';
|
|
25
39
|
classes?: string;
|
|
26
40
|
testId?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Removes every Step's synthetic role and tab stop while retaining its mouse handlers.
|
|
43
|
+
* Opt in when an ancestor or consumer supplies the semantic interactive control.
|
|
44
|
+
*/
|
|
45
|
+
suppressRoleAndTabindex?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Stops the Stepper's own root element from rendering `testId` as `data-pw`/`testID`,
|
|
48
|
+
* while per-step ids still derive from `testId` (`${testId}-step-${n}`) exactly as
|
|
49
|
+
* before. Opt in when the element wrapping the Stepper already carries the same
|
|
50
|
+
* `data-pw` value, which would otherwise leave two elements matching that selector.
|
|
51
|
+
*/
|
|
52
|
+
suppressContainerTestId?: boolean;
|
|
27
53
|
};
|
|
28
54
|
export type StepperEventProperties = {
|
|
29
55
|
onstepclick?: (event: {
|
|
@@ -40,6 +66,13 @@ export type OptionalStepProperties = {
|
|
|
40
66
|
status?: StepStatus;
|
|
41
67
|
badge?: Snippet;
|
|
42
68
|
classes?: string;
|
|
69
|
+
/** Test selector for the step's root element, rendered as `data-pw`. */
|
|
70
|
+
testId?: string | null;
|
|
71
|
+
/**
|
|
72
|
+
* Removes Step's synthetic role and tab stop while retaining its mouse handlers.
|
|
73
|
+
* Opt in when an ancestor or consumer supplies the semantic interactive control.
|
|
74
|
+
*/
|
|
75
|
+
suppressRoleAndTabindex?: boolean;
|
|
43
76
|
};
|
|
44
77
|
export type StepEventProperties = {
|
|
45
78
|
onclick?: (event: {
|