@juspay/svelte-ui-components 2.9.0 → 2.11.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 (189) hide show
  1. package/README.md +337 -77
  2. package/dist/Accordion/Accordion.svelte +4 -2
  3. package/dist/Accordion/Accordion.svelte.d.ts +2 -4
  4. package/dist/Accordion/properties.d.ts +6 -0
  5. package/dist/Accordion/properties.js +1 -0
  6. package/dist/Animations/ModalAnimation.svelte +11 -9
  7. package/dist/Avatar/Avatar.svelte +122 -0
  8. package/dist/Avatar/Avatar.svelte.d.ts +4 -0
  9. package/dist/Avatar/properties.d.ts +15 -0
  10. package/dist/Avatar/properties.js +1 -0
  11. package/dist/Badge/Badge.svelte +2 -2
  12. package/dist/Badge/properties.d.ts +1 -0
  13. package/dist/Banner/Banner.svelte +132 -48
  14. package/dist/Banner/Banner.svelte.d.ts +1 -1
  15. package/dist/Banner/properties.d.ts +8 -3
  16. package/dist/Book/Book.svelte +281 -0
  17. package/dist/Book/Book.svelte.d.ts +4 -0
  18. package/dist/Book/properties.d.ts +24 -0
  19. package/dist/Book/properties.js +1 -0
  20. package/dist/BrandLoader/BrandLoader.svelte +3 -3
  21. package/dist/BrandLoader/properties.d.ts +1 -0
  22. package/dist/Browser/Browser.svelte +193 -0
  23. package/dist/Browser/Browser.svelte.d.ts +3 -0
  24. package/dist/Browser/properties.d.ts +16 -0
  25. package/dist/Browser/properties.js +1 -0
  26. package/dist/Button/Button.svelte +20 -7
  27. package/dist/Button/properties.d.ts +7 -4
  28. package/dist/Calendar/Calendar.svelte +476 -0
  29. package/dist/Calendar/Calendar.svelte.d.ts +4 -0
  30. package/dist/Calendar/properties.d.ts +30 -0
  31. package/dist/Calendar/properties.js +1 -0
  32. package/dist/Carousel/Carousel.svelte +19 -16
  33. package/dist/Carousel/properties.d.ts +1 -0
  34. package/dist/CheckListItem/CheckListItem.svelte +31 -26
  35. package/dist/CheckListItem/properties.d.ts +4 -1
  36. package/dist/Checkbox/Checkbox.svelte +157 -0
  37. package/dist/Checkbox/Checkbox.svelte.d.ts +4 -0
  38. package/dist/Checkbox/properties.d.ts +17 -0
  39. package/dist/Checkbox/properties.js +1 -0
  40. package/dist/Choicebox/Choicebox.svelte +85 -0
  41. package/dist/Choicebox/Choicebox.svelte.d.ts +4 -0
  42. package/dist/Choicebox/properties.d.ts +14 -0
  43. package/dist/Choicebox/properties.js +1 -0
  44. package/dist/CommandMenu/CommandMenu.svelte +452 -0
  45. package/dist/CommandMenu/CommandMenu.svelte.d.ts +4 -0
  46. package/dist/CommandMenu/properties.d.ts +26 -0
  47. package/dist/CommandMenu/properties.js +1 -0
  48. package/dist/ContextMenu/ContextMenu.svelte +308 -0
  49. package/dist/ContextMenu/ContextMenu.svelte.d.ts +4 -0
  50. package/dist/ContextMenu/properties.d.ts +26 -0
  51. package/dist/ContextMenu/properties.js +1 -0
  52. package/dist/Gauge/Gauge.svelte +70 -0
  53. package/dist/Gauge/Gauge.svelte.d.ts +4 -0
  54. package/dist/Gauge/properties.d.ts +9 -0
  55. package/dist/Gauge/properties.js +1 -0
  56. package/dist/GridItem/GridItem.svelte +5 -4
  57. package/dist/GridItem/properties.d.ts +1 -0
  58. package/dist/Icon/Icon.svelte +3 -3
  59. package/dist/Icon/properties.d.ts +1 -0
  60. package/dist/IconStack/IconStack.svelte +3 -3
  61. package/dist/IconStack/properties.d.ts +1 -0
  62. package/dist/Img/Img.svelte +5 -3
  63. package/dist/Img/properties.d.ts +5 -1
  64. package/dist/Input/Input.svelte +12 -6
  65. package/dist/Input/properties.d.ts +1 -0
  66. package/dist/InputButton/InputButton.svelte +4 -3
  67. package/dist/InputButton/properties.d.ts +5 -6
  68. package/dist/KeyboardInput/KeyboardInput.svelte +93 -0
  69. package/dist/KeyboardInput/KeyboardInput.svelte.d.ts +4 -0
  70. package/dist/KeyboardInput/properties.d.ts +12 -0
  71. package/dist/KeyboardInput/properties.js +1 -0
  72. package/dist/ListItem/ListItem.svelte +31 -28
  73. package/dist/ListItem/properties.d.ts +1 -0
  74. package/dist/Loader/Loader.svelte +10 -6
  75. package/dist/Loader/Loader.svelte.d.ts +3 -25
  76. package/dist/Loader/properties.d.ts +3 -0
  77. package/dist/Loader/properties.js +1 -0
  78. package/dist/LoadingDots/LoadingDots.svelte +64 -0
  79. package/dist/LoadingDots/LoadingDots.svelte.d.ts +3 -0
  80. package/dist/LoadingDots/properties.d.ts +8 -0
  81. package/dist/LoadingDots/properties.js +1 -0
  82. package/dist/Menu/Menu.svelte +349 -0
  83. package/dist/Menu/Menu.svelte.d.ts +4 -0
  84. package/dist/Menu/properties.d.ts +24 -0
  85. package/dist/Menu/properties.js +1 -0
  86. package/dist/Modal/Modal.svelte +10 -9
  87. package/dist/Modal/properties.d.ts +1 -0
  88. package/dist/Pagination/Pagination.svelte +152 -0
  89. package/dist/Pagination/Pagination.svelte.d.ts +4 -0
  90. package/dist/Pagination/properties.d.ts +14 -0
  91. package/dist/Pagination/properties.js +1 -0
  92. package/dist/Phone/Phone.svelte +234 -0
  93. package/dist/Phone/Phone.svelte.d.ts +3 -0
  94. package/dist/Phone/properties.d.ts +11 -0
  95. package/dist/Phone/properties.js +1 -0
  96. package/dist/Pill/Pill.svelte +130 -0
  97. package/dist/Pill/Pill.svelte.d.ts +4 -0
  98. package/dist/Pill/properties.d.ts +16 -0
  99. package/dist/Pill/properties.js +1 -0
  100. package/dist/Progress/Progress.svelte +68 -0
  101. package/dist/Progress/Progress.svelte.d.ts +4 -0
  102. package/dist/Progress/properties.d.ts +10 -0
  103. package/dist/Progress/properties.js +1 -0
  104. package/dist/Radio/Radio.svelte +128 -0
  105. package/dist/Radio/Radio.svelte.d.ts +4 -0
  106. package/dist/Radio/properties.d.ts +15 -0
  107. package/dist/Radio/properties.js +1 -0
  108. package/dist/RelativeTime/RelativeTime.svelte +117 -0
  109. package/dist/RelativeTime/RelativeTime.svelte.d.ts +4 -0
  110. package/dist/RelativeTime/properties.d.ts +13 -0
  111. package/dist/RelativeTime/properties.js +1 -0
  112. package/dist/Scroller/Scroller.svelte +389 -0
  113. package/dist/Scroller/Scroller.svelte.d.ts +4 -0
  114. package/dist/Scroller/properties.d.ts +30 -0
  115. package/dist/Scroller/properties.js +1 -0
  116. package/dist/Select/Select.svelte +382 -344
  117. package/dist/Select/Select.svelte.d.ts +1 -1
  118. package/dist/Select/properties.d.ts +16 -26
  119. package/dist/Sheet/Sheet.svelte +264 -0
  120. package/dist/Sheet/Sheet.svelte.d.ts +4 -0
  121. package/dist/Sheet/properties.d.ts +19 -0
  122. package/dist/Sheet/properties.js +1 -0
  123. package/dist/Shimmer/Shimmer.svelte +44 -0
  124. package/dist/Shimmer/Shimmer.svelte.d.ts +4 -0
  125. package/dist/Shimmer/properties.d.ts +4 -0
  126. package/dist/Shimmer/properties.js +1 -0
  127. package/dist/Slider/Slider.svelte +144 -0
  128. package/dist/Slider/Slider.svelte.d.ts +4 -0
  129. package/dist/Slider/properties.d.ts +17 -0
  130. package/dist/Slider/properties.js +1 -0
  131. package/dist/Snippet/Snippet.svelte +123 -0
  132. package/dist/Snippet/Snippet.svelte.d.ts +4 -0
  133. package/dist/Snippet/properties.d.ts +15 -0
  134. package/dist/Snippet/properties.js +1 -0
  135. package/dist/SplitButton/SplitButton.svelte +135 -0
  136. package/dist/SplitButton/SplitButton.svelte.d.ts +4 -0
  137. package/dist/SplitButton/properties.d.ts +17 -0
  138. package/dist/SplitButton/properties.js +1 -0
  139. package/dist/Status/Status.svelte +5 -3
  140. package/dist/Status/properties.d.ts +1 -0
  141. package/dist/Stepper/Step.svelte +3 -3
  142. package/dist/Stepper/Stepper.svelte +3 -3
  143. package/dist/Stepper/properties.d.ts +2 -0
  144. package/dist/Table/Table.svelte +15 -8
  145. package/dist/Table/properties.d.ts +1 -0
  146. package/dist/Tabs/Tabs.svelte +240 -0
  147. package/dist/Tabs/Tabs.svelte.d.ts +4 -0
  148. package/dist/Tabs/properties.d.ts +16 -0
  149. package/dist/Tabs/properties.js +1 -0
  150. package/dist/ThemeSwitcher/ThemeSwitcher.svelte +249 -0
  151. package/dist/ThemeSwitcher/ThemeSwitcher.svelte.d.ts +4 -0
  152. package/dist/ThemeSwitcher/properties.d.ts +19 -0
  153. package/dist/ThemeSwitcher/properties.js +1 -0
  154. package/dist/Toast/Toast.svelte +25 -30
  155. package/dist/Toast/properties.d.ts +1 -0
  156. package/dist/Toggle/Toggle.svelte +2 -2
  157. package/dist/Toggle/properties.d.ts +1 -0
  158. package/dist/Toolbar/Toolbar.svelte +8 -7
  159. package/dist/Toolbar/properties.d.ts +1 -0
  160. package/dist/Tooltip/Tooltip.svelte +153 -0
  161. package/dist/Tooltip/Tooltip.svelte.d.ts +4 -0
  162. package/dist/Tooltip/properties.d.ts +13 -0
  163. package/dist/Tooltip/properties.js +1 -0
  164. package/dist/assets/battery.svg +5 -0
  165. package/dist/assets/checkmark.svg +3 -0
  166. package/dist/assets/chevron-down-sm.svg +3 -0
  167. package/dist/assets/chevron-down.svg +3 -0
  168. package/dist/assets/chevron-left-lg.svg +3 -0
  169. package/dist/assets/chevron-left.svg +3 -0
  170. package/dist/assets/chevron-right-lg.svg +3 -0
  171. package/dist/assets/chevron-right.svg +3 -0
  172. package/dist/assets/chevron-up.svg +3 -0
  173. package/dist/assets/close.svg +4 -0
  174. package/dist/assets/copy.svg +4 -0
  175. package/dist/assets/error-circle.svg +5 -0
  176. package/dist/assets/lock.svg +3 -0
  177. package/dist/assets/minus.svg +3 -0
  178. package/dist/assets/monitor.svg +5 -0
  179. package/dist/assets/moon.svg +3 -0
  180. package/dist/assets/palette.svg +7 -0
  181. package/dist/assets/search.svg +4 -0
  182. package/dist/assets/signal.svg +6 -0
  183. package/dist/assets/sun.svg +11 -0
  184. package/dist/assets/wifi.svg +3 -0
  185. package/dist/index.d.ts +55 -0
  186. package/dist/index.js +27 -0
  187. package/dist/utils.d.ts +2 -0
  188. package/dist/utils.js +18 -4
  189. package/package.json +26 -1
