@softwareone/spi-sv5-library 1.3.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.
@@ -9,70 +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]>;
24
- infoTooltip?: string;
25
+ onclear?: () => void;
26
+ onchange?: (option: SelectOption) => void;
25
27
  }
26
28
 
27
29
  let {
28
30
  options,
29
31
  label,
30
- placeholder,
32
+ placeholder = 'Please select',
33
+ infoTooltip,
31
34
  required = false,
32
35
  optional = false,
33
36
  searchable = false,
34
- clearable = false,
37
+ hideClearButton = false,
35
38
  multiple = false,
36
- closeAfterSelect = false,
37
- readonly = false,
39
+ keepOpen = false,
40
+ disabled = false,
38
41
  disableValidationColor = false,
39
42
  value = $bindable(),
40
43
  error,
41
44
  customSelection,
42
45
  customOption,
43
- infoTooltip
46
+ onclear,
47
+ onchange
44
48
  }: SelectProps = $props();
45
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
+
46
56
  let dropdownElement: HTMLElement;
47
57
 
48
58
  const isStringArray = (items: string[] | SelectOption[]): items is string[] =>
49
59
  typeof items[0] === 'string';
50
60
 
51
- const originalOptions: SelectOption[] = isStringArray(options)
52
- ? options.map((value) => ({ label: value, value }))
53
- : options;
61
+ const filterOptions = (): SelectOption[] => {
62
+ const text = searchText.toLowerCase();
54
63
 
55
- let searchText = $state('');
56
- let showInTopPosition = $state(false);
57
- let showListOptions = $state(false);
58
- let filteredOptions = $state(originalOptions);
59
- let selectedOptions = $state<SelectOption[]>([]);
60
- 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
+ };
61
73
 
62
- const isInvalid: boolean = $derived(!!error && !disableValidationColor);
63
- const isValid: boolean = $derived(!isInvalid && (!!value || optional) && !disableValidationColor);
64
- const noOptionsAvailable: boolean = $derived(
65
- options.length === 0 || (multiple && selectedOptions?.length === options.length)
74
+ const originalOptions = $derived<SelectOption[]>(
75
+ isStringArray(options) ? options.map((value) => ({ label: value, value })) : options
66
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());
67
82
 
68
83
  const generateSelectOption = (value: string): SelectOption => ({ label: value, value });
84
+
69
85
  const generateMultiselectValue = (option: SelectOption): string => option.value;
70
86
 
71
87
  const canBeVisible = (option: SelectOption): boolean =>
72
88
  !selectedOptions.some((selectedOption) => selectedOption.value === option.value);
73
89
 
74
- const onCloseAfterSelect = () => {
75
- if (closeAfterSelect && showListOptions) {
90
+ const closeAfterSelect = () => {
91
+ if (!keepOpen && showListOptions) {
76
92
  showListOptions = false;
77
93
  }
78
94
  };
@@ -85,7 +101,9 @@
85
101
  selectedOption = option;
86
102
  value = option.value;
87
103
  }
88
- onCloseAfterSelect();
104
+
105
+ closeAfterSelect();
106
+ onchange?.(option);
89
107
  };
90
108
 
91
109
  const onClearAll = () => {
@@ -97,7 +115,7 @@
97
115
  value = '';
98
116
  }
99
117
 
100
- onCloseAfterSelect();
118
+ onclear?.();
101
119
  };
102
120
 
103
121
  const onRemoveSelectedOption = (index: number) => {
@@ -107,16 +125,8 @@
107
125
  value = selectedOptions.map(generateMultiselectValue);
108
126
  };
109
127
 
110
- const onInputSearch = () => {
111
- const text = searchText.toLowerCase();
112
-
113
- filteredOptions = text
114
- ? originalOptions.filter((option) => option.label.toLowerCase().includes(text))
115
- : originalOptions;
116
- };
117
-
118
128
  const onClearSearch = () => {
119
- filteredOptions = originalOptions;
129
+ searchText = '';
120
130
  };
121
131
 
