@softwareone/spi-sv5-library 1.2.0 → 1.3.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.
@@ -2,31 +2,29 @@
2
2
  import type { Snippet } from 'svelte';
3
3
 
4
4
  interface CardProps {
5
- type?: 'layout' | 'highlight';
6
5
  children?: Snippet;
6
+ disablePadding?: boolean;
7
7
  }
8
8
 
9
- let { type = 'layout', children }: CardProps = $props();
9
+ let { children, disablePadding = false }: CardProps = $props();
10
10
  </script>
11
11
 
12
- <div class="card card-{type}">
12
+ <div class="card" class:padding={!disablePadding}>
13
13
  {@render children?.()}
14
14
  </div>
15
15
 
16
16
  <style>
17
17
  .card {
18
18
  width: 100%;
19
- min-height: var(--card-min-height, auto);
20
19
  background: #fff;
21
20
  margin-bottom: 24px;
22
- padding: 24px;
23
21
  border-radius: 16px;
24
22
  box-shadow:
25
23
  0px 1px 16px rgba(107, 113, 128, 0.1),
26
24
  0px 1px 3px rgba(107, 113, 128, 0.2);
27
25
  }
28
26
 
29
- .card-layout {
30
- --card-min-height: 100vh;
27
+ .padding {
28
+ padding: 24px;
31
29
  }
32
30
  </style>
@@ -1,7 +1,7 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  interface CardProps {
3
- type?: 'layout' | 'highlight';
4
3
  children?: Snippet;
4
+ disablePadding?: boolean;
5
5
  }
6
6
  declare const Card: import("svelte").Component<CardProps, {}, "">;
7
7
  type Card = ReturnType<typeof Card>;
@@ -14,6 +14,7 @@
14
14
  description?: string;
15
15
  currency?: string;
16
16
  disableValidationColor?: boolean;
17
+ infoTooltip?: string;
17
18
  }
18
19
 
19
20
  let {
@@ -30,6 +31,7 @@
30
31
  readonly,
31
32
  oninput,
32
33
  disableValidationColor = false,
34
+ infoTooltip,
33
35
  ...props
34
36
  }: InputProps = $props();
35
37
 
@@ -51,7 +53,7 @@
51
53
  </script>
52
54
 
53
55
  <div class="form-container">
54
- <Label {label} {id} {optional} {required} />
56
+ <Label {label} {id} {optional} {required} {infoTooltip} />
55
57
  <div
