@softwareone/spi-sv5-library 1.0.0 → 1.1.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.
Files changed (41) hide show
  1. package/README.md +75 -19
  2. package/dist/Button/Button.svelte +12 -1
  3. package/dist/Button/Button.svelte.d.ts +1 -1
  4. package/dist/Chips/Chips.svelte +31 -34
  5. package/dist/Chips/Chips.svelte.d.ts +1 -1
  6. package/dist/ErrorPage/ErrorPage.svelte +5 -7
  7. package/dist/ErrorPage/ErrorPage.svelte.d.ts +1 -2
  8. package/dist/Form/Input/Input.svelte +7 -12
  9. package/dist/Form/Input/Input.svelte.d.ts +2 -2
  10. package/dist/Form/TextArea/TextArea.svelte +6 -4
  11. package/dist/Form/TextArea/TextArea.svelte.d.ts +2 -2
  12. package/dist/Header/Header.svelte +29 -33
  13. package/dist/Header/Header.svelte.d.ts +2 -3
  14. package/dist/Header/HeaderAccount.svelte +8 -6
  15. package/dist/Header/HeaderLoader.svelte +2 -2
  16. package/dist/Header/HeaderLogo.svelte +7 -4
  17. package/dist/Header/HeaderLogo.svelte.d.ts +14 -6
  18. package/dist/Menu/Menu.svelte +11 -11
  19. package/dist/Menu/MenuItem.svelte +7 -11
  20. package/dist/Modal/Modal.svelte +80 -27
  21. package/dist/Modal/Modal.svelte.d.ts +2 -9
  22. package/dist/Modal/ModalContent.svelte +4 -77
  23. package/dist/Modal/ModalContent.svelte.d.ts +2 -3
  24. package/dist/Modal/ModalFooter.svelte +17 -58
  25. package/dist/Modal/ModalFooter.svelte.d.ts +5 -5
  26. package/dist/Modal/ModalHeader.svelte +49 -31
  27. package/dist/Modal/ModalHeader.svelte.d.ts +5 -4
  28. package/dist/Modal/modalState.svelte.d.ts +15 -0
  29. package/dist/Modal/modalState.svelte.js +1 -0
  30. package/dist/ProgressWizard/ProgressWizard.svelte +272 -278
  31. package/dist/ProgressWizard/ProgressWizard.svelte.d.ts +11 -13
  32. package/dist/ProgressWizard/progressWizardState.svelte.d.ts +6 -0
  33. package/dist/ProgressWizard/progressWizardState.svelte.js +1 -0
  34. package/dist/Search/Search.svelte +154 -0
  35. package/dist/Search/Search.svelte.d.ts +10 -0
  36. package/dist/Toast/Toast.svelte +109 -37
  37. package/dist/Toast/toastState.svelte.d.ts +7 -3
  38. package/dist/Toast/toastState.svelte.js +13 -10
  39. package/dist/index.d.ts +5 -2
  40. package/dist/index.js +3 -2
  41. package/package.json +4 -6
