@juspay/svelte-ui-components 2.14.1 → 2.18.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.
Files changed (40) hide show
  1. package/dist/Button/Button.svelte +14 -0
  2. package/dist/Button/properties.d.ts +2 -0
  3. package/dist/Card/Card.svelte +51 -0
  4. package/dist/Card/Card.svelte.d.ts +4 -0
  5. package/dist/Card/properties.d.ts +10 -0
  6. package/dist/Card/properties.js +1 -0
  7. package/dist/ColorPicker/ColorPicker.svelte +583 -0
  8. package/dist/ColorPicker/ColorPicker.svelte.d.ts +4 -0
  9. package/dist/ColorPicker/properties.d.ts +15 -0
  10. package/dist/ColorPicker/properties.js +1 -0
  11. package/dist/Combobox/Combobox.svelte +432 -0
  12. package/dist/Combobox/Combobox.svelte.d.ts +6 -0
  13. package/dist/Combobox/properties.d.ts +42 -0
  14. package/dist/Combobox/properties.js +1 -0
  15. package/dist/EmptyState/EmptyState.svelte +66 -0
  16. package/dist/EmptyState/EmptyState.svelte.d.ts +4 -0
  17. package/dist/EmptyState/properties.d.ts +11 -0
  18. package/dist/EmptyState/properties.js +1 -0
  19. package/dist/Icon/Icon.svelte +22 -2
  20. package/dist/Icon/properties.d.ts +3 -4
  21. package/dist/Input/Input.svelte +30 -5
  22. package/dist/Input/Input.svelte.d.ts +1 -0
  23. package/dist/Input/properties.d.ts +11 -2
  24. package/dist/ListItem/ListItem.svelte +9 -3
  25. package/dist/ListItem/properties.d.ts +3 -0
  26. package/dist/Menu/Menu.svelte +17 -6
  27. package/dist/Menu/properties.d.ts +4 -0
  28. package/dist/Slider/Slider.svelte +9 -6
  29. package/dist/SplitInput/SplitInput.svelte +225 -0
  30. package/dist/SplitInput/SplitInput.svelte.d.ts +7 -0
  31. package/dist/SplitInput/properties.d.ts +20 -0
  32. package/dist/SplitInput/properties.js +1 -0
  33. package/dist/Toolbar/Toolbar.svelte +6 -2
  34. package/dist/assets/swap-vertical.svg +6 -0
  35. package/dist/index.d.ts +10 -0
  36. package/dist/index.js +5 -0
  37. package/dist/types.d.ts +15 -1
  38. package/dist/utils.d.ts +10 -1
  39. package/dist/utils.js +118 -0
  40. package/package.json +1 -1