56
58
  class={[
57
59
  'form-input-wrapper',
@@ -9,6 +9,7 @@ interface InputProps extends Omit<HTMLInputAttributes, 'type' | 'value'> {
9
9
  description?: string;
10
10
  currency?: string;
11
11
  disableValidationColor?: boolean;
12
+ infoTooltip?: string;
12
13
  }
13
14
  declare const Input: import("svelte").Component<InputProps, {}, "value">;
14
15
  type Input = ReturnType<typeof Input>;
@@ -1,12 +1,15 @@
1
1
  <script lang="ts">
2
+ import { Tooltip } from '../index.js';
3
+
2
4
  interface LabelProps {
3
5
  label?: string;
4
6
  id?: string | null;
5
7
  required?: boolean | null;
6
8
  optional?: boolean;
9
+ infoTooltip?: string;
7
10
  }
8
11
 
9
- let { label, id, required, optional }: LabelProps = $props();
12
+ let { label, id, required, optional, infoTooltip }: LabelProps = $props();
10
13
  </script>
11
14
 
12
15
  {#if label}
@@ -17,6 +20,13 @@
17
20
  {:else if optional}
18
21
  <span class="form-label-optional">Optional</span>
19
22
  {/if}
23
+ {#if infoTooltip}
24
+ <Tooltip content={infoTooltip} width="sm">
25
+ {#snippet children()}
26
+ <span class="material-icons-outlined">info</span>
27
+ {/snippet}
28
+ </Tooltip>
29
+ {/if}
20
30
  </div>
21
31
  {/if}
22
32
 
@@ -34,4 +44,9 @@
34
44
  .form-label-required {
35
45
  color: #dc2626;
36
46
  }
47
+
48
+ .material-icons-outlined {
49
+ font-size: 14px;
50
+ vertical-align: middle;
51
+ }
37
52
  </style>
@@ -3,6 +3,7 @@ interface LabelProps {
3
3
  id?: string | null;
4
4
  required?: boolean | null;
5
5
  optional?: boolean;
6
+ infoTooltip?: string;
6
7
  }
7
8
  declare const Label: import("svelte").Component<LabelProps, {}, "">;
8
9
  type Label = ReturnType<typeof Label>;
@@ -9,68 +9,86 @@
9
9
  options: string[] | SelectOption[];
10
10
  label?: string;
11
11
  placeholder?: string;
12
+ infoTooltip?: string;
12
13
  required?: boolean;
13
14
  optional?: boolean;
14
15
  searchable?: boolean;
15
- clearable?: boolean;
16
+ hideClearButton?: boolean;
16
17
  multiple?: boolean;
17
- closeAfterSelect?: boolean;
18
- readonly?: boolean;
18
+ keepOpen?: boolean;
19
+ disabled?: boolean;
19
20
  disableValidationColor?: boolean;
20
21
  value?: string | string[] | null;
21
22
  error?: string | string[];
22
23
  customSelection?: Snippet<[option: SelectOption]>;
23
24
  customOption?: Snippet<[option: SelectOption]>;
25
+ onclear?: () => void;
26
+ onchange?: (option: SelectOption) => void;
24
27
  }
25
28
 
26
29
  let {
27
30
  options,
28
31
  label,
29
- placeholder,
32
+ placeholder = 'Please select',
33
+ infoTooltip,
30
34
  required = false,
31
35
  optional = false,
32
36
  searchable = false,
33
- clearable = false,
37
+ hideClearButton = false,
34
38
  multiple = false,
35
- closeAfterSelect = false,
36
- readonly = false,
39
+ keepOpen = false,
40
+ disabled = false,
37
41
  disableValidationColor = false,
38
42
  value = $bindable(),
39
43
  error,
40
44
  customSelection,
41
- customOption
45
+ customOption,
46
+ onclear,
47
+ onchange
42
48
  }: SelectProps = $props();
43
49
 
50
+ let searchText = $state('');
51
+ let showInTopPosition = $state(false);
52
+ let showListOptions = $state(false);
53
+ let selectedOptions = $state<SelectOption[]>([]);
54
+ let selectedOption = $state<SelectOption | undefined>();
55
+
44
56
  let dropdownElement: HTMLElement;
45
57
 
46
58
  const isStringArray = (items: string[] | SelectOption[]): items is string[] =>
47
59
  typeof items[0] === 'string';
48
60
 
49
- const originalOptions: SelectOption[] = isStringArray(options)
50
- ? options.map((value) => ({ label: value, value }))
51
- : options;
61
+ const filterOptions = (): SelectOption[] => {
62
+ const text = searchText.toLowerCase();
52
63
 
53
- let searchText = $state('');
54
- let showInTopPosition = $state(false);
55
- let showListOptions = $state(false);
56
- let filteredOptions = $state(originalOptions);
57
- let selectedOptions = $state<SelectOption[]>([]);
58
- let selectedOption = $state<SelectOption | undefined>();
64
+ return text
65
+ ? originalOptions.filter((option) => option.label.toLowerCase().includes(text))
66
+ : originalOptions;
67
+ };
68
+
69
+ const checkNoOptionsAvailable = (): boolean => {
70
+ if (!options.length) return true;
71
+ return multiple && originalOptions.every((option) => valueSet.has(option.value));
72
+ };
59
73
 
60
- const isInvalid: boolean = $derived(!!error && !disableValidationColor);
61
- const isValid: boolean = $derived(!isInvalid && (!!value || optional) && !disableValidationColor);
62
- const noOptionsAvailable: boolean = $derived(
63
- options.length === 0 || (multiple && selectedOptions?.length === options.length)
74
+ const originalOptions = $derived<SelectOption[]>(
75
+ isStringArray(options) ? options.map((value) => ({ label: value, value })) : options
64
76
  );
77
+ const valueSet = $derived<Set<string>>(new Set(value));
78
+ const isInvalid = $derived<boolean>(!!error && !disableValidationColor);
79
+ const isValid = $derived<boolean>(!isInvalid && (!!value || optional) && !disableValidationColor);
80
+ const filteredOptions = $derived<SelectOption[]>(filterOptions());
81
+ const noOptionsAvailable = $derived<boolean>(checkNoOptionsAvailable());
65
82
 
66
83
  const generateSelectOption = (value: string): SelectOption => ({ label: value, value });
84
+
67
85
  const generateMultiselectValue = (option: SelectOption): string => option.value;
68
86
 
69
87
  const canBeVisible = (option: SelectOption): boolean =>
70
88
  !selectedOptions.some((selectedOption) => selectedOption.value === option.value);
71
89
 
72
- const onCloseAfterSelect = () => {
73
- if (closeAfterSelect && showListOptions) {
90
+ const closeAfterSelect = () => {
91
+ if (!keepOpen && showListOptions) {
74
92
  showListOptions = false;
75
93
  }
76
94
  };
@@ -83,7 +101,9 @@
83
101
  selectedOption = option;
84
102
  value = option.value;
85
103
  }
86
- onCloseAfterSelect();
104
+
105
+ closeAfterSelect();
106
+ onchange?.(option);
87
107
  };
88
108
 
89
109
  const onClearAll = () => {
@@ -95,7 +115,7 @@
95
115
  value = '';
96
116
  }
97
117
 
98
- onCloseAfterSelect();
118
+ onclear?.();
99
119
  };
100
120
 
101
121
  const onRemoveSelectedOption = (index: number) => {
@@ -105,16 +125,8 @@
105
125
  value = selectedOptions.map(generateMultiselectValue);
106
126
  };
107
127
 
108
- const onInputSearch = () => {
109
- const text = searchText.toLowerCase();
110
-
111
- filteredOptions = text
112
- ? originalOptions.filter((option) => option.label.toLowerCase().includes(text))
113
- : originalOptions;
114
- };
115
-
116
128
  const onClearSearch = () => {
117
- filteredOptions = originalOptions;
129
+ searchText = '';
118
130
  };
119
131
 
120
132
  const onHandleClickOutside = (event: MouseEvent) => {
@@ -156,11 +168,11 @@
156
168
 
157
169
  <div class="form-container">
158
170
  {#if label}
159
- <Label {label} {required} {optional} />
171
+ <Label {label} {required} {optional} {infoTooltip} />
160
172
  {/if}
161
173
 
162
174
  <div
163
- class={['dropdown', readonly ? 'readonly' : [isInvalid && 'invalid', isValid && 'valid']]}
175
+ class={['dropdown', disabled ? 'disabled' : [isInvalid && 'invalid', isValid && 'valid']]}
164
176
  bind:this={dropdownElement}
165
177
  >
166
178
  <section
@@ -191,24 +203,25 @@
191
203
  {/if}
192
204
  </div>
193
205
 
194
- {#if clearable && (selectedOption || selectedOptions.length)}
206
+ {#if !hideClearButton && (selectedOption || selectedOptions.length)}
195
207
  {@render removeButton(onClearAll)}
196
208
  {/if}
197
209
  </section>
198
210
 
199
211
  {#if showListOptions}
200
212
  <section class="dropdown-list" class:upwards={showInTopPosition} use:autoDirection>
201
- {#if noOptionsAvailable}
213
+ {#if searchable && !noOptionsAvailable}
214
+ <Search
215
+ placeholder="Search"
216
+ bind:value={searchText}
217
+ onclear={onClearSearch}
218
+ autocomplete="off"
219
+ />
220
+ {/if}
221
+
222
+ {#if noOptionsAvailable || !filteredOptions.length}
202
223
  <p class="dropdown-list-no-options-message">No options</p>
203
224
  {:else}
204
- {#if searchable}
205
- <Search
206
- placeholder="Search"
207
- bind:value={searchText}
208
- oninput={onInputSearch}
209
- onclear={onClearSearch}
210
- />
211
- {/if}
212
225
  <ul class="dropdown-list-options-container">
213
226
  {#each filteredOptions as filteredOption}
214
227
  {@const isVisible = multiple ? canBeVisible(filteredOption) : true}
@@ -288,7 +301,7 @@
288
301
  --gray-2: #e0e5e8;
289
302
  --gray-3: #aeb1b9;
290
303
  --gray-4: #6b7180;
291
- --border: 1px solid var(--gray-3);
304
+ --border: 1px solid var(--gray-4);
292
305
 
293
306
  display: flex;
294
307
  flex-direction: column;
@@ -312,7 +325,7 @@
312
325
  box-shadow 0.2s ease-in-out;
313
326
  }
314
327
 
315
- .dropdown.readonly {
328
+ .dropdown.disabled {
316
329
  cursor: not-allowed;
317
330
 
318
331
  > .dropdown-container {
@@ -321,11 +334,11 @@
321
334
  }
322
335
  }
323
336
 
324
- .dropdown:not(.readonly, .invalid, .valid):hover {
337
+ .dropdown:not(.disabled, .invalid, .valid):hover {
325
338
  border: 1px solid var(--primary-color);
326
339
  }
327
340
 
328
- .dropdown:not(.readonly, .invalid, .valid):focus-within {
341
+ .dropdown:not(.disabled, .invalid, .valid):focus-within {
329
342
  border: 1px solid var(--primary-color);
330
343
  box-shadow: 0 0 0 3px rgba(149, 155, 255, 0.3);
331
344
  }
@@ -4,18 +4,21 @@ interface SelectProps {
4
4
  options: string[] | SelectOption[];
5
5
  label?: string;
6
6
  placeholder?: string;
7
+ infoTooltip?: string;
7
8
  required?: boolean;
8
9
  optional?: boolean;
9
10
  searchable?: boolean;
10
- clearable?: boolean;
11
+ hideClearButton?: boolean;
11
12
  multiple?: boolean;
12
- closeAfterSelect?: boolean;
13
- readonly?: boolean;
13
+ keepOpen?: boolean;
14
+ disabled?: boolean;
14
15
  disableValidationColor?: boolean;
15
16
  value?: string | string[] | null;
16
17
  error?: string | string[];
17
18
  customSelection?: Snippet<[option: SelectOption]>;
18
19
  customOption?: Snippet<[option: SelectOption]>;
20
+ onclear?: () => void;
21
+ onchange?: (option: SelectOption) => void;
19
22
  }
20
23
  declare const Select: import("svelte").Component<SelectProps, {}, "value">;
21
24
  type Select = ReturnType<typeof Select>;
@@ -10,6 +10,7 @@
10
10
  description?: string;
11
11
  maximumCharactersAllowed?: number;
12
12
  resize?: boolean;
13
+ infoTooltip?: string;
13
14
  }
14
15
 
15
16
  let {
@@ -22,6 +23,7 @@
22
23
  maximumCharactersAllowed,
23
24
  resize = false,
24
25
  id,
26
+ infoTooltip,
25
27
  ...props
26
28
  }: TextAreaProps = $props();
27
29
 
@@ -38,7 +40,7 @@
38
40
  </script>
39
41
 
40
42
  <div class="form-container">
41
- <Label {label} {id} {optional} {required} />
43
+ <Label {label} {id} {optional} {required} {infoTooltip} />
42
44
 
43
45
  <div class={['form-textarea-wrapper', isInvalid && 'error', isValid && 'success']}>
44
46
  <textarea
@@ -87,7 +89,6 @@
87
89
  line-height: 20px;
88
90
  }
89
91
 
90
-
91
92
  .form-message {
92
93
  font-size: 12px;
93
94
  }
@@ -7,6 +7,7 @@ interface TextAreaProps extends Omit<HTMLTextareaAttributes, 'value'> {
7
7
  description?: string;
8
8
  maximumCharactersAllowed?: number;
9
9
  resize?: boolean;
10
+ infoTooltip?: string;
10
11
  }
11
12
  declare const TextArea: import("svelte").Component<TextAreaProps, {}, "value">;
12
13
  type TextArea = ReturnType<typeof TextArea>;
@@ -65,6 +65,10 @@
65
65
  line-height: 20px;
66
66
  }
67
67
 
68
+ h2 {
69
+ font-weight: bold;
70
+ }
71
+
68
72
  .text-description {
69
73
  color: #6b7180;
70
74
  }
@@ -0,0 +1,116 @@
1
+ <script lang="ts">
2
+ import type { HomeItem } from '../index.js';
3
+
4
+ interface HomeProps {
5
+ title: string;
6
+ homeItems: HomeItem[];
7
+ }
8
+
9
+ let { title, homeItems }: HomeProps = $props();
10
+
11
+ const hasSingleItem: boolean = homeItems.length === 1;
12
+ const hasManyItems: boolean = homeItems.length > 1;
13
+ </script>
14
+
15
+ <h1 class={['home-title', hasSingleItem && 'centered']}>
16
+ {title}
17
+ </h1>
18
+
19
+ <section class={['home-container', hasSingleItem && 'centered', hasManyItems && 'grid']}>
20
+ {#each homeItems as homeItem}
21
+ <a href={homeItem.url} class="home-item">
22
+ <span>
23
+ <img src={homeItem.icon} alt={homeItem.text} />
24
+ </span>
25
+ <div>
26
+ <h2>{homeItem.text}</h2>
27
+ <p>{homeItem.detail}</p>
28
+ </div>
29
+ </a>
30
+ {/each}
31
+ </section>
32
+
33
+ <style>
34
+ .home-title {
35
+ padding-bottom: 16px;
36
+ font-size: 18px;
37
+ line-height: 1.55;
38
+ font-weight: 600;
39
+ }
40
+
41
+ .home-title.centered {
42
+ display: flex;
43
+ justify-content: center;
44
+ }
45
+
46
+ .home-container {
47
+ --black-1: #434952;
48
+ --black-2: #273444;
49
+ --white: #fff;
50
+ }
51
+
52
+ .home-container.centered {
53
+ display: flex;
54
+ flex-direction: column;
55
+ align-items: center;
56
+ }
57
+
58
+ .home-container.grid {
59
+ display: grid;
60
+ gap: 24px;
61
+
62
+ @media (width >= 640px) {
63
+ grid-template-columns: repeat(1, minmax(0, 1fr));
64
+ }
65
+
66
+ @media (width >= 1024px) {
67
+ grid-template-columns: repeat(2, minmax(0, 1fr));
68
+ }
69
+
70
+ @media (width >= 1536px) {
71
+ grid-template-columns: repeat(3, minmax(0, 1fr));
72
+ }
73
+ }
74
+
75
+ .home-container > .home-item {
76
+ display: flex;
77
+ max-height: 150px;
78
+ max-width: 770px;
79
+ gap: 16px;
80
+ padding: 12px;
81
+ border-radius: 16px;
82
+ background: var(--black-1);
83
+ transition: background 0.2s ease-in-out;
84
+
85
+ > span {
86
+ display: flex;
87
+ align-items: center;
88
+
89
+ > img {
90
+ width: 40px;
91
+ height: 40px;
92
+ filter: invert(100%);
93
+ }
94
+ }
95
+
96
+ > div {
97
+ color: var(--white);
98
+
99
+ > h2 {
100
+ padding-bottom: 8px;
101
+ font-size: 16px;
102
+ line-height: 1.5;
103
+ font-weight: 600;
104
+ }
105
+
106
+ > p {
107
+ font-size: 14px;
108
+ line-height: 1.42;
109
+ }
110
+ }
111
+ }
112
+
113
+ .home-container > .home-item:hover {
114
+ background: var(--black-2);
115
+ }
116
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { HomeItem } from '../index.js';
2
+ interface HomeProps {
3
+ title: string;
4
+ homeItems: HomeItem[];
5
+ }
6
+ declare const Home: import("svelte").Component<HomeProps, {}, "">;
7
+ type Home = ReturnType<typeof Home>;
8
+ export default Home;
@@ -0,0 +1,4 @@
1
+ import { type MenuItem } from '../index.js';
2
+ export type HomeItem = Omit<MenuItem, 'children'> & {
3
+ detail?: string;
4
+ };
@@ -0,0 +1 @@
1
+ import {} from '../index.js';
@@ -16,70 +16,52 @@
16
16
  footer
17
17
  }: ModalProps = $props();
18
18
 
19
- let dialog = $state<HTMLDialogElement>();
20
-
21
- $effect(() => {
22
- showModal ? dialog?.showModal() : dialog?.close();
23
- });
24
-
25
19
  const onHandleClose = () => {
26
20
  showModal = false;
27
21
  onclose();
28
22
  };
29
23
  </script>
30
24
 
31
- <dialog
32
- bind:this={dialog}
33
- onclose={onHandleClose}
34
- class="modal {width}"
35
- transition:scale={{
36
- duration: 150,
37
- start: 0.95
38
- }}
39
- >
40
- <ModalHeader {title} {errorIcon} onclose={onHandleClose} />
41
- <ModalContent content={children} />
42
- <ModalFooter {footer} onclose={onHandleClose} />
43
- </dialog>
25
+ <div class="modal-container">
26
+ <div
27
+ onclose={onHandleClose}
28
+ class="modal {width}"
29
+ transition:scale={{
30
+ duration: 150,
31
+ start: 0.95
32
+ }}
33
+ >
34
+ <ModalHeader {title} {errorIcon} onclose={onHandleClose} />
35
+ <ModalContent content={children} />
36
+ <ModalFooter {footer} onclose={onHandleClose} />
37
+ </div>
38
+ </div>
44
39
 
45
40
  <style>
46
- dialog {
47
- align-self: center;
41
+ .modal-container {
42
+ display: flex;
43
+ position: fixed;
44
+ top: 0;
45
+ left: 0;
46
+ z-index: 1000;
47
+ width: 100%;
48
+ height: 100%;
49
+ background-color: #e0e5e880;
50
+ }
51
+
52
+ .modal {
53
+ width: var(--modal-width);
54
+ max-height: 90%;
48
55
  border-radius: 16px;
49
56
  background-color: #ffffff;
50
- border: none;
51
57
  margin: auto;
58
+ overflow-y: auto;
52
59
  box-shadow:
53
60
  0px 1px 10px 0px rgba(51, 56, 64, 0.12),
54
61
  0px 2px 4px -1px rgba(51, 56, 64, 0.2),
55
62
  0px 4px 5px 0px rgba(51, 56, 64, 0.14);
56
63
  }
57
64
 
58
- dialog:focus-visible {
59
- outline: none;
60
- }
61
-
62
- dialog::backdrop {
63
- background: #e0e5e880;
64
- }
65
-
66
- dialog::-webkit-scrollbar {
67
- width: 10px;
68
- }
69
-
70
- dialog::-webkit-scrollbar-thumb {
71
- background: #888;
72
- border-radius: 10px;
73
- }
74
-
75
- dialog::-webkit-scrollbar-thumb:hover {
76
- background: #555;
77
- }
78
-
79
- .modal {
80
- width: var(--modal-width);
81
- }
82
-
83
65
  .modal.xs {
84
66
  --modal-width: 500px;
85
67
  }
@@ -96,7 +78,20 @@
96
78
  --modal-width: 1000px;
97
79
  }
98
80
 
99
- :global(html:has(dialog.modal[open])) {
81
+ .modal::-webkit-scrollbar {
82
+ width: 10px;
83
+ }
84
+
85
+ .modal::-webkit-scrollbar-thumb {
86
+ background: #888;
87
+ border-radius: 10px;
88
+ }
89
+
90
+ .modal::-webkit-scrollbar-thumb:hover {
91
+ background: #555;
92
+ }
93
+
94
+ :global(html:has(.modal-container)) {
100
95
  overflow: hidden;
101
96
  }
102
97
  </style>
@@ -7,9 +7,8 @@
7
7
  steps: ProgressWizardStep[];
8
8
  readonly?: boolean;
9
9
  currentStep: number;
10
- lastActiveStep: number;
11
10
  content: Snippet;
12
- additionalButtons: Snippet;
11
+ additionalButtons: Snippet<[lastActiveStep: number]>;
13
12
  oncancel: VoidFunction;
14
13
  }
15
14
 
@@ -17,7 +16,6 @@
17
16
  steps,
18
17
  readonly = false,
19
18
  currentStep = $bindable(0),
20
- lastActiveStep = $bindable(0),
21
19
  content,
22
20
  additionalButtons,
23
21
  oncancel
@@ -25,6 +23,7 @@
25
23
 
26
24
  const allStepsDisabled: boolean = steps.every((step) => step.disabled);
27
25
  const firstActiveStep: number = steps.findIndex((step) => !step.disabled) + 1;
26
+ const lastActiveStep: number = steps.findLastIndex((step) => !step.disabled) + 1;
28
27
 
29
28
  const onclickNext = () => {
30
29
  const nextStepIndex = steps.slice(currentStep).findIndex((step) => !step.disabled);
@@ -47,7 +46,6 @@
47
46
  };
48
47
 
49
48
  currentStep = stepToStart();
50
- lastActiveStep = steps.findLastIndex((step) => !step.disabled) + 1;
51
49
 
52
50
  $effect(() => {
53
51
  if (!readonly) {
@@ -132,7 +130,7 @@
132
130
  {/if}
133
131
  {/if}
134
132
 
135
- {@render additionalButtons()}
133
+ {@render additionalButtons(lastActiveStep)}
136
134
  {/if}
137
135
  </div>
138
136
  </section>
@@ -4,11 +4,10 @@ interface ProgressWizardProps {
4
4
  steps: ProgressWizardStep[];
5
5
  readonly?: boolean;
6
6
  currentStep: number;
7
- lastActiveStep: number;
8
7
  content: Snippet;
9
- additionalButtons: Snippet;
8
+ additionalButtons: Snippet<[lastActiveStep: number]>;
10
9
  oncancel: VoidFunction;
11
10
  }
12
- declare const ProgressWizard: import("svelte").Component<ProgressWizardProps, {}, "currentStep" | "lastActiveStep">;
11
+ declare const ProgressWizard: import("svelte").Component<ProgressWizardProps, {}, "currentStep">;
13
12
  type ProgressWizard = ReturnType<typeof ProgressWizard>;
14
13
  export default ProgressWizard;
@@ -0,0 +1,4 @@
1
+ import { type Writable } from 'svelte/store';
2
+ import type { ProgressWizardStep } from './progressWizardState.svelte.js';
3
+ export declare const setProgressWizardStepsContext: (steps: ProgressWizardStep[]) => Writable<ProgressWizardStep[]>;
4
+ export declare const getProgressWizardContext: () => Writable<ProgressWizardStep[]>;
@@ -0,0 +1,11 @@
1
+ import { getContext, setContext } from 'svelte';
2
+ import { writable } from 'svelte/store';
3
+ const stepsKey = Symbol('steps');
4
+ export const setProgressWizardStepsContext = (steps) => {
5
+ const stepsContext = writable(steps);
6
+ setContext(stepsKey, stepsContext);
7
+ return stepsContext;
8
+ };
9
+ export const getProgressWizardContext = () => {
10
+ return getContext(stepsKey);
11
+ };
@@ -4,3 +4,4 @@ export type ProgressWizardStep = {
4
4
  isValid: boolean;
5
5
  disabled: boolean;
6
6
  };
7
+ export declare const setStepValidity: (steps: ProgressWizardStep[], title: string, isValid: boolean) => void;
@@ -1 +1,6 @@
1
- export {};
1
+ export const setStepValidity = (steps, title, isValid) => {
2
+ const index = steps.findIndex((step) => step.title === title);
3
+ if (index !== -1) {
4
+ steps[index].isValid = isValid;
5
+ }
6
+ };
@@ -59,6 +59,7 @@
59
59
  display: flex;
60
60
  gap: 16px;
61
61
  border-bottom: 1px solid #e0e5e8;
62
+ padding: 0 24px;
62
63
  }
63
64
 
64
65
  .tabs-list button {
@@ -68,6 +69,7 @@
68
69
  position: relative;
69
70
  border: none;
70
71
  font-size: 16px;
72
+ font-weight: 500;
71
73
  }
72
74
 
73
75
  .tabs-list button:hover {
@@ -77,6 +79,7 @@
77
79
 
78
80
  .tabs-list button.active {
79
81
  color: #472aff;
82
+ font-weight: 600;
80
83
  }
81
84
 
82
85
  .tabs-list button::after {
package/dist/index.d.ts CHANGED
@@ -19,6 +19,8 @@ import HeaderLoader from './Header/HeaderLoader.svelte';
19
19
  import HeaderLogo from './Header/HeaderLogo.svelte';
20
20
  import HighlightPanel from './HighlightPanel/HighlightPanel.svelte';
21
21
  import { ColumnType, ImageType, type HighlightPanelColumn } from './HighlightPanel/highlightPanelState.svelte.js';
22
+ import Home from './Home/Home.svelte';
23
+ import type { HomeItem } from './Home/homeState.svelte.js';
22
24
  import Menu from './Menu/Menu.svelte';
23
25
  import Sidebar from './Menu/Sidebar.svelte';
24
26
  import type { MenuItem } from './Menu/SidebarState.svelte';
@@ -26,8 +28,9 @@ import Modal from './Modal/Modal.svelte';
26
28
  import type { ModalProps } from './Modal/modalState.svelte.js';
27
29
  import Notification from './Notification/Notification.svelte';
28
30
  import ProgressPage from './ProgressPage/ProgressPage.svelte';
31
+ import { setProgressWizardStepsContext, getProgressWizardContext } from './ProgressWizard/context.js';
29
32
  import ProgressWizard from './ProgressWizard/ProgressWizard.svelte';
30
- import type { ProgressWizardStep } from './ProgressWizard/progressWizardState.svelte.js';
33
+ import { setStepValidity, type ProgressWizardStep } from './ProgressWizard/progressWizardState.svelte.js';
31
34
  import Search from './Search/Search.svelte';
32
35
  import Spinner from './Spinner/Spinner.svelte';
33
36
  import Tabs from './Tabs/Tabs.svelte';
@@ -35,4 +38,4 @@ import type { Tab } from './Tabs/tabsState.svelte.js';
35
38
  import Toaster from './Toast/Toast.svelte';
36
39
  import { addToast, type Toast } from './Toast/toastState.svelte';
37
40
  import Tooltip from './Tooltip/Tooltip.svelte';
38
- export { Accordion, addBreadcrumbsNameMap, addToast, Avatar, Breadcrumbs, Button, Card, Chips, ChipType, ColumnType, ErrorPage, Footer, Header, HeaderAccount, HeaderLoader, HeaderLogo, HighlightPanel, ImageType, Input, Menu, Modal, Notification, ProgressPage, ProgressWizard, Search, Select, Sidebar, Spinner, Tabs, TextArea, Toaster, Toggle, Tooltip, type BreadcrumbsNameMap, type HighlightPanelColumn, type MenuItem, type ModalProps, type ProgressWizardStep, type SelectOption, type Tab, type Toast };
41
+ export { Accordion, addBreadcrumbsNameMap, addToast, Avatar, Breadcrumbs, Button, Card, Chips, ChipType, ColumnType, ErrorPage, Footer, getProgressWizardContext, Header, HeaderAccount, HeaderLoader, HeaderLogo, HighlightPanel, Home, ImageType, Input, Menu, Modal, Notification, ProgressPage, ProgressWizard, Search, Select, setStepValidity, setProgressWizardStepsContext, Sidebar, Spinner, Tabs, TextArea, Toaster, Toggle, Tooltip, type BreadcrumbsNameMap, type HighlightPanelColumn, type HomeItem, type MenuItem, type ModalProps, type ProgressWizardStep, type SelectOption, type Tab, type Toast };
package/dist/index.js CHANGED
@@ -18,16 +18,19 @@ import HeaderLoader from './Header/HeaderLoader.svelte';
18
18
  import HeaderLogo from './Header/HeaderLogo.svelte';
19
19
  import HighlightPanel from './HighlightPanel/HighlightPanel.svelte';
20
20
  import { ColumnType, ImageType } from './HighlightPanel/highlightPanelState.svelte.js';
21
+ import Home from './Home/Home.svelte';
21
22
  import Menu from './Menu/Menu.svelte';
22
23
  import Sidebar from './Menu/Sidebar.svelte';
23
24
  import Modal from './Modal/Modal.svelte';
24
25
  import Notification from './Notification/Notification.svelte';
25
26
  import ProgressPage from './ProgressPage/ProgressPage.svelte';
27
+ import { setProgressWizardStepsContext, getProgressWizardContext } from './ProgressWizard/context.js';
26
28
  import ProgressWizard from './ProgressWizard/ProgressWizard.svelte';
29
+ import { setStepValidity } from './ProgressWizard/progressWizardState.svelte.js';
27
30
  import Search from './Search/Search.svelte';
28
31
  import Spinner from './Spinner/Spinner.svelte';
29
32
  import Tabs from './Tabs/Tabs.svelte';
30
33
  import Toaster from './Toast/Toast.svelte';
31
34
  import { addToast } from './Toast/toastState.svelte';
32
35
  import Tooltip from './Tooltip/Tooltip.svelte';
33
- export { Accordion, addBreadcrumbsNameMap, addToast, Avatar, Breadcrumbs, Button, Card, Chips, ChipType, ColumnType, ErrorPage, Footer, Header, HeaderAccount, HeaderLoader, HeaderLogo, HighlightPanel, ImageType, Input, Menu, Modal, Notification, ProgressPage, ProgressWizard, Search, Select, Sidebar, Spinner, Tabs, TextArea, Toaster, Toggle, Tooltip };
36
+ export { Accordion, addBreadcrumbsNameMap, addToast, Avatar, Breadcrumbs, Button, Card, Chips, ChipType, ColumnType, ErrorPage, Footer, getProgressWizardContext, Header, HeaderAccount, HeaderLoader, HeaderLogo, HighlightPanel, Home, ImageType, Input, Menu, Modal, Notification, ProgressPage, ProgressWizard, Search, Select, setStepValidity, setProgressWizardStepsContext, Sidebar, Spinner, Tabs, TextArea, Toaster, Toggle, Tooltip };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softwareone/spi-sv5-library",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "Svelte components",
5
5
  "keywords": [
6
6
  "svelte",