@@ -0,0 +1,154 @@
1
+ <script lang="ts">
2
+ import type { HTMLInputAttributes } from 'svelte/elements';
3
+
4
+ type Props = {
5
+ value?: string;
6
+ placeholder?: string;
7
+ disabled?: boolean;
8
+ onclear?: () => void;
9
+ } & Omit<HTMLInputAttributes, 'value' | 'type'>;
10
+
11
+ let {
12
+ value = $bindable(''),
13
+ placeholder = 'Search',
14
+ disabled = false,
15
+ onclear,
16
+ ...props
17
+ }: Props = $props();
18
+
19
+ const hasValue = $derived(!!value);
20
+
21
+ const handleClear = () => {
22
+ value = '';
23
+ onclear?.();
24
+ };
25
+
26
+ const handleKeydown = (event: KeyboardEvent) => {
27
+ if (event.key === 'Escape' && hasValue) {
28
+ handleClear();
29
+ event.preventDefault();
30
+ }
31
+ };
32
+ </script>
33
+
34
+ <div class="search-container" class:disabled>
35
+ <div class="search-wrapper">
36
+ <span class="material-icons-outlined search-icon" aria-hidden="true">search</span>
37
+ <input
38
+ type="search"
39
+ class="search-input"
40
+ bind:value
41
+ {placeholder}
42
+ {disabled}
43
+ onkeydown={handleKeydown}
44
+ {...props}
45
+ />
46
+
47
+ {#if hasValue && !disabled}
48
+ <button type="button" class="clear-button" onclick={handleClear} aria-label="Clear search">
49
+ <span class="material-icons-outlined" aria-hidden="true">close</span>
50
+ </button>
51
+ {/if}
52
+ </div>
53
+ </div>
54
+
55
+ <style>
56
+ .search-container {
57
+ position: relative;
58
+ font-size: 14px;
59
+ line-height: 20px;
60
+ }
61
+
62
+ .search-wrapper {
63
+ position: relative;
64
+ display: flex;
65
+ align-items: center;
66
+ width: 100%;
67
+ border-radius: 8px;
68
+ border: 1px solid #6b7180;
69
+ background: #fff;
70
+ transition: border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
71
+ }
72
+
73
+ .search-wrapper:hover:not(:has(.search-input:disabled)),
74
+ .search-wrapper:focus-within {
75
+ border-color: #472aff;
76
+ box-shadow: 0 0 0 3px rgba(149, 155, 255, 0.3);
77
+ }
78
+
79
+ .search-input {
80
+ width: 100%;
81
+ padding: 8px 40px 8px 40px;
82
+ border: none;
83
+ background: transparent;
84
+ font-size: 14px;
85
+ color: #000;
86
+ }
87
+
88
+ .search-input:focus {
89
+ outline: none;
90
+ }
91
+
92
+ .search-input::placeholder {
93
+ color: #6b7180;
94
+ }
95
+
96
+ .search-input::-webkit-search-cancel-button,
97
+ .search-input::-webkit-search-decoration {
98
+ -webkit-appearance: none;
99
+ }
100
+
101
+ .search-icon,
102
+ .clear-button {
103
+ position: absolute;
104
+ top: 50%;
105
+ transform: translateY(-50%);
106
+ color: #6b7180;
107
+ font-size: 18px;
108
+ }
109
+
110
+ .search-icon {
111
+ left: 12px;
112
+ pointer-events: none;
113
+ }
114
+
115
+ .clear-button {
116
+ right: 12px;
117
+ background: none;
118
+ border: none;
119
+ padding: 4px;
120
+ cursor: pointer;
121
+ border-radius: 4px;
122
+ transition: color 0.2s ease-in-out, background-color 0.2s ease-in-out;
123
+ display: flex;
124
+ align-items: center;
125
+ justify-content: center;
126
+ }
127
+
128
+ .clear-button:hover,
129
+ .clear-button:focus {
130
+ color: #000;
131
+ background-color: #f3f4f6;
132
+ outline: none;
133
+ }
134
+
135
+ .clear-button span {
136
+ font-size: 16px;
137
+ }
138
+
139
+ .disabled .search-wrapper {
140
+ border-color: #d1d5db;
141
+ background-color: #f3f4f6;
142
+ }
143
+
144
+ .disabled .search-input {
145
+ color: #6b7180;
146
+ cursor: not-allowed;
147
+ }
148
+
149
+ @media (max-width: 640px) {
150
+ .search-input {
151
+ font-size: 16px;
152
+ }
153
+ }
154
+ </style>
@@ -0,0 +1,10 @@
1
+ import type { HTMLInputAttributes } from 'svelte/elements';
2
+ type Props = {
3
+ value?: string;
4
+ placeholder?: string;
5
+ disabled?: boolean;
6
+ onclear?: () => void;
7
+ } & Omit<HTMLInputAttributes, 'value' | 'type'>;
8
+ declare const Search: import("svelte").Component<Props, {}, "value">;
9
+ type Search = ReturnType<typeof Search>;
10
+ export default Search;
@@ -1,47 +1,47 @@
1
1
  <script lang="ts">
2
- import { fly } from 'svelte/transition';
3
2
  import { linear } from 'svelte/easing';
4
- import { getToast } from './toastState.svelte';
3
+ import { fly, type FlyParams } from 'svelte/transition';
4
+
5
+ import { deleteToastById, getToast } from './toastState.svelte.js';
5
6
 
6
7
  interface ToastProps {
7
8
  duration?: number;
8
9
  }
9
10
 
10
- let { duration }:ToastProps = $props();
11
+ let { duration }: ToastProps = $props();
11
12
 
12
- const toast = getToast(duration);
13
+ const TRANSITION_DURATION = 100;
14
+ const VERTICAL_DISTANCE = 8;
13
15
 
14
- const getToastColor = (type: string): string => {
15
- const colors: Record<string, string> = {
16
- info: '#472AFF',
17
- warning: '#E87D1E',
18
- danger: '#DC182C',
19
- neutral: '#6B7180',
20
- success: '#008556'
21
- };
22
- return colors[type] || '#6B7180'; // Default to neutral if type is unknown
16
+ const transitionConfig: FlyParams = {
17
+ duration: TRANSITION_DURATION,
18
+ easing: linear,
19
+ y: VERTICAL_DISTANCE
23
20
  };
24
21
 
22
+ const toastNotifications = getToast(duration);
25
23
  </script>
26
24
 
27
- {#if toast.msg.length > 0}
28
- <div class="toast-container" in:fly={{ y: 100, duration: 100, easing: linear }} out:fly={{ y: 100, duration: 100, easing: linear }}>
29
- {#each toast.msg as msg}
30
- <div class="toast" in:fly={{ y: 100, duration: 100, easing: linear }} out:fly={{ y: 100, duration: 100, easing: linear }}>
31
- <div>
32
- <svg
33
- xmlns="http://www.w3.org/2000/svg"
34
- width="8"
35
- height="36"
36
- viewBox="0 0 8 36"
37
- fill="none"
38
- >
39
- <rect width="8" height="36" rx="4" fill={getToastColor(msg.type)} />
40
- </svg>
25
+ {#if toastNotifications.toasts.length > 0}
26
+ <div class="toast-container" in:fly={transitionConfig} out:fly={transitionConfig}>
27
+ {#each toastNotifications.toasts as toast}
28
+ <div class="toast {toast.width ?? 'sm'}" in:fly={transitionConfig} out:fly={transitionConfig}>
29
+ <span class="status-indicator {toast.type}"></span>
30
+ <div class="toast-content-container">
31
+ <div class="toast-content">
32
+ <span>{toast.message}</span>
33
+ {#if toast.link}
34
+ <a class="toast-content-link" href={toast.link} title="View details">View details</a>
35
+ {/if}
36
+ </div>
41
37
  </div>
42
- <div class="toast-content">
43
- <div>{msg.message}</div>
44
- <div class="toast-content-link">View details</div>
38
+ <div class="toast-close-container">
39
+ <button
40
+ type="button"
41
+ class="toast-close-button material-icons"
42
+ aria-label="Close toast"
43
+ onclick={() => deleteToastById(toast.id)}>close</button
44
+ >
45
45
  </div>
46
46
  </div>
47
47
  {/each}
@@ -50,19 +50,21 @@
50
50
 
51
51
  <style>
52
52
  .toast-container {
53
- position: fixed;
53
+ position: fixed;
54
54
  top: 96px;
55
55
  right: 16px;
56
56
  z-index: 9999;
57
57
  display: flex;
58
58
  flex-direction: column;
59
59
  gap: 16px;
60
+ align-items: flex-end;
60
61
  }
62
+
61
63
  .toast {
64
+ width: var(--toast-width);
65
+ height: var(--toast-height);
62
66
  display: inline-flex;
63
67
  padding: 8px;
64
- justify-content: flex-start;
65
- align-items: center;
66
68
  gap: 16px;
67
69
  border-radius: 8px;
68
70
  border: 1px solid #e0e5e8;
@@ -72,28 +74,98 @@
72
74
  0px 1px 3px 0px rgba(51, 56, 64, 0.2),
73
75
  0px 1px 16px 0px rgba(51, 56, 64, 0.1);
74
76
  }
77
+
78
+ .toast.sm {
79
+ --toast-width: 400px;
80
+ --toast-height: 52px;
81
+ --toast-flex-direction: row;
82
+ --toast-gap: 8px;
83
+ --toast-close-button-align: center;
84
+ }
85
+
86
+ .toast.md {
87
+ --toast-width: 600px;
88
+ --toast-height: 88px;
89
+ --toast-flex-direction: column;
90
+ --toast-gap: 4px;
91
+ --toast-close-button-align: flex-start;
92
+ }
93
+
75
94
  .toast-content {
76
95
  display: flex;
96
+ justify-content: space-between;
97
+ flex-direction: var(--toast-flex-direction);
98
+ gap: var(--toast-gap);
77
99
  padding: 8px 0px;
78
- align-items: flex-start;
79
- gap: 8px;
80
100
  color: #000;
81
101
  font-size: 14px;
82
102
  font-style: normal;
83
103
  font-weight: 400;
84
104
  line-height: 20px;
85
105
  }
106
+
107
+ .toast-content-container {
108
+ display: flex;
109
+ align-items: center;
110
+ gap: 16px;
111
+ flex-grow: 1;
112
+ }
113
+
114
+ .status-indicator {
115
+ width: 8px;
116
+ height: calc(var(--toast-height) - 20px);
117
+ flex-shrink: 0;
118
+ border-radius: 4px;
119
+ background-color: var(--toast-bg);
120
+ }
121
+
86
122
  .toast-content-link {
87
123
  display: flex;
88
- align-items: flex-start;
124
+ align-items: center;
89
125
  color: #2b1999;
90
126
  font-size: 14px;
91
- font-style: normal;
92
127
  font-weight: 400;
93
128
  line-height: 20px;
94
129
  }
130
+
95
131
  .toast-content-link:hover {
96
132
  color: #472aff;
97
133
  text-decoration: underline;
98
134
  }
135
+
136
+ .status-indicator.info {
137
+ --toast-bg: #472aff;
138
+ }
139
+
140
+ .status-indicator.warning {
141
+ --toast-bg: #e87d1e;
142
+ }
143
+ .status-indicator.danger {
144
+ --toast-bg: #dc182c;
145
+ }
146
+ .status-indicator.neutral {
147
+ --toast-bg: #6b7180;
148
+ }
149
+ .status-indicator.success {
150
+ --toast-bg: #008556;
151
+ }
152
+
153
+ .toast-close-container {
154
+ display: flex;
155
+ align-items: var(--toast-close-button-align);
156
+ }
157
+
158
+ .toast-close-button {
159
+ background: none;
160
+ border: none;
161
+ }
162
+
163
+ .toast-close-button:hover {
164
+ cursor: pointer;
165
+ color: #434952;
166
+ }
167
+
168
+ .toast-close-button:focus {
169
+ outline: none;
170
+ }
99
171
  </style>
@@ -1,14 +1,18 @@
1
1
  type ToastType = 'info' | 'success' | 'warning' | 'danger' | 'neutral';
2
+ type ToastWidth = 'sm' | 'md';
2
3
  export interface Toast {
3
4
  type: ToastType;
4
5
  message: string;
6
+ width?: ToastWidth;
7
+ link?: string;
5
8
  }
6
9
  interface ToastState extends Toast {
7
10
  id: string;
8
11
  }
9
- export declare function getToast(duration?: number): {
10
- msg: ToastState[];
12
+ export declare const getToast: (duration?: number) => {
13
+ toasts: ToastState[];
11
14
  duration: number;
12
15
  };
13
- export declare function addToast(toast: Toast): void;
16
+ export declare const addToast: (toast: Toast) => void;
17
+ export declare const deleteToastById: (id: string) => void;
14
18
  export {};
@@ -1,20 +1,23 @@
1
1
  const toastState = $state({
2
- msg: [],
3
- duration: 5000,
2
+ toasts: [],
3
+ duration: 5000
4
4
  });
5
- export function getToast(duration) {
5
+ export const getToast = (duration) => {
6
6
  if (duration) {
7
7
  toastState.duration = duration;
8
8
  }
9
9
  return toastState;
10
- }
11
- export function addToast(toast) {
10
+ };
11
+ export const addToast = (toast) => {
12
12
  const newToast = { ...toast, id: crypto.randomUUID() };
13
- toastState.msg.push(newToast);
13
+ toastState.toasts.push(newToast);
14
14
  scheduleToastRemoval(newToast.id);
15
- }
16
- function scheduleToastRemoval(id) {
15
+ };
16
+ const scheduleToastRemoval = (id) => {
17
17
  setTimeout(() => {
18
- toastState.msg = toastState.msg.filter((toast) => toast.id !== id);
18
+ deleteToastById(id);
19
19
  }, toastState.duration);
20
- }
20
+ };
21
+ export const deleteToastById = (id) => {
22
+ toastState.toasts = toastState.toasts.filter((toast) => toast.id !== id);
23
+ };
package/dist/index.d.ts CHANGED
@@ -8,8 +8,8 @@ import { ChipType } from './Chips/chipsState.svelte.js';
8
8
  import ErrorPage from './ErrorPage/ErrorPage.svelte';
9
9
  import Footer from './Footer/Footer.svelte';
10
10
  import Input from './Form/Input/Input.svelte';
11
- import Toggle from './Form/Toggle/Toggle.svelte';
12
11
  import TextArea from './Form/TextArea/TextArea.svelte';
12
+ import Toggle from './Form/Toggle/Toggle.svelte';
13
13
  import Header from './Header/Header.svelte';
14
14
  import HeaderAccount from './Header/HeaderAccount.svelte';
15
15
  import HeaderLoader from './Header/HeaderLoader.svelte';
@@ -20,11 +20,14 @@ import Menu from './Menu/Menu.svelte';
20
20
  import Sidebar from './Menu/Sidebar.svelte';
21
21
  import type { MenuItem } from './Menu/SidebarState.svelte';
22
22
  import Modal from './Modal/Modal.svelte';
23
+ import type { ModalProps } from './Modal/modalState.svelte.js';
23
24
  import ProgressWizard from './ProgressWizard/ProgressWizard.svelte';
25
+ import type { ProgressWizardStep } from './ProgressWizard/progressWizardState.svelte.js';
26
+ import Search from './Search/Search.svelte';
24
27
  import Spinner from './Spinner/Spinner.svelte';
25
28
  import Tabs from './Tabs/Tabs.svelte';
26
29
  import type { Tab } from './Tabs/tabsState.svelte.js';
27
30
  import Toaster from './Toast/Toast.svelte';
28
31
  import { addToast, type Toast } from './Toast/toastState.svelte';
29
32
  import Tooltip from './Tooltip/Tooltip.svelte';
30
- export { addBreadcrumbsNameMap, addToast, Avatar, Breadcrumbs, Button, Card, Chips, ChipType, ColumnType, ErrorPage, Footer, Header, HeaderAccount, HeaderLoader, HeaderLogo, HighlightPanel, ImageType, Input, Menu, TextArea, Modal, ProgressWizard, Sidebar, Spinner, Tabs, Toaster, Toggle, Tooltip, type BreadcrumbsNameMap, type HighlightPanelColumn, type MenuItem, type Tab, type Toast };
33
+ export { addBreadcrumbsNameMap, addToast, Avatar, Breadcrumbs, Button, Card, Chips, ChipType, ColumnType, ErrorPage, Footer, Header, HeaderAccount, HeaderLoader, HeaderLogo, HighlightPanel, ImageType, Input, Menu, Modal, ProgressWizard, Search, Sidebar, Spinner, Tabs, TextArea, Toaster, Toggle, Tooltip, type BreadcrumbsNameMap, type HighlightPanelColumn, type MenuItem, type ModalProps, type ProgressWizardStep, type Tab, type Toast };
package/dist/index.js CHANGED
@@ -8,8 +8,8 @@ import { ChipType } from './Chips/chipsState.svelte.js';
8
8
  import ErrorPage from './ErrorPage/ErrorPage.svelte';
9
9
  import Footer from './Footer/Footer.svelte';
10
10
  import Input from './Form/Input/Input.svelte';
11
- import Toggle from './Form/Toggle/Toggle.svelte';
12
11
  import TextArea from './Form/TextArea/TextArea.svelte';
12
+ import Toggle from './Form/Toggle/Toggle.svelte';
13
13
  import Header from './Header/Header.svelte';
14
14
  import HeaderAccount from './Header/HeaderAccount.svelte';
15
15
  import HeaderLoader from './Header/HeaderLoader.svelte';
@@ -20,9 +20,10 @@ import Menu from './Menu/Menu.svelte';
20
20
  import Sidebar from './Menu/Sidebar.svelte';
21
21
  import Modal from './Modal/Modal.svelte';
22
22
  import ProgressWizard from './ProgressWizard/ProgressWizard.svelte';
23
+ import Search from './Search/Search.svelte';
23
24
  import Spinner from './Spinner/Spinner.svelte';
24
25
  import Tabs from './Tabs/Tabs.svelte';
25
26
  import Toaster from './Toast/Toast.svelte';
26
27
  import { addToast } from './Toast/toastState.svelte';
27
28
  import Tooltip from './Tooltip/Tooltip.svelte';
28
- export { addBreadcrumbsNameMap, addToast, Avatar, Breadcrumbs, Button, Card, Chips, ChipType, ColumnType, ErrorPage, Footer, Header, HeaderAccount, HeaderLoader, HeaderLogo, HighlightPanel, ImageType, Input, Menu, TextArea, Modal, ProgressWizard, Sidebar, Spinner, Tabs, Toaster, Toggle, Tooltip };
29
+ export { addBreadcrumbsNameMap, addToast, Avatar, Breadcrumbs, Button, Card, Chips, ChipType, ColumnType, ErrorPage, Footer, Header, HeaderAccount, HeaderLoader, HeaderLogo, HighlightPanel, ImageType, Input, Menu, Modal, ProgressWizard, Search, 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.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Svelte components",
5
5
  "keywords": [
6
6
  "svelte",
@@ -55,12 +55,10 @@
55
55
  "svelte": "^5.0.0",
56
56
  "svelte-check": "^4.0.0",
57
57
  "typescript": "^5.0.0",
58
- "vite": "^6.0.0"
58
+ "vite": "^6.0.0",
59
+ "zod": "^3.25.76"
59
60
  },
60
61
  "dependencies": {
61
- "@sveltejs/kit": "^2.16.0",
62
- "http-status-codes": "^2.3.0",
63
- "lucide-svelte": "^0.475.0",
64
- "zod": "^3.24.4"
62
+ "@sveltejs/kit": "^2.16.0"
65
63
  }
66
64
  }