@@ -1,433 +1,471 @@
1
1
  <script lang="ts">
2
- import { onDestroy, onMount } from 'svelte';
3
- import type { SelectProperties } from './properties';
4
- import type { ButtonProperties } from '../Button/properties';
5
- import Img from '../Img/Img.svelte';
6
- import Button from '../Button/Button.svelte';
7
- import CheckListItem from '../CheckListItem/CheckListItem.svelte';
8
-
9
- let selectedElementDiv: HTMLDivElement | null = $state(null);
2
+ import { onMount, tick } from 'svelte';
3
+ import type { SelectItem, SelectProperties } from './properties';
4
+ import Pill from '../Pill/Pill.svelte';
5
+ import chevronDownSvg from '../assets/chevron-down.svg?raw';
10
6
 
11
7
  let {
12
- dropDownIconAlt = '',
8
+ items,
9
+ value = $bindable([]),
10
+ multiple = false,
11
+ searchable = false,
13
12
  placeholder = '',
14
- label = '',
15
- allItems = [],
16
- selectedItem = '',
17
- selectedItemLabel = null,
18
- showSelectedItemInDropdown = false,
19
- selectMultipleItems = false,
20
- hideDropDownIcon,
21
- dropDownIcon,
22
- leftIcon = null,
23
- showSingleSelectButton,
24
- showSelectedItem = true,
25
- showSelectedItemCount = false,
13
+ disabled = false,
26
14
  testId,
27
- labelTestId,
28
- itemTestId,
29
- leftContent,
30
- bottomContent,
31
- onselect,
32
- ondropdownClick,
33
- onkeydown
15
+ onchange,
16
+ classes
34
17
  }: SelectProperties = $props();