@@ -0,0 +1,15 @@
1
+ export type ColorPickerProperties = MandatoryColorPickerProperties & OptionalColorPickerProperties & ColorPickerEventProperties;
2
+ export type MandatoryColorPickerProperties = {
3
+ value: string;
4
+ };
5
+ export type OptionalColorPickerProperties = {
6
+ label?: string;
7
+ disabled?: boolean;
8
+ showValue?: boolean;
9
+ testId?: string;
10
+ classes?: string;
11
+ };
12
+ export type ColorPickerEventProperties = {
13
+ onchange?: (value: string) => void;
14
+ oninput?: (value: string) => void;
15
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,432 @@
1
+ <script lang="ts">
2
+ import { onMount, tick } from 'svelte';
3
+ import Input from '../Input/Input.svelte';
4
+ import type { ComboboxItem, ComboboxProperties } from './properties';
5
+
6
+ function defaultFilter(item: ComboboxItem, query: string): boolean {
7
+ return item.label.toLowerCase().includes(query.toLowerCase());
8
+ }
9
+
10
+ let {
11
+ items,
12
+ value = $bindable(''),
13
+ inputValue = $bindable(''),
14
+ open = $bindable(false),
15
+ highlightedIndex = $bindable(-1),
16
+ placeholder = '',
17
+ disabled = false,
18
+ name,
19
+ testId,
20
+ classes,
21
+ noResultsText = 'No results',
22
+ ariaLabel,
23
+ filterFn = defaultFilter,
24
+ inputProperties,
25
+ inputEventProperties,
26
+ itemSnippet,
27
+ emptySnippet,
28
+ inputPrefix,
29
+ inputSuffix,
30
+ dropdownHeader,
31
+ dropdownFooter,
32
+ onselect,
33
+ oninput,
34
+ onopen,
35
+ onclose,
36
+ onkeydown,
37
+ onfocus,
38
+ onblur
39
+ }: ComboboxProperties = $props();
40
+
41
+ let containerEl: HTMLDivElement | null = $state(null);
42
+ let inputRef: ReturnType<typeof Input> | null = $state(null);
43
+
44
+ export function getInputRef(): HTMLInputElement | HTMLTextAreaElement | null {
45
+ return inputRef?.getInputRef() ?? null;
46
+ }
47
+
48
+ const listboxId = `combobox-listbox-${Math.random().toString(36).slice(2, 9)}`;
49
+
50
+ let filteredItems: ComboboxItem[] = $derived(
51
+ inputValue.length > 0 ? items.filter((item) => filterFn(item, inputValue)) : items
52
+ );
53
+
54
+ let selectableItems: ComboboxItem[] = $derived(
55
+ filteredItems.filter((item) => item.disabled !== true)
56
+ );
57
+
58
+ let highlightedOptionId: string | null = $derived(
59
+ highlightedIndex >= 0 ? `${listboxId}-option-${highlightedIndex}` : null
60
+ );
61
+
62
+ function openDropdown() {
63
+ if (disabled || open) {
64
+ return;
65
+ }
66
+ open = true;
67
+ highlightedIndex = -1;
68
+ onopen?.();
69
+ }
70
+
71
+ function closeDropdown() {
72
+ if (!open) {
73
+ return;
74
+ }
75
+ open = false;
76
+ highlightedIndex = -1;
77
+ onclose?.();
78
+ }
79
+
80
+ function selectItem(item: ComboboxItem) {
81
+ if (item.disabled === true) {
82
+ return;
83
+ }
84
+ value = item.id;
85
+ inputValue = item.label;
86
+ onselect?.(item);
87
+ closeDropdown();
88
+ }
89
+
90
+ function getFilteredSelectableIndex(item: ComboboxItem): number {
91
+ let selectableIdx = 0;
92
+ for (let i = 0; i < filteredItems.length; i++) {
93
+ if (filteredItems[i] === item) {
94
+ return filteredItems[i].disabled === true ? -1 : selectableIdx;
95
+ }
96
+ if (filteredItems[i].disabled !== true) {
97
+ selectableIdx++;
98
+ }
99
+ }
100
+ return -1;
101
+ }
102
+
103
+ async function moveHighlight(delta: number): Promise<void> {
104
+ if (selectableItems.length === 0) {
105
+ return;
106
+ }
107
+ let next = highlightedIndex + delta;
108
+ if (next < 0) {
109
+ next = selectableItems.length - 1;
110
+ } else if (next >= selectableItems.length) {
111
+ next = 0;
112
+ }
113
+ highlightedIndex = next;
114
+ await tick();
115
+ if (containerEl !== null) {
116
+ const el = containerEl.querySelector('.combobox-option.highlighted');
117
+ if (el instanceof HTMLElement && typeof el.scrollIntoView === 'function') {
118
+ el.scrollIntoView({ block: 'nearest' });
119
+ }
120
+ }
121
+ }
122
+
123
+ function selectHighlighted() {
124
+ if (highlightedIndex < 0 || highlightedIndex >= selectableItems.length) {
125
+ return;
126
+ }
127
+ const item = selectableItems.at(highlightedIndex);
128
+ if (typeof item === 'object' && item !== null) {
129
+ selectItem(item);
130
+ }
131
+ }
132
+
133
+ function handleInput(val: string, _event: Event) {
134
+ inputValue = val;
135
+ oninput?.(val);
136
+ inputEventProperties?.onInput?.(val, _event);
137
+ if (!open) {
138
+ openDropdown();
139
+ }
140
+ highlightedIndex = -1;
141
+ }
142
+
143
+ function handleKeydown(event: KeyboardEvent) {
144
+ if (disabled) {
145
+ return;
146
+ }
147
+ onkeydown?.(event);
148
+ if (event.defaultPrevented) {
149
+ return;
150
+ }
151
+ switch (event.key) {
152
+ case 'ArrowDown':
153
+ event.preventDefault();
154
+ if (!open) {
155
+ openDropdown();
156
+ } else {
157
+ moveHighlight(1);
158
+ }
159
+ break;
160
+ case 'ArrowUp':
161
+ event.preventDefault();
162
+ if (open) {
163
+ moveHighlight(-1);
164
+ }
165
+ break;
166
+ case 'Enter':
167
+ if (open && highlightedIndex >= 0) {
168
+ event.preventDefault();
169
+ selectHighlighted();
170
+ }
171
+ break;
172
+ case 'Escape':
173
+ if (open) {
174
+ event.preventDefault();
175
+ closeDropdown();
176
+ }
177
+ break;
178
+ case 'Tab':
179
+ if (open) {
180
+ closeDropdown();
181
+ }
182
+ break;
183
+ }
184
+ }
185
+
186
+ function handleFocus(event: FocusEvent) {
187
+ openDropdown();
188
+ onfocus?.(event);
189
+ inputEventProperties?.onFocus?.(event);
190
+ }
191
+
192
+ function handleBlur(event: FocusEvent) {
193
+ onblur?.(event);
194
+ inputEventProperties?.onBlur?.(event);
195
+ }
196
+
197
+ function handleClickOutside(event: Event) {
198
+ if (
199
+ event.target instanceof Node &&
200
+ containerEl !== null &&
201
+ !containerEl.contains(event.target)
202
+ ) {
203
+ closeDropdown();
204
+ }
205
+ }
206
+
207
+ onMount(() => {
208
+ document.addEventListener('click', handleClickOutside);
209
+ return () => {
210
+ document.removeEventListener('click', handleClickOutside);
211
+ };
212
+ });
213
+ </script>
214
+
215
+ <div class="combobox {classes ?? ''}" class:disabled bind:this={containerEl} data-pw={testId}>
216
+ <div class="combobox-input-wrapper">
217
+ {#if typeof inputPrefix === 'function'}
218
+ <div class="combobox-input-prefix">{@render inputPrefix()}</div>
219
+ {/if}
220
+ <div class="combobox-input">
221
+ <Input
222
+ {...inputProperties}
223
+ bind:value={inputValue}
224
+ bind:this={inputRef}
225
+ {placeholder}
226
+ {name}
227
+ disable={disabled}
228
+ autoComplete="off"
229
+ actionInput={true}
230
+ testId={typeof testId === 'string' ? `${testId}-input` : ''}
231
+ role="combobox"
232
+ ariaExpanded={open}
233
+ ariaAutocomplete="list"
234
+ ariaControls={open ? listboxId : null}
235
+ ariaActivedescendant={highlightedOptionId ?? null}
236
+ onInput={handleInput}
237
+ onKeyDown={handleKeydown}
238
+ onFocus={handleFocus}
239
+ onBlur={handleBlur}
240
+ />
241
+ </div>
242
+ {#if typeof inputSuffix === 'function'}
243
+ <div class="combobox-input-suffix">{@render inputSuffix()}</div>
244
+ {/if}
245
+ </div>
246
+
247
+ {#if open && !disabled}
248
+ <div class="combobox-dropdown" role="listbox" id={listboxId} aria-label={ariaLabel}>
249
+ {#if typeof dropdownHeader === 'function'}
250
+ <div class="combobox-dropdown-header">{@render dropdownHeader()}</div>
251
+ {/if}
252
+ {#if filteredItems.length === 0}
253
+ {#if typeof emptySnippet === 'function'}
254
+ {@render emptySnippet()}
255
+ {:else}
256
+ <div class="combobox-empty">{noResultsText}</div>
257
+ {/if}
258
+ {:else}
259
+ {#each filteredItems as item, _index (item.id)}
260
+ {@const selectableIndex = getFilteredSelectableIndex(item)}
261
+ {@const isHighlighted = item.disabled !== true && selectableIndex === highlightedIndex}
262
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
263
+ <div
264
+ class="combobox-option"
265
+ class:highlighted={isHighlighted}
266
+ class:selected={item.id === value}
267
+ class:combobox-option-disabled={item.disabled === true}
268
+ role="option"
269
+ id={`${listboxId}-option-${selectableIndex}`}
270
+ aria-selected={item.id === value}
271
+ aria-disabled={item.disabled === true ? 'true' : null}
272
+ tabindex="-1"
273
+ onclick={() => selectItem(item)}
274
+ onmouseenter={() => {
275
+ if (item.disabled !== true) {
276
+ highlightedIndex = selectableIndex;
277
+ }
278
+ }}
279
+ data-pw={typeof testId === 'string' ? `${testId}-option-${item.id}` : null}
280
+ >
281
+ {#if typeof itemSnippet === 'function'}
282
+ {@render itemSnippet(item, isHighlighted)}
283
+ {:else}
284
+ {item.label}
285
+ {/if}
286
+ </div>
287
+ {/each}
288
+ {/if}
289
+ {#if typeof dropdownFooter === 'function'}
290
+ <div class="combobox-dropdown-footer">{@render dropdownFooter()}</div>
291
+ {/if}
292
+ </div>
293
+ {/if}
294
+ </div>
295
+
296
+ <style>
297
+ .combobox {
298
+ position: relative;
299
+ width: var(--combobox-width, 100%);
300
+ font-family: var(--combobox-font-family, inherit);
301
+ font-size: var(--combobox-font-size, 14px);
302
+ color: var(--combobox-color, #333333);
303
+ }
304
+
305
+ .combobox.disabled {
306
+ opacity: var(--combobox-disabled-opacity, 0.5);
307
+ cursor: var(--combobox-disabled-cursor, not-allowed);
308
+ pointer-events: none;
309
+ }
310
+
311
+ .combobox-input-wrapper {
312
+ display: flex;
313
+ align-items: center;
314
+ background: var(--combobox-input-background, #ffffff);
315
+ border: var(--combobox-input-border, 1px solid #cccccc);
316
+ border-radius: var(--combobox-input-border-radius, 6px);
317
+ transition: var(--combobox-input-transition, border-color 0.15s, box-shadow 0.15s);
318
+ }
319
+
320
+ .combobox-input-wrapper:hover {
321
+ border-color: var(--combobox-input-hover-border-color, #999999);
322
+ }
323
+
324
+ .combobox-input-wrapper:focus-within {
325
+ border-color: var(--combobox-input-focus-border-color, #2563eb);
326
+ box-shadow: var(--combobox-input-focus-shadow, 0 0 0 2px rgba(37, 99, 235, 0.2));
327
+ }
328
+
329
+ .combobox-input-prefix {
330
+ display: flex;
331
+ align-items: center;
332
+ padding-left: var(--combobox-input-prefix-padding, 8px);
333
+ flex-shrink: 0;
334
+ }
335
+
336
+ .combobox-input-suffix {
337
+ display: flex;
338
+ align-items: center;
339
+ padding-right: var(--combobox-input-suffix-padding, 8px);
340
+ flex-shrink: 0;
341
+ }
342
+
343
+ .combobox-input {
344
+ flex: 1;
345
+ min-width: 0;
346
+ --input-border: none;
347
+ --input-focus-border: none;
348
+ --input-box-shadow: none;
349
+ --input-margin: 0;
350
+ --input-width: 100%;
351
+ --input-padding: var(--combobox-input-padding, 8px 12px);
352
+ --input-background: transparent;
353
+ --input-font-size: inherit;
354
+ --input-font-family: inherit;
355
+ --input-font-weight: inherit;
356
+ --input-text-color: inherit;
357
+ --input-radius: 0;
358
+ }
359
+
360
+ .combobox-input::placeholder {
361
+ color: var(--combobox-placeholder-color, #999999);
362
+ }
363
+
364
+ .combobox-dropdown {
365
+ position: absolute;
366
+ top: var(--combobox-dropdown-top, 100%);
367
+ left: var(--combobox-dropdown-left, 0);
368
+ right: var(--combobox-dropdown-right, 0);
369
+ margin-top: var(--combobox-dropdown-gap, 4px);
370
+ background: var(--combobox-dropdown-background, #ffffff);
371
+ border: var(--combobox-dropdown-border, 1px solid #cccccc);
372
+ border-radius: var(--combobox-dropdown-border-radius, 6px);
373
+ box-shadow: var(--combobox-dropdown-shadow, 0 4px 12px rgba(0, 0, 0, 0.1));
374
+ max-height: var(--combobox-dropdown-max-height, 200px);
375
+ overflow-y: auto;
376
+ z-index: var(--combobox-dropdown-z-index, 10);
377
+ padding: var(--combobox-dropdown-padding, 0);
378
+ }
379
+
380
+ .combobox-option {
381
+ padding: var(--combobox-option-padding, 8px 12px);
382
+ color: var(--combobox-option-color, #333333);
383
+ font-size: var(--combobox-option-font-size, inherit);
384
+ font-weight: var(--combobox-option-font-weight, inherit);
385
+ cursor: pointer;
386
+ transition: background 0.1s;
387
+ }
388
+
389
+ .combobox-option:hover,
390
+ .combobox-option.highlighted {
391
+ background: var(--combobox-option-hover-background, #f0f0f0);
392
+ color: var(--combobox-option-hover-color, var(--combobox-option-color, #333333));
393
+ }
394
+
395
+ .combobox-option.selected {
396
+ background: var(--combobox-option-selected-background, #e8f0fe);
397
+ color: var(--combobox-option-selected-color, var(--combobox-option-color, #333333));
398
+ font-weight: var(
399
+ --combobox-option-selected-font-weight,
400
+ var(--combobox-option-font-weight, inherit)
401
+ );
402
+ }
403
+
404
+ .combobox-option.selected.highlighted {
405
+ background: var(
406
+ --combobox-option-selected-hover-background,
407
+ var(--combobox-option-selected-background, #e8f0fe)
408
+ );
409
+ }
410
+
411
+ .combobox-option-disabled {
412
+ opacity: var(--combobox-option-disabled-opacity, 0.4);
413
+ cursor: var(--combobox-option-disabled-cursor, not-allowed);
414
+ pointer-events: none;
415
+ }
416
+
417
+ .combobox-dropdown-header {
418
+ border-bottom: var(--combobox-dropdown-header-border, none);
419
+ padding: var(--combobox-dropdown-header-padding, 0);
420
+ }
421
+
422
+ .combobox-dropdown-footer {
423
+ border-top: var(--combobox-dropdown-footer-border, none);
424
+ padding: var(--combobox-dropdown-footer-padding, 0);
425
+ }
426
+
427
+ .combobox-empty {
428
+ padding: var(--combobox-empty-padding, 8px 12px);
429
+ color: var(--combobox-empty-color, #999999);
430
+ font-style: var(--combobox-empty-font-style, italic);
431
+ }
432
+ </style>
@@ -0,0 +1,6 @@
1
+ import type { ComboboxProperties } from './properties';
2
+ declare const Combobox: import("svelte").Component<ComboboxProperties, {
3
+ getInputRef: () => HTMLInputElement | HTMLTextAreaElement | null;
4
+ }, "value" | "open" | "inputValue" | "highlightedIndex">;
5
+ type Combobox = ReturnType<typeof Combobox>;
6
+ export default Combobox;
@@ -0,0 +1,42 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { OptionalInputProperties, InputEventProperties } from '../Input/properties';
3
+ export type ComboboxItem = {
4
+ id: string;
5
+ label: string;
6
+ disabled?: boolean;
7
+ };
8
+ export type ComboboxProperties = MandatoryComboboxProperties & OptionalComboboxProperties & ComboboxEventProperties;
9
+ export type MandatoryComboboxProperties = {
10
+ items: ComboboxItem[];
11
+ };
12
+ export type OptionalComboboxProperties = {
13
+ value?: string;
14
+ inputValue?: string;
15
+ open?: boolean;
16
+ highlightedIndex?: number;
17
+ placeholder?: string;
18
+ disabled?: boolean;
19
+ name?: string;
20
+ testId?: string;
21
+ classes?: string;
22
+ noResultsText?: string;
23
+ ariaLabel?: string;
24
+ filterFn?: (item: ComboboxItem, query: string) => boolean;
25
+ inputProperties?: OptionalInputProperties;
26
+ inputEventProperties?: InputEventProperties;
27
+ itemSnippet?: Snippet<[ComboboxItem, boolean]>;
28
+ emptySnippet?: Snippet;
29
+ inputPrefix?: Snippet;
30
+ inputSuffix?: Snippet;
31
+ dropdownHeader?: Snippet;
32
+ dropdownFooter?: Snippet;
33
+ };
34
+ export type ComboboxEventProperties = {
35
+ onselect?: (item: ComboboxItem) => void;
36
+ oninput?: (value: string) => void;
37
+ onopen?: () => void;
38
+ onclose?: () => void;
39
+ onkeydown?: (event: KeyboardEvent) => void;
40
+ onfocus?: (event: FocusEvent) => void;
41
+ onblur?: (event: FocusEvent) => void;
42
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,66 @@
1
+ <script lang="ts">
2
+ import type { EmptyStateProperties } from './properties';
3
+
4
+ let { title, description, icon, children, classes }: EmptyStateProperties = $props();
5
+ </script>
6
+
7
+ <div class="empty-state {classes ?? ''}">
8
+ {#if typeof icon === 'function'}
9
+ <div class="empty-state-icon">
10
+ {@render icon()}
11
+ </div>
12
+ {/if}
13
+ <div class="empty-state-title">{title}</div>
14
+ <div class="empty-state-description">{description}</div>
15
+ {#if typeof children === 'function'}
16
+ <div class="empty-state-actions">
17
+ {@render children()}
18
+ </div>
19
+ {/if}
20
+ </div>
21
+
22
+ <style>
23
+ .empty-state {
24
+ display: flex;
25
+ flex-direction: column;
26
+ align-items: center;
27
+ padding: var(--empty-state-padding, 32px 16px);
28
+ text-align: var(--empty-state-text-align, center);
29
+ gap: var(--empty-state-gap, 0px);
30
+ color: inherit;
31
+ }
32
+
33
+ .empty-state-icon {
34
+ width: var(--empty-state-icon-size, 48px);
35
+ height: var(--empty-state-icon-size, 48px);
36
+ color: var(--empty-state-icon-color, currentColor);
37
+ opacity: var(--empty-state-icon-opacity, 0.4);
38
+ margin-bottom: var(--empty-state-icon-margin-bottom, 16px);
39
+ display: flex;
40
+ align-items: center;
41
+ justify-content: center;
42
+ }
43
+
44
+ .empty-state-icon :global(svg) {
45
+ width: 100%;
46
+ height: 100%;
47
+ }
48
+
49
+ .empty-state-title {
50
+ font-size: var(--empty-state-title-font-size, 16px);
51
+ font-weight: var(--empty-state-title-font-weight, 600);
52
+ color: var(--empty-state-title-color, inherit);
53
+ }
54
+
55
+ .empty-state-description {
56
+ font-size: var(--empty-state-description-font-size, 14px);
57
+ color: var(--empty-state-description-color, inherit);
58
+ opacity: var(--empty-state-description-opacity, 0.6);
59
+ max-width: var(--empty-state-description-max-width, 360px);
60
+ margin-top: 4px;
61
+ }
62
+
63
+ .empty-state-actions {
64
+ margin-top: 16px;
65
+ }
66
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { EmptyStateProperties } from './properties';
2
+ declare const EmptyState: import("svelte").Component<EmptyStateProperties, {}, "">;
3
+ type EmptyState = ReturnType<typeof EmptyState>;
4
+ export default EmptyState;
@@ -0,0 +1,11 @@
1
+ import type { Snippet } from 'svelte';
2
+ export type EmptyStateProperties = MandatoryEmptyStateProperties & OptionalEmptyStateProperties;
3
+ export type MandatoryEmptyStateProperties = {
4
+ title: string;
5
+ description: string;
6
+ };
7
+ export type OptionalEmptyStateProperties = {
8
+ icon?: Snippet;
9
+ children?: Snippet;
10
+ classes?: string;
11
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,11 +1,16 @@
1
1
  <script lang="ts">
2
2
  import type { IconProperties } from './properties';
3
3
 
4
- let { icon, text, onclick, onkeydown, classes }: IconProperties = $props();
4
+ let { icon, svg, text, onclick, onkeydown, classes }: IconProperties = $props();
5
5
  </script>
6
6
 
7
7
  <div class="icon-container {classes ?? ''}" {onclick} {onkeydown} role="button" tabindex="0">
8
- <img src={icon} alt="" />
8
+ {#if typeof svg === 'string' && svg.length > 0}
9
+ <!-- eslint-disable svelte/no-at-html-tags -->
10
+ <span class="icon-svg">{@html svg}</span>
11
+ {:else if icon}
12
+ <img src={icon} alt="" />
13
+ {/if}
9
14
  {#if typeof text === 'string' && text.length > 0}
10
15
  <div class="icon-text">{text}</div>
11
16
  {/if}
@@ -25,6 +30,21 @@
25
30
  padding: var(--icon-padding, 4px);
26
31
  }
27
32
 
33
+ .icon-svg {
34
+ display: inline-flex;
35
+ align-items: center;
36
+ justify-content: center;
37
+ width: var(--icon-width, 20px);
38
+ height: var(--icon-height, 20px);
39
+ padding: var(--icon-padding, 4px);
40
+ color: var(--icon-svg-color, currentColor);
41
+ }
42
+
43
+ .icon-svg :global(svg) {
44
+ width: 100%;
45
+ height: 100%;
46
+ }
47
+
28
48
  .icon-text {
29
49
  display: flex;
30
50
  padding: var(--icon-text-padding, 4px);
@@ -1,8 +1,7 @@
1
- export type IconProperties = OptionalIconProperties & IconEventProperties & MandatoryIconProperties;
2
- export type MandatoryIconProperties = {
3
- icon: string;
4
- };
1
+ export type IconProperties = OptionalIconProperties & IconEventProperties;
5
2
  export type OptionalIconProperties = {
3
+ icon?: string;
4
+ svg?: string;
6
5
  text?: string | null;
7
6
  classes?: string;
8
7
  };