@softwareone/spi-sv5-library 1.15.4 → 1.16.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.
@@ -1,28 +1,51 @@
1
1
  <script lang="ts">
2
2
  import { untrack, type Snippet } from 'svelte';
3
3
 
4
- import { Button, ErrorPage, type ProgressWizardStep } from '../../index.js';
4
+ import { type ProgressWizardStep } from '../../index.js';
5
+ import { setWizardContext } from './context.js';
6
+ import ProgressWizardHorizontal from './ProgressWizardHorizontal.svelte';
7
+ import ProgressWizardVertical from './ProgressWizardVertical.svelte';
5
8
 
6
- interface Props {
9
+ interface BaseProps {
7
10
  steps: ProgressWizardStep[];
8
11
  currentStep: number;
9
12
  readonly?: boolean;
10
13
  initialStep?: number;
11
14
  content: Snippet;
15
+ }
16
+
17
+ interface HorizontalProps extends BaseProps {
18
+ orientation: 'horizontal';
19
+ additionalButtons?: never;
20
+ oncancel?: never;
21
+ }
22
+
23
+ interface VerticalProps extends BaseProps {
24
+ orientation?: 'vertical';
12
25
  additionalButtons: Snippet<[lastActiveStep: number]>;
13
26
  oncancel: VoidFunction;
14
27
  }
15
28
 
29
+ type Props = VerticalProps | HorizontalProps;
30
+
16
31
  let {
17
32
  steps,
18
- readonly = false,
19
33
  currentStep = $bindable(0),
34
+ readonly = false,
20
35
  initialStep,
21
36
  content,
22
- additionalButtons,
23
- oncancel
37
+ ...props
24
38
  }: Props = $props();
25
39
 
40
+ setWizardContext({
41
+ getSteps: () => steps,
42
+ getReadonly: () => readonly,
43
+ getCurrentStep: () => currentStep,
44
+ setCurrentStep: (step: number) => {
45
+ currentStep = step;
46
+ }
47
+ });
48
+
26
49
  const allStepsDisabled = $derived<boolean>(steps.every((step) => step.disabled));
27
50
  const firstActiveStep = $derived<number>(steps.findIndex((step) => !step.disabled) + 1);
28
51
  const lastActiveStep = $derived<number>(steps.findLastIndex((step) => !step.disabled) + 1);
@@ -63,86 +86,27 @@
63
86
  </script>
64
87
 
65
88
  <div class="progress-wizard">
66
- <section class="progress-wizard-container">
67
- <ol class={['progress-wizard-steps', readonly ? 'readonly' : 'editing-mode']}>
68
- {#each steps as step, index (index)}
69
- {@const stepIndex = index + 1}
70
- {@const isActive = stepIndex === currentStep}
71
- {@const isPreviousStep = currentStep > stepIndex}
72
- {@const isValid = isPreviousStep && step.isValid}
73
- {@const isInvalid = isPreviousStep && !step.isValid}
74
- {@const disabled = step.disabled}
75
-
76
- {@const circleContent = (): string => {
77
- if (isActive) return stepIndex.toString();
78
- if (readonly || disabled) return 'ー';
79
- if (isValid) return '✔';
80
- if (isInvalid) return '✖';
81
- return stepIndex.toString();
82
- }}
83
-
84
- <li
85
- class={[
86
- 'progress-wizard-step',
87
- isActive && 'active',
88
- !readonly && [
89
- isPreviousStep && 'previous',
90
- isValid && 'valid',
91
- isInvalid && 'invalid',
92
- disabled && 'disabled'
93
- ]
94
- ]}
95
- >
96
- <div class="time-line">
97
- <span class="circle spi-text-semibold-1">
98
- {circleContent()}
99
- </span>
100
- <span class="line"></span>
101
- </div>
102
- <button
103
- type="button"
104
- class="details"
105
- disabled={disabled || readonly}
106
- onclick={() => (currentStep = stepIndex)}
107
- >
108
- <h2 class="spi-text-semibold-3">{step.title}</h2>
109
- {#if step.description}
110
- <p class="spi-text-regular-1">{step.description}</p>
111
- {/if}
112
- </button>
113
- </li>
114
- {/each}
115
- </ol>
116
- <article class="progress-wizard-content">
117
- {#if allStepsDisabled}
118
- <ErrorPage status={403} title="All steps disabled" />
119
- {:else}
120
- {@render content()}
121
- {/if}
122
- </article>
123
- </section>
124
- <section class="progress-wizard-footer">
125
- <Button type="button" variant="outline-none" onclick={oncancel}>Cancel</Button>
126
-
127
- <div class="actions">
128
- {#if !allStepsDisabled}
129
- {#if !readonly}
130
- <Button
131
- type="button"
132
- variant="secondary"
133
- disabled={currentStep === firstActiveStep}
134
- onclick={onclickBack}>Back</Button
135
- >
136
-
137
- {#if currentStep < lastActiveStep}
138
- <Button type="button" variant="primary" onclick={onclickNext}>Next</Button>
139
- {/if}
140
- {/if}
141
-
142
- {@render additionalButtons(lastActiveStep)}
143
- {/if}
144
- </div>
145
- </section>
89
+ {#if props.orientation === 'horizontal'}
90
+ <ProgressWizardHorizontal
91
+ {allStepsDisabled}
92
+ {firstActiveStep}
93
+ {lastActiveStep}
94
+ onclickback={onclickBack}
95
+ onclicknext={onclickNext}
96
+ {content}
97
+ />
98
+ {:else}
99
+ <ProgressWizardVertical
100
+ {allStepsDisabled}
101
+ {firstActiveStep}
102
+ {lastActiveStep}
103
+ additionalButtons={props.additionalButtons}
104
+ oncancel={props.oncancel}
105
+ onclickback={onclickBack}
106
+ onclicknext={onclickNext}
107
+ {content}
108
+ />
109
+ {/if}
146
110
  </div>
147
111
 
148
112
  <style>
@@ -156,188 +120,4 @@
156
120
  flex-direction: column;
157
121
  width: 100%;
158
122
  }
159
-
160
- .progress-wizard-container {
161
- display: flex;
162
- flex: 1;
163
- }
164
-
165
- .progress-wizard-steps {
166
- display: flex;
167
- flex-direction: column;
168
- width: fit-content;
169
- max-width: 250px;
170
- border-right: var(--border-wizard);
171
- }
172
-
173
- .progress-wizard-step {
174
- display: grid;
175
- grid-template-columns: auto 1fr;
176
- max-height: 70px;
177
- padding: var(--spi-size-4) var(--spi-size-6);
178
- gap: var(--spi-size-2);
179
- align-items: center;
180
- border: none;
181
- }
182
-
183
- .time-line {
184
- position: relative;
185
- display: flex;
186
- flex-direction: column;
187
- align-items: center;
188
- }
189
-
190
- .circle {
191
- display: flex;
192
- width: var(--circle-size);
193
- height: var(--circle-size);
194
- align-items: center;
195
- justify-content: center;
196
- border-radius: 50%;
197
- background: var(--spi-color-primary-lighter);
198
- color: var(--spi-color-primary-base);
199
- }
200
-
201
- .line {
202
- position: absolute;
203
- top: var(--circle-size);
204
- width: 1px;
205
- height: 50px;
206
- background: var(--spi-color-border-default);
207
- transition: background var(--spi-duration-slower) var(--spi-ease-default);
208
- }
209
-
210
- .progress-wizard-step:last-child .time-line .line {
211
- display: none;
212
- }
213
-
214
- .details {
215
- padding: var(--spi-size-1) var(--spi-size-2);
216
- text-align: left;
217
- border: none;
218
- border-radius: var(--spi-rounded-lg);
219
- background: transparent;
220
- cursor: pointer;
221
-
222
- h2 {
223
- color: var(--spi-color-text-primary);
224
- }
225
-
226
- p {
227
- word-break: keep-all;
228
- color: var(--spi-color-text-muted);
229
- }
230
- }
231
-
232
- .details:hover {
233
- background: var(--spi-color-surface-subtle);
234
- }
235
-
236
- .details:focus-visible:not(:disabled) {
237
- background: var(--spi-color-surface-subtle);
238
- box-shadow: 0 0 0 var(--spi-border-2) var(--spi-color-focus-ring);
239
- outline: none;
240
- }
241
-
242
- .progress-wizard-content {
243
- flex: 1;
244
- padding: var(--spi-size-6);
245
- }
246
-
247
- .progress-wizard-footer {
248
- display: flex;
249
- padding: var(--spi-size-6);
250
- border-top: var(--border-wizard);
251
-
252
- .actions {
253
- display: flex;
254
- gap: var(--spi-size-3);
255
- margin-left: auto;
256
- }
257
- }
258
-
259
- .progress-wizard-step.active {
260
- .time-line .circle {
261
- border: var(--border-circle);
262
- }
263
-
264
- .details {
265
- box-shadow: none;
266
- background: transparent;
267
-
268
- h2,
269
- p {
270
- color: var(--spi-color-primary-base);
271
- }
272
- }
273
- }
274
-
275
- .progress-wizard-steps:is(.readonly) {
276
- .progress-wizard-step:not(.active) {
277
- .line {
278
- background: var(--spi-color-border-default);
279
- }
280
-
281
- .circle {
282
- border: var(--border-readonly);
283
- background: var(--spi-color-disabled-bg);
284
- color: var(--spi-color-text-muted);
285
- }
286
-
287
- .details {
288
- h2 {
289
- color: var(--spi-color-text-muted);
290
- }
291
- p {
292
- color: var(--spi-color-text-disabled);
293
- }
294
- }
295
-
296
- .details:hover {
297
- background: transparent;
298
- cursor: not-allowed;
299
- }
300
- }
301
- }
302
-
303
- .progress-wizard-steps:is(.editing-mode) {
304
- .progress-wizard-step.previous .time-line .line {
305
- background: var(--spi-color-primary-base);
306
- }
307
-
308
- .progress-wizard-step.previous.valid .time-line .circle {
309
- color: var(--spi-color-text-inverse);
310
- background: var(--spi-color-primary-base);
311
- border: var(--border-circle);
312
- }
313
-
314
- .progress-wizard-step.previous.invalid .time-line .circle {
315
- color: var(--spi-color-text-danger);
316
- background: var(--spi-color-danger-lighter);
317
- border: var(--spi-border-2) solid var(--spi-color-border-danger);
318
- }
319
-
320
- .progress-wizard-step:is(.disabled, .previous.disabled) {
321
- .details {
322
- h2 {
323
- color: var(--spi-color-text-muted);
324
- }
325
- p {
326
- color: var(--spi-color-text-disabled);
327
- }
328
- }
329
- .details:hover {
330
- background: transparent;
331
- cursor: not-allowed;
332
- }
333
-
334
- .time-line {
335
- .circle {
336
- color: var(--spi-color-text-muted);
337
- background: var(--spi-color-disabled-bg);
338
- border: var(--border-readonly);
339
- }
340
- }
341
- }
342
- }
343
123
  </style>
@@ -1,14 +1,23 @@
1
1
  import { type Snippet } from 'svelte';
2
2
  import { type ProgressWizardStep } from '../../index.js';
3
- interface Props {
3
+ interface BaseProps {
4
4
  steps: ProgressWizardStep[];
5
5
  currentStep: number;
6
6
  readonly?: boolean;
7
7
  initialStep?: number;
8
8
  content: Snippet;
9
+ }
10
+ interface HorizontalProps extends BaseProps {
11
+ orientation: 'horizontal';
12
+ additionalButtons?: never;
13
+ oncancel?: never;
14
+ }
15
+ interface VerticalProps extends BaseProps {
16
+ orientation?: 'vertical';
9
17
  additionalButtons: Snippet<[lastActiveStep: number]>;
10
18
  oncancel: VoidFunction;
11
19
  }
20
+ type Props = VerticalProps | HorizontalProps;
12
21
  declare const ProgressWizard: import("svelte").Component<Props, {}, "currentStep">;
13
22
  type ProgressWizard = ReturnType<typeof ProgressWizard>;
14
23
  export default ProgressWizard;
@@ -0,0 +1,66 @@
1
+ <script lang="ts">
2
+ import { Button, Card, ErrorPage } from '../../index.js';
3
+ import { getWizardContext } from './context.js';
4
+ import ProgressWizardStep from './ProgressWizardStep.svelte';
5
+ import type { BaseProgressWizardProps } from './types.js';
6
+
7
+ let {
8
+ allStepsDisabled,
9
+ firstActiveStep,
10
+ lastActiveStep,
11
+ onclickback,
12
+ onclicknext,
13
+ content
14
+ }: BaseProgressWizardProps = $props();
15
+
16
+ const wizard = getWizardContext();
17
+
18
+ const readonly = $derived<boolean>(wizard.readonly);
19
+ const currentStep = $derived<number>(wizard.currentStep);
20
+ </script>
21
+
22
+ <section class="progress-wizard-container">
23
+ <Card>
24
+ {#if !allStepsDisabled}
25
+ {#if !readonly}
26
+ <Button
27
+ type="button"
28
+ variant="outline"
29
+ disabled={currentStep === firstActiveStep}
30
+ onclick={onclickback}>Back</Button
31
+ >
32
+ {/if}
33
+ {/if}
34
+
35
+ <ProgressWizardStep />
36
+
37
+ {#if !allStepsDisabled}
38
+ {#if !readonly}
39
+ {#if currentStep < lastActiveStep}
40
+ <Button type="button" variant="outline" onclick={onclicknext}>Next</Button>
41
+ {/if}
42
+ {/if}
43
+ {/if}
44
+ </Card>
45
+ <Card>
46
+ {#if allStepsDisabled}
47
+ <ErrorPage status={403} title="All steps disabled" />
48
+ {:else}
49
+ {@render content()}
50
+ {/if}
51
+ </Card>
52
+ </section>
53
+
54
+ <style>
55
+ .progress-wizard-container {
56
+ display: flex;
57
+ flex-direction: column;
58
+ gap: var(--spi-size-4);
59
+ }
60
+
61
+ .progress-wizard-container > :global(.card):first-child {
62
+ display: flex;
63
+ align-items: center;
64
+ gap: var(--spi-size-8);
65
+ }
66
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { BaseProgressWizardProps } from './types.js';
2
+ declare const ProgressWizardHorizontal: import("svelte").Component<BaseProgressWizardProps, {}, "">;
3
+ type ProgressWizardHorizontal = ReturnType<typeof ProgressWizardHorizontal>;
4
+ export default ProgressWizardHorizontal;
@@ -0,0 +1,251 @@
1
+ <script lang="ts">
2
+ import { getWizardContext } from './context.js';
3
+ import type { ProgressWizardStep } from './types.js';
4
+
5
+ interface Props {
6
+ isVertical?: boolean;
7
+ }
8
+
9
+ let { isVertical = false }: Props = $props();
10
+
11
+ const wizard = getWizardContext();
12
+
13
+ const readonly = $derived<boolean>(wizard.readonly);
14
+ const steps = $derived<ProgressWizardStep[]>(wizard.steps);
15
+ const currentStep = $derived<number>(wizard.currentStep);
16
+ </script>
17
+
18
+ <ol
19
+ class={[
20
+ 'progress-wizard-steps',
21
+ isVertical ? 'vertical' : 'horizontal',
22
+ readonly ? 'readonly' : 'editing-mode'
23
+ ]}
24
+ >
25
+ {#each steps as step, index (index)}
26
+ {@const stepIndex = index + 1}
27
+ {@const isActive = stepIndex === currentStep}
28
+ {@const isPreviousStep = currentStep > stepIndex}
29
+ {@const isValid = isPreviousStep && step.isValid}
30
+ {@const isInvalid = isPreviousStep && !step.isValid}
31
+ {@const disabled = step.disabled}
32
+
33
+ {@const circleContent = (): string => {
34
+ if (isActive) return stepIndex.toString();
35
+ if (readonly || disabled) return 'ー';
36
+ if (isValid) return '✔';
37
+ if (isInvalid) return '✖';
38
+ return stepIndex.toString();
39
+ }}
40
+
41
+ <li
42
+ class={[
43
+ 'progress-wizard-step',
44
+ isVertical && 'vertical',
45
+ isActive && 'active',
46
+ !readonly && [
47
+ isPreviousStep && 'previous',
48
+ isValid && 'valid',
49
+ isInvalid && 'invalid',
50
+ disabled && 'disabled'
51
+ ]
52
+ ]}
53
+ >
54
+ <div class="time-line">
55
+ <span class="circle spi-text-semibold-1">
56
+ {circleContent()}
57
+ </span>
58
+ {#if isVertical}
59
+ <span class="line"></span>
60
+ {/if}
61
+ </div>
62
+ <button
63
+ type="button"
64
+ class="details"
65
+ disabled={disabled || readonly}
66
+ onclick={() => wizard.setCurrentStep(stepIndex)}
67
+ >
68
+ <h2 class="spi-text-semibold-3">{step.title}</h2>
69
+ {#if step.description}
70
+ <p class="spi-text-regular-1">{step.description}</p>
71
+ {/if}
72
+ </button>
73
+ </li>
74
+ {/each}
75
+ </ol>
76
+
77
+ <style>
78
+ .progress-wizard-steps {
79
+ display: flex;
80
+ }
81
+
82
+ .progress-wizard-steps.vertical {
83
+ flex-direction: column;
84
+ width: fit-content;
85
+ max-width: 250px;
86
+ border-right: var(--border-wizard);
87
+ }
88
+
89
+ .progress-wizard-steps.horizontal {
90
+ flex-direction: row;
91
+ width: 100%;
92
+ gap: var(--spi-size-8);
93
+ }
94
+
95
+ .progress-wizard-step {
96
+ display: grid;
97
+ grid-template-columns: auto 1fr;
98
+ max-height: 70px;
99
+ gap: var(--spi-size-2);
100
+ align-items: center;
101
+ border: none;
102
+ }
103
+
104
+ .progress-wizard-step.vertical {
105
+ padding: var(--spi-size-4) var(--spi-size-6);
106
+ }
107
+
108
+ .time-line {
109
+ position: relative;
110
+ display: flex;
111
+ flex-direction: column;
112
+ align-items: center;
113
+ }
114
+
115
+ .circle {
116
+ display: flex;
117
+ width: var(--circle-size);
118
+ height: var(--circle-size);
119
+ align-items: center;
120
+ justify-content: center;
121
+ border-radius: 50%;
122
+ background: var(--spi-color-primary-lighter);
123
+ color: var(--spi-color-primary-base);
124
+ }
125
+
126
+ .line {
127
+ position: absolute;
128
+ top: var(--circle-size);
129
+ width: 1px;
130
+ height: 50px;
131
+ background: var(--spi-color-border-default);
132
+ transition: background var(--spi-duration-slower) var(--spi-ease-default);
133
+ }
134
+
135
+ .progress-wizard-step:last-child .time-line .line {
136
+ display: none;
137
+ }
138
+
139
+ .details {
140
+ padding: var(--spi-size-1) var(--spi-size-2);
141
+ text-align: left;
142
+ border: none;
143
+ border-radius: var(--spi-rounded-lg);
144
+ background: transparent;
145
+ cursor: pointer;
146
+
147
+ h2 {
148
+ color: var(--spi-color-text-primary);
149
+ }
150
+
151
+ p {
152
+ word-break: keep-all;
153
+ color: var(--spi-color-text-muted);
154
+ }
155
+ }
156
+
157
+ .details:hover {
158
+ background: var(--spi-color-surface-subtle);
159
+ }
160
+
161
+ .details:focus-visible:not(:disabled) {
162
+ background: var(--spi-color-surface-subtle);
163
+ box-shadow: 0 0 0 var(--spi-border-2) var(--spi-color-focus-ring);
164
+ outline: none;
165
+ }
166
+
167
+ .progress-wizard-step.active {
168
+ .time-line .circle {
169
+ border: var(--border-circle);
170
+ }
171
+
172
+ .details {
173
+ box-shadow: none;
174
+ background: transparent;
175
+
176
+ h2,
177
+ p {
178
+ color: var(--spi-color-primary-base);
179
+ }
180
+ }
181
+ }
182
+
183
+ .progress-wizard-steps:is(.readonly) {
184
+ .progress-wizard-step:not(.active) {
185
+ .line {
186
+ background: var(--spi-color-border-default);
187
+ }
188
+
189
+ .circle {
190
+ border: var(--border-readonly);
191
+ background: var(--spi-color-disabled-bg);
192
+ color: var(--spi-color-text-muted);
193
+ }
194
+
195
+ .details {
196
+ h2 {
197
+ color: var(--spi-color-text-muted);
198
+ }
199
+ p {
200
+ color: var(--spi-color-text-disabled);
201
+ }
202
+ }
203
+
204
+ .details:hover {
205
+ background: transparent;
206
+ cursor: not-allowed;
207
+ }
208
+ }
209
+ }
210
+
211
+ .progress-wizard-steps:is(.editing-mode) {
212
+ .progress-wizard-step.previous .time-line .line {
213
+ background: var(--spi-color-primary-base);
214
+ }
215
+
216
+ .progress-wizard-step.previous.valid .time-line .circle {
217
+ color: var(--spi-color-text-inverse);
218
+ background: var(--spi-color-primary-base);
219
+ border: var(--border-circle);
220
+ }
221
+
222
+ .progress-wizard-step.previous.invalid .time-line .circle {
223
+ color: var(--spi-color-text-danger);
224
+ background: var(--spi-color-danger-lighter);
225
+ border: var(--spi-border-2) solid var(--spi-color-border-danger);
226
+ }
227
+
228
+ .progress-wizard-step:is(.disabled, .previous.disabled) {
229
+ .details {
230
+ h2 {
231
+ color: var(--spi-color-text-muted);
232
+ }
233
+ p {
234
+ color: var(--spi-color-text-disabled);
235
+ }
236
+ }
237
+ .details:hover {
238
+ background: transparent;
239
+ cursor: not-allowed;
240
+ }
241
+
242
+ .time-line {
243
+ .circle {
244
+ color: var(--spi-color-text-muted);
245
+ background: var(--spi-color-disabled-bg);
246
+ border: var(--border-readonly);
247
+ }
248
+ }
249
+ }
250
+ }
251
+ </style>
@@ -0,0 +1,7 @@
1
+ import type { ProgressWizardStep } from './types.js';
2
+ interface Props {
3
+ isVertical?: boolean;
4
+ }
5
+ declare const ProgressWizardStep: import("svelte").Component<Props, {}, "">;
6
+ type ProgressWizardStep = ReturnType<typeof ProgressWizardStep>;
7
+ export default ProgressWizardStep;
@@ -0,0 +1,88 @@
1
+ <script lang="ts">
2
+ import { type Snippet } from 'svelte';
3
+
4
+ import { Button, ErrorPage } from '../../index.js';
5
+ import { getWizardContext } from './context.js';
6
+ import ProgressWizardStep from './ProgressWizardStep.svelte';
7
+ import type { BaseProgressWizardProps } from './types.js';
8
+
9
+ interface Props extends BaseProgressWizardProps {
10
+ additionalButtons: Snippet<[lastActiveStep: number]>;
11
+ oncancel: VoidFunction;
12
+ }
13
+
14
+ let {
15
+ allStepsDisabled,
16
+ firstActiveStep,
17
+ lastActiveStep,
18
+ additionalButtons,
19
+ oncancel,
20
+ onclickback,
21
+ onclicknext,
22
+ content
23
+ }: Props = $props();
24
+
25
+ const wizard = getWizardContext();
26
+
27
+ const readonly = $derived<boolean>(wizard.readonly);
28
+ const currentStep = $derived<number>(wizard.currentStep);
29
+ </script>
30
+
31
+ <section class="progress-wizard-container">
32
+ <ProgressWizardStep isVertical />
33
+
34
+ <article class="progress-wizard-content">
35
+ {#if allStepsDisabled}
36
+ <ErrorPage status={403} title="All steps disabled" />
37
+ {:else}
38
+ {@render content()}
39
+ {/if}
40
+ </article>
41
+ </section>
42
+
43
+ <section class="progress-wizard-footer">
44
+ <Button type="button" variant="outline-none" onclick={oncancel}>Cancel</Button>
45
+
46
+ <div class="actions">
47
+ {#if !allStepsDisabled}
48
+ {#if !readonly}
49
+ <Button
50
+ type="button"
51
+ variant="secondary"
52
+ disabled={currentStep === firstActiveStep}
53
+ onclick={onclickback}>Back</Button
54
+ >
55
+
56
+ {#if currentStep < lastActiveStep}
57
+ <Button type="button" variant="primary" onclick={onclicknext}>Next</Button>
58
+ {/if}
59
+ {/if}
60
+
61
+ {@render additionalButtons(lastActiveStep)}
62
+ {/if}
63
+ </div>
64
+ </section>
65
+
66
+ <style>
67
+ .progress-wizard-container {
68
+ display: flex;
69
+ flex: 1;
70
+ }
71
+
72
+ .progress-wizard-content {
73
+ flex: 1;
74
+ padding: var(--spi-size-6);
75
+ }
76
+
77
+ .progress-wizard-footer {
78
+ display: flex;
79
+ padding: var(--spi-size-6);
80
+ border-top: var(--border-wizard);
81
+
82
+ .actions {
83
+ display: flex;
84
+ gap: var(--spi-size-3);
85
+ margin-left: auto;
86
+ }
87
+ }
88
+ </style>
@@ -0,0 +1,9 @@
1
+ import { type Snippet } from 'svelte';
2
+ import type { BaseProgressWizardProps } from './types.js';
3
+ interface Props extends BaseProgressWizardProps {
4
+ additionalButtons: Snippet<[lastActiveStep: number]>;
5
+ oncancel: VoidFunction;
6
+ }
7
+ declare const ProgressWizardVertical: import("svelte").Component<Props, {}, "">;
8
+ type ProgressWizardVertical = ReturnType<typeof ProgressWizardVertical>;
9
+ export default ProgressWizardVertical;
@@ -1,5 +1,5 @@
1
1
  import { type Writable } from 'svelte/store';
2
- import type { ProgressWizardStep } from './types.js';
2
+ import type { ProgressWizardContext, ProgressWizardContextAccessors, ProgressWizardStep } from './types.js';
3
3
  export declare const setProgressWizardStepsContext: (steps: ProgressWizardStep[]) => Writable<{
4
4
  title: string;
5
5
  description?: string;
@@ -7,3 +7,5 @@ export declare const setProgressWizardStepsContext: (steps: ProgressWizardStep[]
7
7
  disabled: boolean;
8
8
  }[]>;
9
9
  export declare const getProgressWizardContext: () => Writable<ProgressWizardStep[]>;
10
+ export declare const setWizardContext: (accessors: ProgressWizardContextAccessors) => ProgressWizardContext;
11
+ export declare const getWizardContext: () => ProgressWizardContext;
@@ -1,6 +1,7 @@
1
1
  import { getContext, setContext } from 'svelte';
2
2
  import { writable } from 'svelte/store';
3
3
  const stepsKey = Symbol('steps');
4
+ const WIZARD_CONTEXT_KEY = Symbol('progressWizard');
4
5
  export const setProgressWizardStepsContext = (steps) => {
5
6
  const stepsContext = writable(steps.map((step) => ({ ...step })));
6
7
  setContext(stepsKey, stepsContext);
@@ -9,3 +10,16 @@ export const setProgressWizardStepsContext = (steps) => {
9
10
  export const getProgressWizardContext = () => {
10
11
  return getContext(stepsKey);
11
12
  };
13
+ export const setWizardContext = (accessors) => setContext(WIZARD_CONTEXT_KEY, {
14
+ get currentStep() {
15
+ return accessors.getCurrentStep();
16
+ },
17
+ get readonly() {
18
+ return accessors.getReadonly();
19
+ },
20
+ get steps() {
21
+ return accessors.getSteps();
22
+ },
23
+ setCurrentStep: accessors.setCurrentStep
24
+ });
25
+ export const getWizardContext = () => getContext(WIZARD_CONTEXT_KEY);
@@ -1,6 +1,27 @@
1
+ import type { Snippet } from 'svelte';
2
+ export interface BaseProgressWizardProps {
3
+ allStepsDisabled: boolean;
4
+ firstActiveStep: number;
5
+ lastActiveStep: number;
6
+ onclickback: VoidFunction;
7
+ onclicknext: VoidFunction;
8
+ content: Snippet;
9
+ }
1
10
  export type ProgressWizardStep = {
2
11
  title: string;
3
12
  description?: string;
4
13
  isValid: boolean;
5
14
  disabled: boolean;
6
15
  };
16
+ export type ProgressWizardContext = {
17
+ steps: ProgressWizardStep[];
18
+ readonly: boolean;
19
+ currentStep: number;
20
+ setCurrentStep: (step: number) => void;
21
+ };
22
+ export type ProgressWizardContextAccessors = {
23
+ getSteps: () => ProgressWizardStep[];
24
+ getReadonly: () => boolean;
25
+ getCurrentStep: () => number;
26
+ setCurrentStep: (step: number) => void;
27
+ };
@@ -5,9 +5,10 @@
5
5
  interface Props {
6
6
  bodyRows: Row<T>[];
7
7
  enableChecked?: boolean;
8
+ disabledRows: T[];
8
9
  }
9
10
 
10
- let { bodyRows, enableChecked = false }: Props = $props();
11
+ let { bodyRows, enableChecked = false, disabledRows }: Props = $props();
11
12
 
12
13
  const CLICKABLE_TAGS = ['td', 'span', 'p'];
13
14
 
@@ -18,6 +19,8 @@
18
19
  row.toggleSelected();
19
20
  }
20
21
  };
22
+
23
+ const isDisabledRow = (row: T) => disabledRows.includes(row);
21
24
  </script>
22
25
 
23
26
  <tbody class="table-body">
@@ -27,9 +30,12 @@
27
30
  class={[
28
31
  'table-row',
29
32
  row.getIsSelected() && 'table-row--selected',
30
- enableChecked && 'table-row--clickable'
33
+ enableChecked && 'table-row--clickable',
34
+ isDisabledRow(row.original) && 'cursor-not-allowed'
31
35
  ]}
32
- onclick={enableChecked ? (event) => handleRowClick(row, event.target) : undefined}
36
+ onclick={enableChecked && !isDisabledRow(row.original)
37
+ ? (event) => handleRowClick(row, event.target)
38
+ : undefined}
33
39
  >
34
40
  {#each row.getVisibleCells() as cell (cell.id)}
35
41
  {@const alignColumn = cell.column.columnDef.meta?.alignColumn}
@@ -98,4 +104,8 @@
98
104
  .table-cell--right {
99
105
  text-align: right;
100
106
  }
107
+
108
+ .cursor-not-allowed {
109
+ cursor: not-allowed;
110
+ }
101
111
  </style>
@@ -3,6 +3,7 @@ declare function $$render<T>(): {
3
3
  props: {
4
4
  bodyRows: Row<T>[];
5
5
  enableChecked?: boolean;
6
+ disabledRows: T[];
6
7
  };
7
8
  exports: {};
8
9
  bindings: "";
@@ -43,12 +43,11 @@
43
43
  isValidPageSize
44
44
  } from './utils.js';
45
45
 
46
- interface Props {
46
+ interface BaseProps<T> {
47
47
  columns: ColumnDef<T, any>[];
48
48
  isLoading?: boolean;
49
49
  data?: T[];
50
50
  minPageSize?: boolean;
51
- enableChecked?: boolean;
52
51
  keepVisibleBulkActions?: boolean;
53
52
  enableExportExcel?: boolean;
54
53
  enableGlobalSearch?: boolean;
@@ -68,12 +67,34 @@
68
67
  onprevpage?: (pageSize: number, pageNumber: number) => void;
69
68
  }
70
69
 
70
+ interface TableWithCheckbox<T> extends BaseProps<T> {
71
+ enableChecked: true;
72
+ preselectedRows?: T[];
73
+ disabledRows?: T[];
74
+ onselectionchange?: (selectedRows: T[]) => void;
75
+ onclearselection?: (clearSelection: VoidFunction) => void;
76
+ }
77
+
78
+ interface TableWithoutCheckbox<T> extends BaseProps<T> {
79
+ enableChecked?: false;
80
+ preselectedRows?: never;
81
+ disabledRows?: never;
82
+ onselectionchange?: never;
83
+ onclearselection?: never;
84
+ }
85
+
86
+ type Props<T> = TableWithCheckbox<T> | TableWithoutCheckbox<T>;
87
+
71
88
  let {
72
89
  columns = [],
73
90
  isLoading = false,
74
91
  data = [],
75
92
  minPageSize = false,
76
93
  enableChecked = false,
94
+ preselectedRows = [],
95
+ disabledRows = [],
96
+ onselectionchange,
97
+ onclearselection,
77
98
  keepVisibleBulkActions = false,
78
99
  enableExportExcel = false,
79
100
  enableGlobalSearch = true,
@@ -94,26 +115,41 @@
94
115
  onprevpage,
95
116
  header,
96
117
  bulkActions
97
- }: Props = $props();
118
+ }: Props<T> = $props();
119
+
120
+ const tableManualPagination = serverSide ? setPaginationTableContext() : undefined;
98
121
 
99
122
  let globalFilter = $state<string>('');
100
123
  let sorting = $state<SortingState>([]);
101
124
  let columnFilters = $state<ColumnFiltersState>([]);
102
- let rowSelection = $state<RowSelectionState>({});
103
125
  let expanded = $state<ExpandedState>({});
104
126
  let columnVisibility = $state<VisibilityState>(getInitialColumnVisibility());
105
127
  let paginationState = $state<PaginationState>(getInitialPaginationState());
128
+ let rowSelection = $state<RowSelectionState>();
129
+
106
130
  let tableData = $derived(data);
107
- const tableManualPagination = serverSide ? setPaginationTableContext() : undefined;
108
131
  const hasData = $derived(data.length > 0);
109
132
 
133
+ const selectedRowIds = $derived.by<RowSelectionState>(() => {
134
+ if (rowSelection !== undefined) return rowSelection;
135
+ if (!enableChecked || !preselectedRows?.length) return {};
136
+
137
+ const selectedRowIds: RowSelectionState = {};
138
+
139
+ table.getRowModel().rows.forEach((row) => {
140
+ if (preselectedRows.includes(row.original)) selectedRowIds[row.id] = true;
141
+ });
142
+
143
+ return selectedRowIds;
144
+ });
145
+
110
146
  const tableColumns = $derived.by(() => {
111
147
  if (!enableChecked) return columns;
112
148
 
113
149
  const checkedColumnExists = columns.some((column) => column.id === 'checked');
114
150
  if (checkedColumnExists) return columns;
115
151
 
116
- const checkedColumn = createCheckedColumn<T>();
152
+ const checkedColumn = createCheckedColumn<T>(disabledRows);
117
153
  return [checkedColumn, ...columns];
118
154
  });
119
155
 
@@ -135,7 +171,7 @@
135
171
  return columnFilters;
136
172
  },
137
173
  get rowSelection() {
138
- return rowSelection;
174
+ return selectedRowIds;
139
175
  },
140
176
  get globalFilter() {
141
177
  return globalFilter;
@@ -157,7 +193,7 @@
157
193
  columnVisibility = updater instanceof Function ? updater(columnVisibility) : updater;
158
194
  },
159
195
  onRowSelectionChange: (updater: Updater<RowSelectionState>) => {
160
- rowSelection = updater instanceof Function ? updater(rowSelection) : updater;
196
+ rowSelection = updater instanceof Function ? updater(selectedRowIds) : updater;
161
197
  },
162
198
  onColumnFiltersChange: (updater: Updater<ColumnFiltersState>) => {
163
199
  columnFilters = updater instanceof Function ? updater(columnFilters) : updater;
@@ -264,6 +300,16 @@
264
300
  $tableManualPagination = { ...pagination };
265
301
  }
266
302
  });
303
+
304
+ $effect(() => {
305
+ if (!enableChecked) return;
306
+ onselectionchange?.(selectedRows.map((row) => row.original));
307
+ });
308
+
309
+ $effect(() => {
310
+ if (!enableChecked) return;
311
+ onclearselection?.(clearSelection);
312
+ });
267
313
  </script>
268
314
 
269
315
  <section class="table-container">
@@ -334,7 +380,7 @@
334
380
  <table class="table-main">
335
381
  <Header headerGroups={table.getHeaderGroups()} {enableColumnSearch} />
336
382
  {#if hasData}
337
- <Body bodyRows={table.getRowModel().rows} {enableChecked} />
383
+ <Body bodyRows={table.getRowModel().rows} {enableChecked} {disabledRows} />
338
384
  {/if}
339
385
  </table>
340
386
  </article>
@@ -2,31 +2,46 @@ import { type Snippet } from 'svelte';
2
2
  import { type ColumnDef } from './adapter/index.js';
3
3
  import type { ExcelSetting } from './excel-setting.js';
4
4
  import { type Pagination } from './types.js';
5
+ interface BaseProps<T> {
6
+ columns: ColumnDef<T, any>[];
7
+ isLoading?: boolean;
8
+ data?: T[];
9
+ minPageSize?: boolean;
10
+ keepVisibleBulkActions?: boolean;
11
+ enableExportExcel?: boolean;
12
+ enableGlobalSearch?: boolean;
13
+ enableColumnSearch?: boolean;
14
+ enableColumnVisibility?: boolean;
15
+ enableAdvancedFilter?: boolean;
16
+ serverSide?: boolean;
17
+ pagination?: Pagination;
18
+ initialPage?: number;
19
+ initialPageSize?: number;
20
+ excelSetting?: ExcelSetting;
21
+ header?: Snippet;
22
+ bulkActions?: Snippet<[selectedRows: T[], clearSelection: VoidFunction]>;
23
+ onpagechange?: (pageSize: number, pageNumber: number) => void;
24
+ onpagesizechange?: (pageSize: number) => void;
25
+ onnextpage?: (pageSize: number, pageNumber: number) => void;
26
+ onprevpage?: (pageSize: number, pageNumber: number) => void;
27
+ }
28
+ interface TableWithCheckbox<T> extends BaseProps<T> {
29
+ enableChecked: true;
30
+ preselectedRows?: T[];
31
+ disabledRows?: T[];
32
+ onselectionchange?: (selectedRows: T[]) => void;
33
+ onclearselection?: (clearSelection: VoidFunction) => void;
34
+ }
35
+ interface TableWithoutCheckbox<T> extends BaseProps<T> {
36
+ enableChecked?: false;
37
+ preselectedRows?: never;
38
+ disabledRows?: never;
39
+ onselectionchange?: never;
40
+ onclearselection?: never;
41
+ }
42
+ type Props<T> = TableWithCheckbox<T> | TableWithoutCheckbox<T>;
5
43
  declare function $$render<T extends object>(): {
6
- props: {
7
- columns: ColumnDef<T, any>[];
8
- isLoading?: boolean;
9
- data?: T[];
10
- minPageSize?: boolean;
11
- enableChecked?: boolean;
12
- keepVisibleBulkActions?: boolean;
13
- enableExportExcel?: boolean;
14
- enableGlobalSearch?: boolean;
15
- enableColumnSearch?: boolean;
16
- enableColumnVisibility?: boolean;
17
- enableAdvancedFilter?: boolean;
18
- serverSide?: boolean;
19
- pagination?: Pagination;
20
- initialPage?: number;
21
- initialPageSize?: number;
22
- excelSetting?: ExcelSetting;
23
- header?: Snippet;
24
- bulkActions?: Snippet<[selectedRows: T[], clearSelection: VoidFunction]>;
25
- onpagechange?: (pageSize: number, pageNumber: number) => void;
26
- onpagesizechange?: (pageSize: number) => void;
27
- onnextpage?: (pageSize: number, pageNumber: number) => void;
28
- onprevpage?: (pageSize: number, pageNumber: number) => void;
29
- };
44
+ props: Props<T>;
30
45
  exports: {};
31
46
  bindings: "";
32
47
  slots: {};
@@ -1,6 +1,6 @@
1
1
  import { type ColumnDef, type Row } from './adapter/index.js';
2
2
  import type { Action, CursorPagination, Filter, PagePagination, Pagination } from './types.js';
3
- export declare const createCheckedColumn: <T>() => import("./adapter/index.js").DisplayColumnDef<T, unknown>;
3
+ export declare const createCheckedColumn: <T>(disabledRows: T[]) => import("./adapter/index.js").DisplayColumnDef<T, unknown>;
4
4
  export declare const createActionsColumn: <T>(getActions: (row: Row<T>) => Action[]) => import("./adapter/index.js").DisplayColumnDef<T, unknown>;
5
5
  export declare const createStaticTable: <TData>(columns: ColumnDef<TData, any>[], data: TData[]) => import("./adapter/index.js").Table<TData>;
6
6
  export declare const isValidPage: (value: number | undefined) => value is number;
@@ -2,7 +2,7 @@ import ActionsColumn from './ActionsColumn.svelte';
2
2
  import RowCheckBox from './RowCheckBox.svelte';
3
3
  import { createColumnHelper, createTable, getCoreRowModel, renderComponent } from './adapter/index.js';
4
4
  import { DEFAULT_ITEMS_PER_PAGE_OPTIONS, DEFAULT_PAGE, DEFAULT_PAGE_LIMIT, Operation, Operator } from './consts.js';
5
- export const createCheckedColumn = () => {
5
+ export const createCheckedColumn = (disabledRows) => {
6
6
  const columnHelper = createColumnHelper();
7
7
  return columnHelper.display({
8
8
  id: 'checked',
@@ -12,12 +12,13 @@ export const createCheckedColumn = () => {
12
12
  enableHiding: false,
13
13
  header: ({ table }) => renderComponent(RowCheckBox, {
14
14
  checked: table.getIsAllRowsSelected(),
15
+ disabled: disabledRows.length > 0,
15
16
  indeterminate: table.getIsSomeRowsSelected(),
16
17
  onchange: table.getToggleAllRowsSelectedHandler()
17
18
  }),
18
19
  cell: ({ row }) => renderComponent(RowCheckBox, {
19
20
  checked: row.getIsSelected(),
20
- disabled: !row.getCanSelect(),
21
+ disabled: disabledRows.includes(row.original),
21
22
  indeterminate: row.getIsSomeSelected(),
22
23
  onchange: row.getToggleSelectedHandler()
23
24
  }),
package/dist/utils/url.js CHANGED
@@ -1,15 +1,17 @@
1
1
  import { base } from '$app/paths';
2
2
  import { page } from '$app/state';
3
+ const normalizePathname = (path) => path.replace(/^\/v\d+\//, '/');
3
4
  export const getNormalizedPathname = () => {
4
5
  const pathname = page.url.pathname;
5
6
  const withoutBase = base && pathname.startsWith(base) ? pathname.slice(base.length) : pathname;
6
- return withoutBase.replace(/^\/v\d+\//, '/');
7
+ return normalizePathname(withoutBase);
7
8
  };
8
9
  export const getFirstPathSegment = (pathname) => {
9
10
  const matchedPath = /^\/[^/]+/.exec(pathname);
10
11
  return matchedPath?.[0] ?? '/';
11
12
  };
12
13
  export const existRoute = (url, pathname) => {
13
- const regex = new RegExp(`${url}(?![\\w-])`);
14
- return regex.test(pathname);
14
+ const normalizedPathname = normalizePathname(pathname);
15
+ const regex = new RegExp(`^${url}(?![\\w-])`);
16
+ return regex.test(normalizedPathname);
15
17
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softwareone/spi-sv5-library",
3
- "version": "1.15.4",
3
+ "version": "1.16.0",
4
4
  "description": "Svelte components",
5
5
  "keywords": [
6
6
  "svelte",