35
18
 
36
- const dropDownIconUrl = dropDownIcon ?? 'https://sdk.breeze.in/gallery/icons/down-arrow.svg';
19
+ let isOpen = $state(false);
20
+ let query = $state('');
21
+ let highlightedIndex = $state(-1);
22
+ let containerEl: HTMLDivElement | null = $state(null);
23
+ let searchInputEl: HTMLInputElement | null = $state(null);
24
+ let triggerEl: HTMLDivElement | null = $state(null);
37
25
 
38
- let applyButtonProps: ButtonProperties = $derived({
39
- text: `Select (${selectedItem.length})`,
40
- enable: selectedItem.length > 0,
41
- showLoader: false,
42
- type: 'submit'
43
- });
26
+ const listboxId = `select-listbox-${Math.random().toString(36).slice(2, 9)}`;
44
27
 
45
- const selectAllButtonProps: ButtonProperties = {
46
- text: 'Select All',
47
- enable: true,
48
- showLoader: false,
49
- type: 'submit'
50
- };
28
+ function getLabel(id: string): string {
29
+ const found = items.find((item) => item.id === id);
30
+ return typeof found === 'object' ? found.label : id;
31
+ }
32
+
33
+ let filteredItems: SelectItem[] = $derived(
34
+ searchable && query.length > 0
35
+ ? items.filter((item) => item.label.toLowerCase().includes(query.toLowerCase()))
36
+ : items
37
+ );
51
38
 
52
- let isSelectOpen = $state(false);
39
+ let displayText = $derived.by(() => {
40
+ const firstId = value.at(0);
41
+ if (typeof firstId !== 'string') {
42
+ return '';
43
+ }
44
+ return getLabel(firstId);
45
+ });
53
46
 
