@juspay/svelte-ui-components 2.61.0 → 2.62.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/Stepper/Step.svelte +165 -16
- package/dist/Stepper/Stepper.svelte +115 -15
- package/dist/Stepper/properties.d.ts +45 -9
- package/package.json +1 -1
package/dist/Stepper/Step.svelte
CHANGED
|
@@ -1,18 +1,77 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { StepProperties } from './properties';
|
|
3
3
|
|
|
4
|
-
let {
|
|
4
|
+
let {
|
|
5
|
+
stepIndex,
|
|
6
|
+
label,
|
|
7
|
+
icon,
|
|
8
|
+
status,
|
|
9
|
+
badge,
|
|
10
|
+
orientation = 'horizontal',
|
|
11
|
+
classes,
|
|
12
|
+
ariaLabel,
|
|
13
|
+
onclick,
|
|
14
|
+
onkeydown
|
|
15
|
+
}: StepProperties = $props();
|
|
5
16
|
|
|
6
|
-
|
|
17
|
+
const handleStepClick = (): void => {
|
|
7
18
|
onclick?.({ selectedIndex: stepIndex });
|
|
8
|
-
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const handleKeydown = (event: KeyboardEvent): void => {
|
|
22
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
23
|
+
event.preventDefault();
|
|
24
|
+
handleStepClick();
|
|
25
|
+
}
|
|
26
|
+
onkeydown?.(event);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
let isVertical = $derived(orientation === 'vertical');
|
|
30
|
+
|
|
31
|
+
let stepClass = $derived(
|
|
32
|
+
['step', isVertical ? 'step-vertical' : '', classes ?? ''].filter((c) => c.length > 0).join(' ')
|
|
33
|
+
);
|
|
9
34
|
</script>
|
|
10
35
|
|
|
11
|
-
<div
|
|
36
|
+
<div
|
|
37
|
+
class={stepClass}
|
|
38
|
+
onclick={handleStepClick}
|
|
39
|
+
onkeydown={handleKeydown}
|
|
40
|
+
role="button"
|
|
41
|
+
tabindex="0"
|
|
42
|
+
aria-label={ariaLabel ?? null}
|
|
43
|
+
aria-labelledby={ariaLabel ? null : `step-label-${stepIndex}`}
|
|
44
|
+
aria-current={status === 'active' ? 'step' : null}
|
|
45
|
+
>
|
|
12
46
|
{#if typeof icon === 'string' && icon.length > 0}
|
|
13
47
|
<div class="step-icon-container">
|
|
14
48
|
<img class="step-icon" src={icon} alt="" />
|
|
15
49
|
</div>
|
|
50
|
+
{:else if status === 'in-progress'}
|
|
51
|
+
<div class="step-index-container">
|
|
52
|
+
<svg
|
|
53
|
+
class="step-spinner"
|
|
54
|
+
viewBox="0 0 24 24"
|
|
55
|
+
fill="none"
|
|
56
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
57
|
+
aria-hidden="true"
|
|
58
|
+
>
|
|
59
|
+
<circle
|
|
60
|
+
cx="12"
|
|
61
|
+
cy="12"
|
|
62
|
+
r="9"
|
|
63
|
+
stroke="currentColor"
|
|
64
|
+
stroke-opacity="0.25"
|
|
65
|
+
stroke-width="3"
|
|
66
|
+
/>
|
|
67
|
+
<path
|
|
68
|
+
d="M12 3a9 9 0 0 1 9 9"
|
|
69
|
+
stroke="currentColor"
|
|
70
|
+
stroke-width="3"
|
|
71
|
+
stroke-linecap="round"
|
|
72
|
+
/>
|
|
73
|
+
</svg>
|
|
74
|
+
</div>
|
|
16
75
|
{:else}
|
|
17
76
|
<div class="step-index-container">
|
|
18
77
|
<div class="step-index-text">
|
|
@@ -20,9 +79,17 @@
|
|
|
20
79
|
</div>
|
|
21
80
|
</div>
|
|
22
81
|
{/if}
|
|
23
|
-
|
|
82
|
+
|
|
83
|
+
<div class="step-text" id="step-label-{stepIndex}">
|
|
24
84
|
{label}
|
|
25
85
|
</div>
|
|
86
|
+
|
|
87
|
+
{#if typeof badge === 'function'}
|
|
88
|
+
<div class="step-badge">
|
|
89
|
+
{@render badge()}
|
|
90
|
+
</div>
|
|
91
|
+
{/if}
|
|
92
|
+
|
|
26
93
|
<div class="separator"></div>
|
|
27
94
|
</div>
|
|
28
95
|
|
|
@@ -33,6 +100,11 @@
|
|
|
33
100
|
align-items: center;
|
|
34
101
|
}
|
|
35
102
|
|
|
103
|
+
.step-vertical {
|
|
104
|
+
--step-flex-direction: column;
|
|
105
|
+
align-items: flex-start;
|
|
106
|
+
}
|
|
107
|
+
|
|
36
108
|
.step-index-container {
|
|
37
109
|
display: flex;
|
|
38
110
|
justify-content: center;
|
|
@@ -41,33 +113,110 @@
|
|
|
41
113
|
width: var(--step-index-container-width, 30px);
|
|
42
114
|
border-radius: var(--step-index-container-radius, 50%);
|
|
43
115
|
background-color: var(--step-index-container-background-color, #798fa5cc);
|
|
116
|
+
color: var(--step-index-color, white);
|
|
117
|
+
flex-shrink: 0;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.step-icon-container {
|
|
121
|
+
display: flex;
|
|
122
|
+
justify-content: center;
|
|
123
|
+
align-items: center;
|
|
124
|
+
height: var(--step-index-container-height, 30px);
|
|
125
|
+
width: var(--step-index-container-width, 30px);
|
|
126
|
+
border-radius: var(--step-index-container-radius, 50%);
|
|
127
|
+
background-color: var(--step-index-container-background-color, #798fa5cc);
|
|
128
|
+
flex-shrink: 0;
|
|
129
|
+
overflow: hidden;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.step-icon {
|
|
133
|
+
width: var(--step-icon-size, 18px);
|
|
134
|
+
height: var(--step-icon-size, 18px);
|
|
135
|
+
object-fit: contain;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.step-spinner {
|
|
139
|
+
width: var(--step-spinner-size, 18px);
|
|
140
|
+
height: var(--step-spinner-size, 18px);
|
|
141
|
+
animation: stepper-spin 0.8s linear infinite;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
@keyframes stepper-spin {
|
|
145
|
+
from {
|
|
146
|
+
transform: rotate(0deg);
|
|
147
|
+
}
|
|
148
|
+
to {
|
|
149
|
+
transform: rotate(360deg);
|
|
150
|
+
}
|
|
44
151
|
}
|
|
45
152
|
|
|
46
153
|
.separator {
|
|
47
|
-
display: var(--separator-display, block);
|
|
48
|
-
height: var(--separator-height, 1px);
|
|
49
|
-
width: var(--separator-width, 50px);
|
|
50
|
-
margin: var(--separator-margin, 0px 12px 0px 12px);
|
|
154
|
+
display: var(--stepper-separator-display, var(--separator-display, block));
|
|
155
|
+
height: var(--stepper-separator-height, var(--separator-height, 1px));
|
|
156
|
+
width: var(--stepper-separator-width, var(--separator-width, 50px));
|
|
157
|
+
margin: var(--stepper-separator-margin, var(--separator-margin, 0px 12px 0px 12px));
|
|
51
158
|
background-image: var(
|
|
52
|
-
--separator-background-image,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
159
|
+
--stepper-separator-background-image,
|
|
160
|
+
var(
|
|
161
|
+
--separator-background-image,
|
|
162
|
+
repeating-linear-gradient(
|
|
163
|
+
to right,
|
|
164
|
+
var(
|
|
165
|
+
--stepper-separator-background-image-color,
|
|
166
|
+
var(--separator-background-image-color, #798fa5cc)
|
|
167
|
+
),
|
|
168
|
+
var(
|
|
169
|
+
--stepper-separator-background-image-color,
|
|
170
|
+
var(--separator-background-image-color, #798fa5cc)
|
|
171
|
+
)
|
|
172
|
+
6px,
|
|
173
|
+
transparent 6px,
|
|
174
|
+
transparent 10px
|
|
175
|
+
)
|
|
59
176
|
)
|
|
60
177
|
);
|
|
61
178
|
}
|
|
62
179
|
|
|
180
|
+
.step-vertical .separator {
|
|
181
|
+
height: var(--stepper-separator-vertical-height, 32px);
|
|
182
|
+
width: var(--stepper-separator-vertical-width, 1px);
|
|
183
|
+
margin: var(--stepper-separator-vertical-margin, 4px 0px 4px 14px);
|
|
184
|
+
background-image: repeating-linear-gradient(
|
|
185
|
+
to bottom,
|
|
186
|
+
var(
|
|
187
|
+
--stepper-separator-background-image-color,
|
|
188
|
+
var(--separator-background-image-color, #798fa5cc)
|
|
189
|
+
),
|
|
190
|
+
var(
|
|
191
|
+
--stepper-separator-background-image-color,
|
|
192
|
+
var(--separator-background-image-color, #798fa5cc)
|
|
193
|
+
)
|
|
194
|
+
6px,
|
|
195
|
+
transparent 6px,
|
|
196
|
+
transparent 10px
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
63
200
|
.step-text {
|
|
64
201
|
margin: var(--step-text-margin, 0px 0px 0px 12px);
|
|
65
202
|
font-size: var(--step-text-font-size, 12px);
|
|
66
203
|
color: var(--step-text-color, #798fa5cc);
|
|
67
204
|
}
|
|
68
205
|
|
|
206
|
+
.step-vertical .step-text {
|
|
207
|
+
margin: var(--step-text-vertical-margin, 4px 0px 0px 0px);
|
|
208
|
+
}
|
|
209
|
+
|
|
69
210
|
.step-index-text {
|
|
70
211
|
font-size: var(--step-index-font-size, 14px);
|
|
71
212
|
color: var(--step-index-color, white);
|
|
72
213
|
}
|
|
214
|
+
|
|
215
|
+
.step-badge {
|
|
216
|
+
margin: var(--step-badge-margin, 0 0 0 4px);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.step-vertical .step-badge {
|
|
220
|
+
margin: var(--step-badge-vertical-margin, 4px 0 0 0);
|
|
221
|
+
}
|
|
73
222
|
</style>
|
|
@@ -1,22 +1,57 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { StepperProperties } from './properties';
|
|
2
|
+
import type { StepperProperties, StepStatus } from './properties';
|
|
3
3
|
import Step from './Step.svelte';
|
|
4
4
|
|
|
5
|
-
let {
|
|
5
|
+
let {
|
|
6
|
+
steps,
|
|
7
|
+
currentStepIndex,
|
|
8
|
+
orientation = 'horizontal',
|
|
9
|
+
classes,
|
|
10
|
+
testId,
|
|
11
|
+
onstepclick,
|
|
12
|
+
onhandleStepClick
|
|
13
|
+
}: StepperProperties = $props();
|
|
14
|
+
|
|
15
|
+
const resolveStatus = (stepIndex: number, explicitStatus: StepStatus | null): StepStatus => {
|
|
16
|
+
if (explicitStatus !== null) {
|
|
17
|
+
return explicitStatus;
|
|
18
|
+
}
|
|
19
|
+
if (stepIndex < currentStepIndex) {
|
|
20
|
+
return 'completed';
|
|
21
|
+
}
|
|
22
|
+
if (stepIndex === currentStepIndex) {
|
|
23
|
+
return 'active';
|
|
24
|
+
}
|
|
25
|
+
return 'pending';
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Support the deprecated onhandleStepClick alias — onstepclick takes priority.
|
|
29
|
+
const effectiveStepClick = $derived(onstepclick ?? onhandleStepClick);
|
|
30
|
+
|
|
31
|
+
let containerClass = $derived(
|
|
32
|
+
['container', orientation === 'vertical' ? 'container-vertical' : '', classes ?? '']
|
|
33
|
+
.filter((c) => c.length > 0)
|
|
34
|
+
.join(' ')
|
|
35
|
+
);
|
|
6
36
|
</script>
|
|
7
37
|
|
|
8
|
-
<div class=
|
|
38
|
+
<div class={containerClass} data-pw={typeof testId === 'string' ? testId : null} role="list">
|
|
9
39
|
{#each steps as currentStep, stepIndex (stepIndex)}
|
|
40
|
+
{@const effectiveStatus = resolveStatus(stepIndex, currentStep.status ?? null)}
|
|
10
41
|
<div
|
|
11
|
-
|
|
12
|
-
class
|
|
13
|
-
|
|
42
|
+
role="listitem"
|
|
43
|
+
class="step-container status-{effectiveStatus} {effectiveStatus === 'active'
|
|
44
|
+
? 'active-step'
|
|
45
|
+
: ''} {effectiveStatus === 'completed' ? 'completed-step' : ''}"
|
|
14
46
|
>
|
|
15
47
|
<Step
|
|
16
|
-
onclick={
|
|
48
|
+
onclick={effectiveStepClick}
|
|
17
49
|
label={currentStep.label}
|
|
18
50
|
icon={currentStep.icon}
|
|
19
51
|
stepIndex={stepIndex + 1}
|
|
52
|
+
status={effectiveStatus}
|
|
53
|
+
badge={currentStep.badge}
|
|
54
|
+
{orientation}
|
|
20
55
|
/>
|
|
21
56
|
</div>
|
|
22
57
|
{/each}
|
|
@@ -29,8 +64,13 @@
|
|
|
29
64
|
align-items: center;
|
|
30
65
|
}
|
|
31
66
|
|
|
67
|
+
.container-vertical {
|
|
68
|
+
--container-flex-direction: column;
|
|
69
|
+
align-items: flex-start;
|
|
70
|
+
}
|
|
71
|
+
|
|
32
72
|
.step-container:last-child {
|
|
33
|
-
--separator-display: none;
|
|
73
|
+
--stepper-separator-display: none;
|
|
34
74
|
}
|
|
35
75
|
|
|
36
76
|
.step-container {
|
|
@@ -38,21 +78,81 @@
|
|
|
38
78
|
align-items: center;
|
|
39
79
|
}
|
|
40
80
|
|
|
41
|
-
|
|
81
|
+
/* status-completed */
|
|
82
|
+
.status-completed {
|
|
83
|
+
--step-text-color: var(--step-text-completed-color, #24aa5a);
|
|
84
|
+
--stepper-separator-background-image-color: var(
|
|
85
|
+
--stepper-separator-background-image-completed-color,
|
|
86
|
+
#24aa5a
|
|
87
|
+
);
|
|
88
|
+
--step-index-container-background-color: var(
|
|
89
|
+
--step-index-container-completed-background-color,
|
|
90
|
+
#24aa5a
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* status-active */
|
|
95
|
+
.status-active {
|
|
42
96
|
--step-text-color: var(--step-text-active-color, #2f3841);
|
|
43
|
-
--separator-background-image-color: var(
|
|
97
|
+
--stepper-separator-background-image-color: var(
|
|
98
|
+
--stepper-separator-background-image-active-color,
|
|
99
|
+
#2f3841
|
|
100
|
+
);
|
|
44
101
|
--step-index-container-background-color: var(
|
|
45
102
|
--step-index-container-active-background-color,
|
|
46
103
|
#2f3841
|
|
47
104
|
);
|
|
48
105
|
}
|
|
49
106
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
107
|
+
/* status-pending: no override — falls through to #798fa5cc grey default in Step.svelte */
|
|
108
|
+
|
|
109
|
+
/* status-failure */
|
|
110
|
+
.status-failure {
|
|
111
|
+
--step-text-color: var(--step-text-failure-color, #e53935);
|
|
112
|
+
--stepper-separator-background-image-color: var(
|
|
113
|
+
--stepper-separator-background-image-failure-color,
|
|
114
|
+
#e53935
|
|
115
|
+
);
|
|
53
116
|
--step-index-container-background-color: var(
|
|
54
|
-
--step-index-container-
|
|
55
|
-
#
|
|
117
|
+
--step-index-container-failure-background-color,
|
|
118
|
+
var(--stepper-status-failure-color, #e53935)
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* status-in-progress */
|
|
123
|
+
.status-in-progress {
|
|
124
|
+
--step-text-color: var(--step-text-in-progress-color, #f59e0b);
|
|
125
|
+
--stepper-separator-background-image-color: var(
|
|
126
|
+
--stepper-separator-background-image-in-progress-color,
|
|
127
|
+
#f59e0b
|
|
128
|
+
);
|
|
129
|
+
--step-index-container-background-color: var(
|
|
130
|
+
--step-index-container-in-progress-background-color,
|
|
131
|
+
var(--stepper-status-in-progress-color, #f59e0b)
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
:global([theme='dark']) .status-failure {
|
|
136
|
+
--step-text-color: var(--step-text-failure-color, #ef5350);
|
|
137
|
+
--stepper-separator-background-image-color: var(
|
|
138
|
+
--stepper-separator-background-image-failure-color,
|
|
139
|
+
#ef5350
|
|
140
|
+
);
|
|
141
|
+
--step-index-container-background-color: var(
|
|
142
|
+
--step-index-container-failure-background-color,
|
|
143
|
+
var(--stepper-status-failure-color, #ef5350)
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
:global([theme='dark']) .status-in-progress {
|
|
148
|
+
--step-text-color: var(--step-text-in-progress-color, #fbbf24);
|
|
149
|
+
--stepper-separator-background-image-color: var(
|
|
150
|
+
--stepper-separator-background-image-in-progress-color,
|
|
151
|
+
#fbbf24
|
|
152
|
+
);
|
|
153
|
+
--step-index-container-background-color: var(
|
|
154
|
+
--step-index-container-in-progress-background-color,
|
|
155
|
+
var(--stepper-status-in-progress-color, #fbbf24)
|
|
56
156
|
);
|
|
57
157
|
}
|
|
58
158
|
</style>
|
|
@@ -1,21 +1,44 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
export type StepStatus = 'completed' | 'active' | 'pending' | 'failure' | 'in-progress';
|
|
3
|
+
export type Step = {
|
|
4
|
+
label: string;
|
|
5
|
+
/**
|
|
6
|
+
* URL of a custom icon image. When provided, the icon takes precedence over
|
|
7
|
+
* any `status`-driven rendering (including the `in-progress` spinner).
|
|
8
|
+
*/
|
|
9
|
+
icon?: string;
|
|
10
|
+
status?: StepStatus;
|
|
11
|
+
/**
|
|
12
|
+
* Optional Svelte snippet rendered inline after the step label (to its right
|
|
13
|
+
* in horizontal layout, below it in vertical layout). Use it for badges,
|
|
14
|
+
* tags, or other inline metadata — e.g. a count pill, a status chip, or a
|
|
15
|
+
* "New" label.
|
|
16
|
+
*/
|
|
17
|
+
badge?: Snippet;
|
|
18
|
+
};
|
|
19
|
+
export type MandatoryStepperProperties = {
|
|
2
20
|
steps: Array<Step>;
|
|
3
21
|
currentStepIndex: number;
|
|
22
|
+
};
|
|
23
|
+
export type OptionalStepperProperties = {
|
|
24
|
+
orientation?: 'horizontal' | 'vertical';
|
|
4
25
|
classes?: string;
|
|
26
|
+
testId?: string;
|
|
27
|
+
};
|
|
28
|
+
export type StepperEventProperties = {
|
|
29
|
+
onstepclick?: (event: {
|
|
30
|
+
selectedIndex: number;
|
|
31
|
+
}) => void;
|
|
32
|
+
/** @deprecated Use `onstepclick` instead. */
|
|
5
33
|
onhandleStepClick?: (event: {
|
|
6
34
|
selectedIndex: number;
|
|
7
35
|
}) => void;
|
|
8
36
|
};
|
|
9
|
-
export type
|
|
10
|
-
label: string;
|
|
11
|
-
icon?: string;
|
|
12
|
-
};
|
|
13
|
-
export type StepProperties = OptionalStepProperties & StepEventProperties & {
|
|
14
|
-
stepIndex: number;
|
|
15
|
-
label: string;
|
|
16
|
-
};
|
|
37
|
+
export type StepperProperties = MandatoryStepperProperties & OptionalStepperProperties & StepperEventProperties;
|
|
17
38
|
export type OptionalStepProperties = {
|
|
18
39
|
icon?: string;
|
|
40
|
+
status?: StepStatus;
|
|
41
|
+
badge?: Snippet;
|
|
19
42
|
classes?: string;
|
|
20
43
|
};
|
|
21
44
|
export type StepEventProperties = {
|
|
@@ -24,3 +47,16 @@ export type StepEventProperties = {
|
|
|
24
47
|
}) => void;
|
|
25
48
|
onkeydown?: (event: KeyboardEvent) => void;
|
|
26
49
|
};
|
|
50
|
+
export type StepProperties = OptionalStepProperties & StepEventProperties & {
|
|
51
|
+
/** 1-based display index — Stepper passes `stepIndex + 1` so that clicking step 1 returns `selectedIndex: 1`. */
|
|
52
|
+
stepIndex: number;
|
|
53
|
+
label: string;
|
|
54
|
+
orientation?: 'horizontal' | 'vertical';
|
|
55
|
+
/**
|
|
56
|
+
* Accessible label for the step button. When provided, set as `aria-label` on
|
|
57
|
+
* the `role="button"` element, overriding the default `aria-labelledby` association
|
|
58
|
+
* with the visible label text. Use when the visible label alone is insufficient
|
|
59
|
+
* context for screen reader users (e.g. "Step 1 — Cart (completed)").
|
|
60
|
+
*/
|
|
61
|
+
ariaLabel?: string;
|
|
62
|
+
};
|