122
132
  const onHandleClickOutside = (event: MouseEvent) => {
@@ -162,7 +172,7 @@
162
172
  {/if}
163
173
 
164
174
  <div
165
- class={['dropdown', readonly ? 'readonly' : [isInvalid && 'invalid', isValid && 'valid']]}
175
+ class={['dropdown', disabled ? 'disabled' : [isInvalid && 'invalid', isValid && 'valid']]}
166
176
  bind:this={dropdownElement}
167
177
  >
168
178
  <section
@@ -193,24 +203,25 @@
193
203
  {/if}
194
204
  </div>
195
205
 
196
- {#if clearable && (selectedOption || selectedOptions.length)}
206
+ {#if !hideClearButton && (selectedOption || selectedOptions.length)}
197
207
  {@render removeButton(onClearAll)}
198
208
  {/if}
199
209
  </section>
200
210
 
201
211
  {#if showListOptions}
202
212
  <section class="dropdown-list" class:upwards={showInTopPosition} use:autoDirection>
203
- {#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}
204
223
  <p class="dropdown-list-no-options-message">No options</p>
205
224
  {:else}
206
- {#if searchable}
207
- <Search
208
- placeholder="Search"
209
- bind:value={searchText}
210
- oninput={onInputSearch}
211
- onclear={onClearSearch}
212
- />
213
- {/if}
214
225
  <ul class="dropdown-list-options-container">
215
226
  {#each filteredOptions as filteredOption}
216
227
  {@const isVisible = multiple ? canBeVisible(filteredOption) : true}
@@ -290,7 +301,7 @@
290
301
  --gray-2: #e0e5e8;
291
302
  --gray-3: #aeb1b9;
292
303
  --gray-4: #6b7180;
293
- --border: 1px solid var(--gray-3);
304
+ --border: 1px solid var(--gray-4);
294
305
 
295
306
  display: flex;
296
307
  flex-direction: column;
@@ -314,7 +325,7 @@
314
325
  box-shadow 0.2s ease-in-out;
315
326
  }
316
327
 
317
- .dropdown.readonly {
328
+ .dropdown.disabled {
318
329
  cursor: not-allowed;
319
330
 
320
331
  > .dropdown-container {
@@ -323,11 +334,11 @@
323
334
  }
324
335
  }
325
336
 
326
- .dropdown:not(.readonly, .invalid, .valid):hover {
337
+ .dropdown:not(.disabled, .invalid, .valid):hover {
327
338
  border: 1px solid var(--primary-color);
328
339
  }
329
340
 
330
- .dropdown:not(.readonly, .invalid, .valid):focus-within {
341
+ .dropdown:not(.disabled, .invalid, .valid):focus-within {
331
342
  border: 1px solid var(--primary-color);
332
343
  box-shadow: 0 0 0 3px rgba(149, 155, 255, 0.3);
333
344
  }
@@ -4,19 +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]>;
19
- infoTooltip?: string;
20
+ onclear?: () => void;
21
+ onchange?: (option: SelectOption) => void;
20
22
  }
21
23
  declare const Select: import("svelte").Component<SelectProps, {}, "value">;
22
24
  type Select = ReturnType<typeof Select>;
@@ -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
+ };
package/dist/index.d.ts CHANGED
@@ -28,8 +28,9 @@ import Modal from './Modal/Modal.svelte';
28
28
  import type { ModalProps } from './Modal/modalState.svelte.js';
29
29
  import Notification from './Notification/Notification.svelte';
30
30
  import ProgressPage from './ProgressPage/ProgressPage.svelte';
31
+ import { setProgressWizardStepsContext, getProgressWizardContext } from './ProgressWizard/context.js';
31
32
  import ProgressWizard from './ProgressWizard/ProgressWizard.svelte';
32
- import type { ProgressWizardStep } from './ProgressWizard/progressWizardState.svelte.js';
33
+ import { setStepValidity, type ProgressWizardStep } from './ProgressWizard/progressWizardState.svelte.js';
33
34
  import Search from './Search/Search.svelte';
34
35
  import Spinner from './Spinner/Spinner.svelte';
35
36
  import Tabs from './Tabs/Tabs.svelte';
@@ -37,4 +38,4 @@ import type { Tab } from './Tabs/tabsState.svelte.js';
37
38
  import Toaster from './Toast/Toast.svelte';
38
39
  import { addToast, type Toast } from './Toast/toastState.svelte';
39
40
  import Tooltip from './Tooltip/Tooltip.svelte';
40
- export { Accordion, addBreadcrumbsNameMap, addToast, Avatar, Breadcrumbs, Button, Card, Chips, ChipType, ColumnType, ErrorPage, Footer, Header, HeaderAccount, HeaderLoader, HeaderLogo, HighlightPanel, Home, ImageType, Input, Menu, Modal, Notification, ProgressPage, ProgressWizard, Search, Select, 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 };
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
@@ -24,11 +24,13 @@ import Sidebar from './Menu/Sidebar.svelte';
24
24
  import Modal from './Modal/Modal.svelte';
25
25
  import Notification from './Notification/Notification.svelte';
26
26
  import ProgressPage from './ProgressPage/ProgressPage.svelte';
27
+ import { setProgressWizardStepsContext, getProgressWizardContext } from './ProgressWizard/context.js';
27
28
  import ProgressWizard from './ProgressWizard/ProgressWizard.svelte';
29
+ import { setStepValidity } from './ProgressWizard/progressWizardState.svelte.js';
28
30
  import Search from './Search/Search.svelte';
29
31
  import Spinner from './Spinner/Spinner.svelte';
30
32
  import Tabs from './Tabs/Tabs.svelte';
31
33
  import Toaster from './Toast/Toast.svelte';
32
34
  import { addToast } from './Toast/toastState.svelte';
33
35
  import Tooltip from './Tooltip/Tooltip.svelte';
34
- export { Accordion, addBreadcrumbsNameMap, addToast, Avatar, Breadcrumbs, Button, Card, Chips, ChipType, ColumnType, ErrorPage, Footer, Header, HeaderAccount, HeaderLoader, HeaderLogo, HighlightPanel, Home, 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.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "Svelte components",
5
5
  "keywords": [
6
6
  "svelte",