54
- let nonSelectedItems = $derived(
55
- allItems.filter((item) =>
56
- selectMultipleItems ? !selectedItem.includes(item) : item !== selectedItem
57
- )
47
+ let highlightedOptionId: string | null = $derived(
48
+ highlightedIndex >= 0 ? `${listboxId}-option-${highlightedIndex}` : null
58
49
  );
59
50
 
60
- function isSelected(selectedItem: string | string[], item: string) {
61
- if (Array.isArray(selectedItem)) {
62
- return selectedItem.includes(item);
63
- } else {
64
- return selectedItem.trim() === item.trim();
51
+ let searchPlaceholder = $derived(isOpen && displayText.length > 0 ? displayText : placeholder);
52
+
53
+ async function open(): Promise<void> {
54
+ if (disabled || isOpen) {
55
+ return;
56
+ }
57
+ isOpen = true;
58
+ highlightedIndex = -1;
59
+ query = '';
60
+ if (searchable) {
61
+ await tick();
62
+ if (searchInputEl !== null) {
63
+ searchInputEl.focus();
64
+ }
65
65
  }
66
66
  }
67
67
 
68
- function selectItem(item: string) {
69
- if (selectMultipleItems && Array.isArray(selectedItemLabel) && Array.isArray(selectedItem)) {
70
- if (isSelected(selectedItem, item)) {
71
- selectedItem = selectedItem.filter((selected) => selected !== item);
72
- selectedItemLabel = selectedItemLabel.filter((label) => label !== item);
73
- } else {
74
- selectedItem = [...selectedItem, item];
75
- selectedItemLabel = [...selectedItemLabel, item];
76
- }
68
+ function close(): void {
69
+ isOpen = false;
70
+ query = '';
71
+ highlightedIndex = -1;
72
+ }
73
+
74
+ function selectItem(id: string): void {
75
+ if (disabled) {
76
+ return;
77
+ }
78
+ if (multiple) {
79
+ value = value.includes(id) ? value.filter((v) => v !== id) : [...value, id];
77
80
  } else {
78
- selectedItem = [item];
79
- selectedItemLabel = [item];
81
+ value = [id];
82
+ close();
80
83
  }
81
- if (!selectMultipleItems) {
82
- toggleSelect();
83
- dispatchEvent();
84
+ onchange?.(value);
85
+ }
86
+
87
+ function removeItem(id: string): void {
88
+ if (disabled) {
89
+ return;
84
90
  }
91
+ value = value.filter((v) => v !== id);
92
+ onchange?.(value);
85
93
  }
86
94
 
87
- function dispatchEvent() {
88
- onselect?.({ selectedItems: selectedItem });
89
- isSelectOpen = false;
95
+ function selectHighlighted(): void {
96
+ if (highlightedIndex < 0 || highlightedIndex >= filteredItems.length) {
97
+ return;
98
+ }
99
+ const item = filteredItems.at(highlightedIndex);
100
+ if (typeof item === 'object' && item !== null) {
101
+ selectItem(item.id);
102
+ }
90
103
  }
91
104
 
92
- function toggleSelect() {
93
- isSelectOpen = !isSelectOpen;
94
- ondropdownClick?.();
105
+ async function moveHighlight(delta: number): Promise<void> {
106
+ const next = highlightedIndex + delta;
107
+ if (next < 0 || next >= filteredItems.length) {
108
+ return;
109
+ }
110
+ highlightedIndex = next;
111
+ await tick();
112
+ if (containerEl !== null) {
113
+ const el = containerEl.querySelector('.select-option.highlighted');
114
+ if (el instanceof HTMLElement) {
115
+ el.scrollIntoView({ block: 'nearest' });
116
+ }
117
+ }
95
118
  }
96
119
 
97
- function selectAllItems() {
98
- if (selectedItem.length === allItems.length) {
99
- selectedItem = [];
100
- selectedItemLabel = [];
120
+ function handleTriggerClick(event: MouseEvent): void {
121
+ if (event.target instanceof HTMLInputElement) {
122
+ if (!isOpen) {
123
+ open();
124
+ }
125
+ return;
126
+ }
127
+ if (multiple && searchable) {
128
+ open();
129
+ } else if (isOpen) {
130
+ close();
101
131
  } else {
102
- selectedItem = [...allItems];
103
- selectedItemLabel = [...allItems];
132
+ open();
104
133
  }
105
134
  }
106
135
 
107
- function closeSelect(event: Event) {
108
- const clickedElement = event.target as HTMLElement;
109
- if (selectedElementDiv !== null && !selectedElementDiv.contains(clickedElement)) {
110
- const isItemClicked = clickedElement.classList.contains('item');
111
- const isApplyButtonClicked = clickedElement.classList.contains('apply-btn');
112
- const isClearAllButtonClicked = clickedElement.innerText === 'Clear All';
113
- const isSelectAllButtonClicked = clickedElement.innerText === 'Select All';
114
- const isCheckListClicked = clickedElement.classList.contains('checkbox');
115
- if (
116
- !isItemClicked &&
117
- !isApplyButtonClicked &&
118
- !isClearAllButtonClicked &&
119
- !isSelectAllButtonClicked &&
120
- !isCheckListClicked
121
- ) {
122
- isSelectOpen = false;
123
- }
136
+ function handleKeydown(event: KeyboardEvent): void {
137
+ if (disabled) {
138
+ return;
139
+ }
140
+ switch (event.key) {
141
+ case 'Enter':
142
+ event.preventDefault();
143
+ if (isOpen) {
144
+ selectHighlighted();
145
+ } else {
146
+ open();
147
+ }
148
+ break;
149
+ case ' ':
150
+ if (!(event.target instanceof HTMLInputElement)) {
151
+ event.preventDefault();
152
+ if (isOpen) {
153
+ selectHighlighted();
154
+ } else {
155
+ open();
156
+ }
157
+ }
158
+ break;
159
+ case 'ArrowDown':
160
+ event.preventDefault();
161
+ if (isOpen) {
162
+ moveHighlight(1);
163
+ } else {
164
+ open();
165
+ }
166
+ break;
167
+ case 'ArrowUp':
168
+ event.preventDefault();
169
+ moveHighlight(-1);
170
+ break;
171
+ case 'Escape':
172
+ if (isOpen) {
173
+ close();
174
+ if (!searchable && triggerEl !== null) {
175
+ triggerEl.focus();
176
+ }
177
+ }
178
+ break;
179
+ case 'Backspace':
180
+ if (multiple && query === '' && value.length > 0) {
181
+ const lastId = value.at(-1);
182
+ if (typeof lastId === 'string') {
183
+ removeItem(lastId);
184
+ }
185
+ }
186
+ break;
187
+ case 'Tab':
188
+ if (isOpen) {
189
+ close();
190
+ }
191
+ break;
124
192
  }
125
193
  }
126
194
 
127
- onMount(() => {
128
- document.addEventListener('click', closeSelect);
129
- });
195
+ function handleSearchInput(event: Event): void {
196
+ if (!(event.target instanceof HTMLInputElement)) {
197
+ return;
198
+ }
199
+ query = event.target.value;
200
+ if (!isOpen) {
201
+ isOpen = true;
202
+ }
203
+ highlightedIndex = -1;
204
+ }
130
205
 
131
- onDestroy(() => {
132
- if (typeof window !== 'undefined') {
133
- document.removeEventListener('click', closeSelect);
206
+ function handleSearchFocus(): void {
207
+ if (!isOpen) {
208
+ open();
134
209
  }
210
+ }
211
+
212
+ function handleClickOutside(event: Event): void {
213
+ if (
214
+ event.target instanceof Node &&
215
+ containerEl !== null &&
216
+ !containerEl.contains(event.target)
217
+ ) {
218
+ close();
219
+ }
220
+ }
221
+
222
+ onMount(() => {
223
+ document.addEventListener('click', handleClickOutside);
224
+ return () => {
225
+ document.removeEventListener('click', handleClickOutside);
226
+ };
135
227
  });
136
228
  </script>
137
229
 
138
- {#if label !== null && label !== ''}
139
- <label class="label-container" for={label} data-pw={labelTestId}>
140
- {label}
141
- </label>
142
- {/if}
143
-
144
- {#if allItems.length !== 0}
145
- <div class="select">
146
- <div
147
- class="selected item"
148
- onclick={toggleSelect}
149
- bind:this={selectedElementDiv}
150
- {onkeydown}
151
- role="button"
152
- tabindex="0"
153
- data-pw={testId}
154
- >
155
- {#if leftIcon !== null}
156
- <div class="icon-container">
157
- <Img {...leftIcon} />
158
- </div>
159
- {/if}
160
- {#if leftContent}
161
- {@render leftContent()}
162
- {/if}
163
- <div class="selected-content">
164
- {#if selectMultipleItems && Array.isArray(selectedItemLabel) && Array.isArray(selectedItem)}
165
- {#if selectedItem.length === 0}
166
- {placeholder}
167
- {:else if selectedItemLabel?.length === 0 || (showSelectedItemInDropdown && showSelectedItem !== false)}
168
- {selectedItem.join(', ')}
169
- {:else if showSelectedItem !== false}
170
- {selectedItemLabel.join(', ')}
171
- {:else}
172
- {placeholder}
173
- {/if}
174
- {:else if selectedItem === ''}
175
- {placeholder}
176
- {:else if selectedItemLabel === null || (selectedItemLabel === '' && showSelectedItem !== false)}
177
- {selectedItem}
178
- {:else if showSelectedItem !== false}
179
- {selectedItemLabel}
180
- {:else}
181
- {placeholder}
182
- {/if}
183
- </div>
184
- <div class="filler"></div>
185
- {#if showSelectedItemCount && selectMultipleItems && Array.isArray(selectedItem)}
186
- <div class="selected-item-count">
187
- {selectedItem.length}
188
- </div>
189
- {/if}
190
- {#if !hideDropDownIcon}
191
- <img
192
- src={dropDownIconUrl}
193
- alt={dropDownIconAlt}
194
- class="arrow {isSelectOpen ? 'active' : ''}"
230
+ <div
231
+ class="select {classes ?? ''}"
232
+ class:open={isOpen}
233
+ class:disabled
234
+ bind:this={containerEl}
235
+ {...typeof testId === 'string' ? { 'data-pw': testId } : {}}
236
+ >
237
+ <div
238
+ class="select-trigger"
239
+ bind:this={triggerEl}
240
+ onclick={handleTriggerClick}
241
+ onkeydown={handleKeydown}
242
+ role="combobox"
243
+ aria-expanded={isOpen}
244
+ aria-haspopup="listbox"
245
+ aria-controls={listboxId}
246
+ {...highlightedOptionId !== null ? { 'aria-activedescendant': highlightedOptionId } : {}}
247
+ tabindex={disabled ? -1 : searchable ? -1 : 0}
248
+ >
249
+ {#if multiple}
250
+ {#each value as id (id)}
251
+ <Pill
252
+ text={getLabel(id)}
253
+ dismissible
254
+ {disabled}
255
+ ondismiss={() => removeItem(id)}
256
+ {...typeof testId === 'string' ? { testId: `${testId}-pill-${id}` } : {}}
195
257
  />
258
+ {/each}
259
+ {#if searchable}
260
+ <input
261
+ class="select-search"
262
+ type="text"
263
+ value={query}
264
+ oninput={handleSearchInput}
265
+ onfocus={handleSearchFocus}
266
+ bind:this={searchInputEl}
267
+ placeholder={value.length === 0 ? placeholder : ''}
268
+ {disabled}
269
+ autocomplete="off"
270
+ tabindex={disabled ? -1 : 0}
271
+ />
272
+ {:else if value.length === 0}
273
+ <span class="select-placeholder">{placeholder}</span>
196
274
  {/if}
197
- </div>
198
- <div
199
- class="non-selected-items"
200
- style="--non-selected-display:{isSelectOpen ? 'inline-block' : 'none'};"
201
- >
202
- {#if selectMultipleItems && !showSingleSelectButton}
203
- <div class="select-all-btn">
204
- <CheckListItem
205
- checked={Array.isArray(selectedItem) && selectedItem.length === allItems.length}
206
- text=""
207
- onclick={selectAllItems}
208
- />
209
- <Button {...selectAllButtonProps} onclick={selectAllItems} />
210
- </div>
211
- {/if}
212
- <div class="item-list">
213
- {#each showSelectedItemInDropdown ? allItems : nonSelectedItems as item (item)}
275
+ {:else if searchable}
276
+ <input
277
+ class="select-search"
278
+ type="text"
279
+ value={isOpen ? query : displayText}
280
+ oninput={handleSearchInput}
281
+ onfocus={handleSearchFocus}
282
+ bind:this={searchInputEl}
283
+ placeholder={searchPlaceholder}
284
+ {disabled}
285
+ autocomplete="off"
286
+ tabindex={disabled ? -1 : 0}
287
+ />
288
+ {:else}
289
+ <span class={displayText.length > 0 ? 'select-value' : 'select-placeholder'}>
290
+ {displayText.length > 0 ? displayText : placeholder}
291
+ </span>
292
+ {/if}
293
+ <!-- eslint-disable svelte/no-at-html-tags -->
294
+ <span class="select-arrow">{@html chevronDownSvg}</span>
295
+ </div>
296
+
297
+ {#if isOpen && !disabled}
298
+ <div class="select-dropdown" role="listbox" id={listboxId} aria-multiselectable={multiple}>
299
+ {#if filteredItems.length === 0}
300
+ <div class="select-empty">No results</div>
301
+ {:else}
302
+ {#each filteredItems as item, index (item.id)}
303
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
214
304
  <div
215
- {onkeydown}
216
- onclick={() => {
217
- selectItem(item);
218
- }}
219
- class="item {isSelected(selectedItem, item) ? ' item-selected' : ''}"
220
- role="button"
221
- tabindex="0"
222
- data-pw={`${itemTestId}-${item}`}
305
+ class="select-option"
306
+ class:selected={value.includes(item.id)}
307
+ class:highlighted={index === highlightedIndex}
308
+ role="option"
309
+ id={`${listboxId}-option-${index}`}
310
+ aria-selected={value.includes(item.id)}
311
+ tabindex="-1"
312
+ onclick={() => selectItem(item.id)}
313
+ onmouseenter={() => (highlightedIndex = index)}
223
314
  >
224
- {#if selectMultipleItems}
225
- <CheckListItem checked={isSelected(selectedItem, item)} text="" />
226
- {/if}
227
- {item}
315
+ {item.label}
228
316
  </div>
229
317
  {/each}
230
- </div>
231
- {#if bottomContent}
232
- {@render bottomContent()}
233
- {/if}
234
- {#if selectMultipleItems}
235
- <div class="apply-btn-container">
236
- <Button {...applyButtonProps} onclick={dispatchEvent} />
237
- </div>
238
318
  {/if}
239
319
  </div>
240
- </div>
241
- {/if}
320
+ {/if}
321
+ </div>
242
322
 
243
323
  <style>
244
324
  .select {
245
- height: var(--select-height, fit-content);
246
- background-color: var(--select-bgcolor, #ffffff);
247
- font-size: var(--select-font-size, 14px);
248
- font-family: var(--select-font-family, Euclid Circular A);
249
- border-radius: var(--select-radius, 4px);
250
- font-weight: var(--select-font-weight, 400);
325
+ position: relative;
251
326
  width: var(--select-width, 100%);
252
- min-width: var(--select-min-width);
253
- box-shadow: var(--select-box-shadow, 0px 1px 8px #2f537733);
254
- -webkit-appearance: none !important; /* For Safari MWeb */
255
- outline: var(--select-outline, none);
256
- resize: none;
257
- cursor: pointer;
258
- border: var(--select-border, 1px solid #ccc);
259
- position: var(--select-position, relative);
260
- color: var(--select-color, #333);
261
- align-content: var(--select-align-content);
262
- display: var(--select-display, inline-block);
263
- --button-margin: var(--select-btn-margin, 1px);
264
- --button-border-radius: var(--select-btn-border-radius, 2px);
265
- --input-button-margin: var(--select-input-button-margin, 10px);
266
- --check-list-item-margin: var(--select-check-list-item-margin, 0px);
267
- --checkbox-margin: var(--select-checkbox-margin, 2px 8px 0px 0px);
268
- --checkbox-height: var(--select-checkbox-height, 14px);
269
- --checkbox-width: var(--select-checkbox-width, 14px);
270
- --checkbox-accent-color: var(--select-checkbox-accent-color, #3a4550);
271
- --check-list-item-checked-font-weight: var(--select-check-list-item-checked-font-weight, bold);
272
- --check-list-item-width: var(--select-check-list-item-width, fit-content);
327
+ font-family: var(--select-font-family, inherit);
328
+ font-size: var(--select-font-size, 14px);
329
+ color: var(--select-color, #333333);
273
330
  }
274
331
 
275
- .select:hover {
276
- color: var(--select-hover-color, #000);
277
- background-color: var(--select-hover-bgcolor, #ffffff);
332
+ .select.disabled {
333
+ opacity: var(--select-disabled-opacity, 0.5);
334
+ cursor: var(--select-disabled-cursor, not-allowed);
335
+ pointer-events: none;
278
336
  }
279
337
 
280
- .arrow {
281
- height: var(--dropdown-arrow-icon-height, 16px);
282
- width: var(--dropdown-arrow-icon-width, 16px);
283
- transition: transform 0.1s;
338
+ .select-trigger {
339
+ display: flex;
340
+ flex-wrap: wrap;
341
+ align-items: center;
342
+ gap: var(--select-trigger-gap, 4px);
343
+ min-height: var(--select-trigger-min-height, 40px);
344
+ padding: var(--select-trigger-padding, 8px 12px);
345
+ background: var(--select-trigger-background, #ffffff);
346
+ border: var(--select-trigger-border, 1px solid #cccccc);
347
+ border-radius: var(--select-trigger-border-radius, 6px);
348
+ cursor: pointer;
349
+ outline: none;
350
+ -webkit-tap-highlight-color: transparent;
351
+ transition: var(--select-trigger-transition, border-color 0.15s, box-shadow 0.15s);
284
352
  }
285
353
 
286
- .active {
287
- transform: rotate(0.5turn);
354
+ .select-trigger:hover:not(.disabled .select-trigger) {
355
+ border-color: var(--select-trigger-hover-border-color, #999999);
288
356
  }
289
357
 
290
- .item {
291
- padding: var(--item-padding, 8px 16px);
292
- background-color: var(--item-background-color, #fff);
293
- border-radius: var(--item-border-radius);
294
- align-items: var(--item-align-items);
295
- height: var(--item-height);
296
- cursor: pointer;
297
- position: relative;
298
- display: flex;
358
+ .select-trigger:focus-within,
359
+ .select.open .select-trigger {
360
+ border-color: var(--select-trigger-focus-border-color, #2563eb);
361
+ box-shadow: var(--select-trigger-focus-shadow, 0 0 0 2px rgba(37, 99, 235, 0.2));
299
362
  }
300
363
 
301
- .filler {
364
+ .select-value {
302
365
  flex: 1;
366
+ white-space: nowrap;
367
+ overflow: hidden;
368
+ text-overflow: ellipsis;
303
369
  }
304
370
 
305
- .item:hover {
306
- background-color: var(--non-selected-hover-bg, #f0f0f0);
307
- color: var(--non-selected-hover-color);
371
+ .select-placeholder {
372
+ flex: 1;
373
+ color: var(--select-placeholder-color, #999999);
374
+ white-space: nowrap;
375
+ overflow: hidden;
376
+ text-overflow: ellipsis;
308
377
  }
309
378
 
310
- .selected {
311
- display: flex;
312
- align-items: var(--selected-align-items, center);
313
- margin: var(--selected-margin, 0px 0px 0px 0px);
314
- justify-content: var(--selected-justify-content, flex-start);
315
- background-color: var(--selected-item-background-color, #f9f9f9);
316
- white-space: var(--selected-item-white-space, nowrap);
317
- overflow: var(--selected-item-overflow, hidden);
318
- text-overflow: var(--selected-item-text-overflow, ellipsis);
319
- max-width: var(--selected-item-max-width, 100%);
320
- padding: var(--selected-item-padding, var(--item-padding, 8px 16px));
379
+ .select-search {
380
+ flex: 1;
381
+ min-width: 60px;
382
+ border: none;
383
+ outline: none;
384
+ background: transparent;
385
+ font-family: inherit;
386
+ font-size: inherit;
387
+ color: inherit;
388
+ padding: 0;
389
+ cursor: text;
321
390
  }
322
391
 
323
- .selected-content {
324
- white-space: nowrap;
325
- overflow: hidden;
326
- text-overflow: ellipsis;
327
- max-width: calc(100% - 20px);
392
+ .select-search::placeholder {
393
+ color: var(--select-placeholder-color, #999999);
328
394
  }
329
395
 
330
- .selected:hover {
331
- background-color: var(--selected-hover-bg, transparent);
332
- color: var(--selected-color, black);
396
+ .select-arrow {
397
+ display: inline-flex;
398
+ align-items: center;
399
+ justify-content: center;
400
+ width: var(--select-arrow-size, 16px);
401
+ height: var(--select-arrow-size, 16px);
402
+ color: var(--select-arrow-color, #666666);
403
+ flex-shrink: 0;
404
+ transition: transform 0.15s;
333
405
  }
334
406
 
335
- .non-selected-items {
336
- display: var(--non-selected-display);
337
- background-color: var(--non-selected-item-bgcolor, #ffffff);
338
- color: var(--non-selected-item-color);
339
- box-shadow: 0px 1px 8px #2f537733;
340
- width: var(--non-selected-width, 100%);
341
- min-width: var(--non-selected-min-width, 100%);
342
- word-wrap: var(--non-selected-word-break, break-word);
343
- position: var(--non-selected-items-position, absolute);
344
- border-radius: var(--non-selected-items-border-radius, 4px);
345
- margin: var(--non-selected-margin, 4px 0px 0px 0px);
346
- font-weight: var(--non-select-font-weight, 500);
347
- left: var(--non-selected-left);
348
- right: var(--non-selected-right);
349
- top: var(--non-selected-top);
350
- bottom: var(--non-selected-bottom);
351
- z-index: 10;
352
- overflow-y: auto;
407
+ .select.open .select-arrow {
408
+ transform: rotate(180deg);
353
409
  }
354
410
 
355
- ::-webkit-scrollbar {
356
- width: var(--scrollbar-width, 0);
411
+ .select-arrow :global(svg) {
412
+ width: 100%;
413
+ height: 100%;
357
414
  }
358
415
 
359
- .item-list {
360
- max-height: var(--non-selected-max-height, 165px);
416
+ .select-dropdown {
417
+ position: absolute;
418
+ top: 100%;
419
+ left: 0;
420
+ right: 0;
421
+ margin-top: var(--select-dropdown-gap, 4px);
422
+ background: var(--select-dropdown-background, #ffffff);
423
+ border: var(--select-dropdown-border, 1px solid #cccccc);
424
+ border-radius: var(--select-dropdown-border-radius, 6px);
425
+ box-shadow: var(--select-dropdown-shadow, 0 4px 12px rgba(0, 0, 0, 0.1));
426
+ max-height: var(--select-dropdown-max-height, 200px);
361
427
  overflow-y: auto;
428
+ z-index: var(--select-dropdown-z-index, 10);
362
429
  }
363
430
 
364
- .item-selected::after {
365
- content: var(--selected-option-icon, '✔');
366
- position: absolute;
367
- right: 7px;
368
- color: var(--item-selected-icon-color);
431
+ .select-option {
432
+ padding: var(--select-option-padding, 8px 12px);
433
+ color: var(--select-option-color, #333333);
434
+ font-size: var(--select-option-font-size, inherit);
435
+ cursor: pointer;
436
+ transition: background 0.1s;
369
437
  }
370
438
 
371
- .label-container {
372
- font-weight: var(--label-text-weight, 400);
373
- font-size: var(--label-text-size, 12px);
374
- color: var(--label-text-color, #333);
375
- margin-bottom: var(--label-container-margin-bottom, 4px);
376
- display: var(--label-container-display, inline-block);
439
+ .select-option:hover,
440
+ .select-option.highlighted {
441
+ background: var(--select-option-hover-background, #f0f0f0);
442
+ color: var(--select-option-hover-color, var(--select-option-color, #333333));
377
443
  }
378
444
 
379
- .select-all-btn {
380
- display: flex;
381
- width: var(--select-all-btn-width, 99%);
382
- white-space: var(--select-all-btn-white-space, nowrap);
383
- padding: var(--select-all-btn-padding, 10px 16px);
384
- --button-font-size: var(--select-all-btn-font-size, 14px);
385
- --button-width: var(--select-all-btn-width, 100%);
386
- --button-color: var(--select-all-btn-color, #ffffff);
387
- --button-text-color: var(--select-all-btn-text-color, #333);
388
- --button-padding: var(--select-all-btn-inner-padding, 0px);
389
- --button-justify-content: var(--select-all-btn-justify-content, flex-start);
445
+ .select-option.selected {
446
+ background: var(--select-option-selected-background, #e8f0fe);
447
+ color: var(--select-option-selected-color, var(--select-option-color, #333333));
390
448
  }
391
449
 
392
- .apply-btn-container {
393
- padding: var(--apply-btn-container-padding, 5px);
394
- border-top: var(--apply-btn-container-border-top, 1px solid #ddd);
395
- background-color: var(--apply-btn-container-background-color, #f9f9f9);
396
- position: var(--apply-btn-container-position, sticky);
397
- width: var(--apply-btn-container-width, 94%);
398
- display: var(--apply-btn-display, flex);
399
- flex-direction: var(--apply-btn-flex-direction, column);
400
- --button-width: var(--apply-btn-width, 100%);
401
- --button-padding: var(--apply-btn-padding, 10px);
402
- --button-font-size: var(--apply-btn-font-size, 14px);
450
+ .select-option.selected.highlighted {
451
+ background: var(
452
+ --select-option-selected-hover-background,
453
+ var(--select-option-selected-background, #e8f0fe)
454
+ );
403
455
  }
404
456
 
405
- .icon-container {
406
- width: var(--select-icon-container-width, fit-content);
407
- height: var(--select-icon-container-height, fit-content);
408
- border-radius: var(--select-icon-container-border-radius);
409
- opacity: var(--select-icon-container-opacity);
410
- background: var(--select-icon-container-background);
411
- display: flex;
412
- align-items: center;
413
- justify-content: center;
414
- margin: var(--select-icon-container-margin, 0px 8px 0px 0px);
415
- padding: var(--select-icon-container-padding);
416
- --image-height: var(--select-icon-height);
417
- --image-width: var(--select-icon-height);
457
+ .select-empty {
458
+ padding: var(--select-empty-padding, 8px 12px);
459
+ color: var(--select-empty-color, #999999);
460
+ font-style: var(--select-empty-font-style, italic);
461
+ font-size: var(--select-empty-font-size, inherit);
418
462
  }
419
463
 
420
- .selected-item-count {
421
- margin: var(--selected-item-count-margin, 0px 6px);
422
- height: var(--selected-item-count-height, 18px);
423
- width: var(--selected-item-count-width, 18px);
424
- min-width: var(--selected-item-count-min-width, 18px);
425
- padding: var(--selected-item-count-padding, 4px);
426
- display: var(--selected-item-count-display, flex);
427
- justify-content: var(--selected-item-count-justify-content, center);
428
- align-items: var(--selected-item-count-align-item, center);
429
- background-color: var(--selected-item-count-bg-color, #3a4550);
430
- color: var(--selected-item-count-text-color, #ffffff);
431
- border-radius: var(--selected-item-count-border-radius, 4px);
464
+ .select-trigger :global(.pill) {
465
+ --pill-background: var(--select-pill-background, #e0e0e0);
466
+ --pill-color: var(--select-pill-color, #333333);
467
+ --pill-border-radius: var(--select-pill-border-radius, 999px);
468
+ --pill-padding: var(--select-pill-padding, 2px 8px);
469
+ --pill-font-size: var(--select-pill-font-size, 13px);
432
470
  }
433
471
  </style>