@ibobbyts/svelte-ui-utils 0.3.1 → 0.3.5

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.
package/README.md CHANGED
@@ -241,6 +241,8 @@ controls need caller-owned accessible labels. Each label is applied to both
241
241
  `SortableTableEnhanced` composes the same table and adds a controlled current
242
242
  radio immediately after each drag handle. `currentId` is read-only input;
243
243
  `onCurrentChange` asks the caller to persist a new current item. Use
244
+ `showCurrentControl={false}` to omit the radios while retaining the table's
245
+ drag, remove, reorder, and row-color behavior. Use
244
246
  `getCurrentDisabled` for per-row eligibility and `getRowColorPreset` to select
245
247
  the package-owned `red`, `yellow`, or `green` background. The presets include
246
248
  light and dark defaults and expose `--suu-sortable-table-row-*-light` and
@@ -348,7 +350,12 @@ prop is available on all dialog wrapper components.
348
350
 
349
351
  ```svelte
350
352
  <script lang="ts">
351
- import { Dropdown, type DropdownOption, type DropdownValue } from '@ibobbyts/svelte-ui-utils/dropdown';
353
+ import {
354
+ Dropdown,
355
+ DropdownMultiSelect,
356
+ type DropdownOption,
357
+ type DropdownValue
358
+ } from '@ibobbyts/svelte-ui-utils/dropdown';
352
359
 
353
360
  const pageSizeOptions: DropdownOption[] = [
354
361
  { label: '10', value: 10 },
@@ -377,6 +384,36 @@ use `menuAlign="right"` to align their right edges instead. `fitContent` sizes
377
384
  the menu to its longest option while keeping the selected edge aligned.
378
385
  `DataTable` uses this same component for its page-size picker.
379
386
 
387
+ Set `multiselect={true}` to control the component with a `DropdownValue[]`.
388
+ Each option then shows a checkbox, selecting or deselecting an option keeps the
389
+ menu open, and `onChange` receives the complete selected-value array in option
390
+ display order. `DropdownMultiSelect` is the convenience wrapper for this mode:
391
+
392
+ ```svelte
393
+ <script lang="ts">
394
+ import { DropdownMultiSelect, type DropdownMultiValue } from '@ibobbyts/svelte-ui-utils/dropdown';
395
+
396
+ let selectedRoles: DropdownMultiValue = ['editor'];
397
+ </script>
398
+
399
+ <DropdownMultiSelect
400
+ value={selectedRoles}
401
+ options={[
402
+ { label: 'Editor', value: 'editor' },
403
+ { label: 'Reviewer', value: 'reviewer' }
404
+ ]}
405
+ ariaLabel="Roles"
406
+ maxWidth="18rem"
407
+ onChange={(next) => {
408
+ selectedRoles = next;
409
+ }}
410
+ />
411
+ ```
412
+
413
+ The collapsed trigger joins selected labels with `", "` and truncates the
414
+ single line when it exceeds the existing `width` or `maxWidth` constraint.
415
+ Grouped and disabled options retain the same behavior as single-select mode.
416
+
380
417
  The dropdown panel is constrained by default to the space available above or
381
418
  below the trigger and remains scrollable when its contents exceed that height.
382
419
  Set `fitViewport={false}` to fall back to the stylesheet's viewport-height
@@ -5,15 +5,20 @@
5
5
  import type {
6
6
  DropdownChangeHandler,
7
7
  DropdownMenuAlign,
8
+ DropdownMultiChangeHandler,
9
+ DropdownMultiValue,
8
10
  DropdownOption,
9
11
  DropdownOptionGroup,
10
12
  DropdownPlacement,
13
+ DropdownSelectionChangeHandler,
14
+ DropdownSelection,
11
15
  DropdownTriggerClickHandler,
12
16
  DropdownValue
13
17
  } from './types.js';
14
18
 
15
19
  export let id: string | undefined = undefined;
16
- export let value: DropdownValue = '';
20
+ export let value: DropdownSelection = '';
21
+ export let multiselect = false;
17
22
  export let options: DropdownOption[] = [];
18
23
  export let optionGroups: DropdownOptionGroup[] | undefined = undefined;
19
24
  export let ariaLabel: string | undefined = undefined;
@@ -26,33 +31,50 @@
26
31
  export let minWidth: string | undefined = undefined;
27
32
  export let maxWidth: string | undefined = undefined;
28
33
  export let className: string | undefined = undefined;
29
- export let onChange: DropdownChangeHandler | undefined = undefined;
34
+ export let portal = false;
35
+ export let onChange: DropdownSelectionChangeHandler | undefined = undefined;
30
36
  export let onTriggerClick: DropdownTriggerClickHandler | undefined = undefined;
31
37
 
32
38
  const viewportMargin = 20;
33
39
  const menuGap = 6;
34
40
 
35
41
  let open = false;
36
- let activeValue: DropdownValue = value;
42
+ let activeValue: DropdownValue = '';
37
43
  let dropdownElement: HTMLSpanElement | undefined;
38
44
  let buttonElement: HTMLButtonElement | undefined;
45
+ let menuElement: HTMLDivElement | undefined;
39
46
  let resolvedPlacement: Exclude<DropdownPlacement, 'auto'> = placement === 'up' ? 'up' : 'down';
40
47
  let viewportPanelMaxHeight: string | undefined = undefined;
48
+ let portalMenuTop = '-9999px';
49
+ let portalMenuLeft: string | undefined = undefined;
50
+ let portalMenuRight: string | undefined = undefined;
51
+ let portalMenuWidth: string | undefined = undefined;
41
52
  let removeOpenViewportListeners: (() => void) | undefined = undefined;
53
+ let removeOutsidePointerListener: (() => void) | undefined = undefined;
42
54
 
43
55
  $: resolvedOptions = optionGroups === undefined ? options : optionGroups.flatMap((group) => group.options);
44
- $: selectedOption = resolvedOptions.find((option) => option.value === value);
45
- $: selectedText = selectedOption?.label ?? String(value);
56
+ $: selectedValues = normalizeSelectedValues(multiselect && Array.isArray(value) ? value : []);
57
+ $: selectedOption = Array.isArray(value) ? undefined : resolvedOptions.find((option) => option.value === value);
58
+ $: selectedText = multiselect
59
+ ? resolvedOptions
60
+ .filter((option) => selectedValues.includes(option.value))
61
+ .map((option) => option.label)
62
+ .join(', ')
63
+ : selectedOption?.label ?? String(value);
46
64
  $: if (!open) {
47
- activeValue = selectedOption?.value ?? firstEnabledOption()?.value ?? value;
65
+ activeValue = initialActiveValue();
48
66
  }
49
67
  $: {
50
68
  placement;
51
69
  if (open) {
52
70
  enableOpenViewportTracking();
71
+ if (multiselect) {
72
+ enableOutsidePointerDismissal();
73
+ }
53
74
  void updateViewportPanelMaxHeight();
54
75
  } else {
55
76
  disableOpenViewportTracking();
77
+ disableOutsidePointerDismissal();
56
78
  resolvedPlacement = placement === 'up' ? 'up' : 'down';
57
79
  viewportPanelMaxHeight = undefined;
58
80
  }
@@ -62,12 +84,36 @@
62
84
  return resolvedOptions.find((option) => !option.disabled);
63
85
  }
64
86
 
87
+ function normalizeSelectedValues(nextValues: DropdownMultiValue): DropdownMultiValue {
88
+ const incoming = new Set(nextValues);
89
+ const normalized: DropdownMultiValue = [];
90
+ for (const option of resolvedOptions) {
91
+ if (incoming.has(option.value) && !normalized.includes(option.value)) {
92
+ normalized.push(option.value);
93
+ }
94
+ }
95
+ return normalized;
96
+ }
97
+
98
+ function initialActiveValue(): DropdownValue {
99
+ const firstSelectedEnabled = resolvedOptions.find(
100
+ (option) => selectedValues.includes(option.value) && !option.disabled
101
+ );
102
+ return firstSelectedEnabled?.value ?? selectedOption?.value ?? firstEnabledOption()?.value ?? '';
103
+ }
104
+
105
+ function isOptionSelected(option: DropdownOption): boolean {
106
+ return multiselect ? selectedValues.includes(option.value) : option.value === value;
107
+ }
108
+
65
109
  function activeOptionIndex(nextActiveValue: DropdownValue): number {
66
110
  const index = resolvedOptions.findIndex((option) => option.value === nextActiveValue && !option.disabled);
67
111
  if (index >= 0) {
68
112
  return index;
69
113
  }
70
- const fallbackIndex = resolvedOptions.findIndex((option) => option.value === value && !option.disabled);
114
+ const fallbackIndex = resolvedOptions.findIndex(
115
+ (option) => isOptionSelected(option) && !option.disabled
116
+ );
71
117
  if (fallbackIndex >= 0) {
72
118
  return fallbackIndex;
73
119
  }
@@ -89,9 +135,21 @@
89
135
  if (option.disabled) {
90
136
  return;
91
137
  }
92
- open = false;
93
138
  activeValue = option.value;
94
- void onChange?.(option.value);
139
+ if (multiselect) {
140
+ const nextSelected = new Set(selectedValues);
141
+ if (nextSelected.has(option.value)) {
142
+ nextSelected.delete(option.value);
143
+ } else {
144
+ nextSelected.add(option.value);
145
+ }
146
+ const nextValues = normalizeSelectedValues([...nextSelected]);
147
+ void (onChange as DropdownMultiChangeHandler | undefined)?.(nextValues);
148
+ return;
149
+ }
150
+
151
+ open = false;
152
+ void (onChange as DropdownChangeHandler | undefined)?.(option.value);
95
153
  buttonElement?.focus();
96
154
  }
97
155
 
@@ -103,7 +161,7 @@
103
161
  updateResolvedPlacement();
104
162
  }
105
163
  open = !open;
106
- activeValue = selectedOption?.value ?? firstEnabledOption()?.value ?? value;
164
+ activeValue = initialActiveValue();
107
165
  }
108
166
 
109
167
  function handleTriggerClick(event: MouseEvent) {
@@ -129,7 +187,7 @@
129
187
  if (!open) {
130
188
  updateResolvedPlacement();
131
189
  open = true;
132
- activeValue = selectedOption?.value ?? firstEnabledOption()?.value ?? value;
190
+ activeValue = initialActiveValue();
133
191
  }
134
192
  moveActiveOption(event.key === 'ArrowDown' ? 1 : -1);
135
193
  return;
@@ -147,6 +205,9 @@
147
205
 
148
206
  function handleFocusout(event: FocusEvent) {
149
207
  const nextTarget = event.relatedTarget;
208
+ if (portal && nextTarget instanceof Node && menuElement?.contains(nextTarget)) {
209
+ return;
210
+ }
150
211
  if (nextTarget instanceof Node && event.currentTarget instanceof Node && event.currentTarget.contains(nextTarget)) {
151
212
  return;
152
213
  }
@@ -204,6 +265,7 @@
204
265
 
205
266
  updateResolvedPlacement(rect);
206
267
  if (!fitViewport) {
268
+ updatePortalPosition(rect);
207
269
  return;
208
270
  }
209
271
 
@@ -212,6 +274,39 @@
212
274
  ? rect.top - viewportMargin - menuGap
213
275
  : window.innerHeight - rect.bottom - viewportMargin - menuGap;
214
276
  viewportPanelMaxHeight = `${Math.max(0, Math.floor(available))}px`;
277
+ updatePortalPosition(rect);
278
+ }
279
+
280
+ function updatePortalPosition(rect: DOMRect) {
281
+ if (!portal || typeof window === 'undefined') {
282
+ return;
283
+ }
284
+
285
+ const menuHeight = menuElement?.getBoundingClientRect().height ?? 0;
286
+ portalMenuTop = `${Math.round(
287
+ resolvedPlacement === 'up' ? rect.top - menuHeight - menuGap : rect.bottom + menuGap
288
+ )}px`;
289
+ if (menuAlign === 'right') {
290
+ portalMenuLeft = undefined;
291
+ portalMenuRight = `${Math.round(window.innerWidth - rect.right)}px`;
292
+ } else {
293
+ portalMenuLeft = `${Math.round(rect.left)}px`;
294
+ portalMenuRight = undefined;
295
+ }
296
+ portalMenuWidth = fitContent ? undefined : `${Math.round(rect.width)}px`;
297
+ }
298
+
299
+ function portalMenu(node: HTMLDivElement, enabled: boolean) {
300
+ if (enabled && typeof document !== 'undefined') {
301
+ document.body.appendChild(node);
302
+ }
303
+ return {
304
+ destroy() {
305
+ if (node.parentNode) {
306
+ node.parentNode.removeChild(node);
307
+ }
308
+ }
309
+ };
215
310
  }
216
311
 
217
312
  function enableOpenViewportTracking() {
@@ -236,13 +331,44 @@
236
331
  removeOpenViewportListeners?.();
237
332
  }
238
333
 
239
- onDestroy(disableOpenViewportTracking);
334
+ function enableOutsidePointerDismissal() {
335
+ if (removeOutsidePointerListener || typeof document === 'undefined') {
336
+ return;
337
+ }
338
+
339
+ const handleOutsidePointer = (event: PointerEvent) => {
340
+ const target = event.target;
341
+ if (!(target instanceof Node)) {
342
+ return;
343
+ }
344
+ if (dropdownElement?.contains(target) || menuElement?.contains(target)) {
345
+ return;
346
+ }
347
+ open = false;
348
+ };
349
+
350
+ document.addEventListener('pointerdown', handleOutsidePointer, true);
351
+ removeOutsidePointerListener = () => {
352
+ document.removeEventListener('pointerdown', handleOutsidePointer, true);
353
+ removeOutsidePointerListener = undefined;
354
+ };
355
+ }
356
+
357
+ function disableOutsidePointerDismissal() {
358
+ removeOutsidePointerListener?.();
359
+ }
360
+
361
+ onDestroy(() => {
362
+ disableOpenViewportTracking();
363
+ disableOutsidePointerDismissal();
364
+ });
240
365
  </script>
241
366
 
242
367
  <span
243
368
  bind:this={dropdownElement}
244
369
  class={[
245
370
  'suu-dropdown',
371
+ multiselect ? 'suu-dropdown--multiselect' : '',
246
372
  width !== undefined || minWidth !== undefined || maxWidth !== undefined
247
373
  ? 'suu-dropdown--sized'
248
374
  : '',
@@ -263,24 +389,36 @@
263
389
  aria-label={ariaLabel}
264
390
  aria-haspopup="listbox"
265
391
  aria-expanded={open}
266
- data-value={String(value)}
392
+ data-value={Array.isArray(value) ? value.map(String).join(',') : String(value)}
267
393
  on:click={handleTriggerClick}
268
394
  on:keydown={handleKeydown}
269
395
  >
270
- <span>{selectedText}</span>
396
+ <span class="suu-dropdown__label">{selectedText}</span>
271
397
  <span class="suu-dropdown__chevron" aria-hidden="true"></span>
272
398
  </button>
273
399
 
274
400
  {#if open}
275
401
  <div
402
+ bind:this={menuElement}
403
+ use:portalMenu={portal}
276
404
  class="suu-dropdown__menu"
405
+ class:suu-dropdown__menu--portal={portal}
277
406
  class:suu-dropdown__menu--up={resolvedPlacement === 'up'}
278
407
  class:suu-dropdown__menu--down={resolvedPlacement === 'down'}
279
408
  class:suu-dropdown__menu--left={menuAlign === 'left'}
280
409
  class:suu-dropdown__menu--right={menuAlign === 'right'}
281
410
  class:suu-dropdown__menu--fit-content={fitContent}
411
+ style:--suu-dropdown-menu-top={portalMenuTop}
412
+ style:--suu-dropdown-menu-left={portalMenuLeft}
413
+ style:--suu-dropdown-menu-right={portalMenuRight}
414
+ style:--suu-dropdown-menu-width={portalMenuWidth}
282
415
  >
283
- <div class="suu-dropdown__panel" role="listbox" aria-label={ariaLabel}>
416
+ <div
417
+ class="suu-dropdown__panel"
418
+ role="listbox"
419
+ aria-label={ariaLabel}
420
+ aria-multiselectable={multiselect || undefined}
421
+ >
284
422
  {#if optionGroups === undefined}
285
423
  {#each options as option}
286
424
  <button
@@ -289,7 +427,7 @@
289
427
  class:suu-dropdown__option--active={option.value === activeValue}
290
428
  class:suu-dropdown__option--disabled={option.disabled}
291
429
  role="option"
292
- aria-selected={option.value === value}
430
+ aria-selected={multiselect ? selectedValues.includes(option.value) : option.value === value}
293
431
  aria-disabled={option.disabled}
294
432
  disabled={option.disabled}
295
433
  data-value={String(option.value)}
@@ -301,7 +439,14 @@
301
439
  }}
302
440
  on:click={() => selectOption(option)}
303
441
  >
304
- {option.label}
442
+ {#if multiselect}
443
+ <span
444
+ class="suu-dropdown__checkbox"
445
+ data-checked={multiselect ? selectedValues.includes(option.value) : option.value === value}
446
+ aria-hidden="true"
447
+ ></span>
448
+ {/if}
449
+ <span class="suu-dropdown__label">{option.label}</span>
305
450
  </button>
306
451
  {/each}
307
452
  {:else}
@@ -317,7 +462,7 @@
317
462
  class:suu-dropdown__option--active={option.value === activeValue}
318
463
  class:suu-dropdown__option--disabled={option.disabled}
319
464
  role="option"
320
- aria-selected={option.value === value}
465
+ aria-selected={multiselect ? selectedValues.includes(option.value) : option.value === value}
321
466
  aria-disabled={option.disabled}
322
467
  disabled={option.disabled}
323
468
  data-value={String(option.value)}
@@ -329,7 +474,14 @@
329
474
  }}
330
475
  on:click={() => selectOption(option)}
331
476
  >
332
- {option.label}
477
+ {#if multiselect}
478
+ <span
479
+ class="suu-dropdown__checkbox"
480
+ data-checked={multiselect ? selectedValues.includes(option.value) : option.value === value}
481
+ aria-hidden="true"
482
+ ></span>
483
+ {/if}
484
+ <span class="suu-dropdown__label">{option.label}</span>
333
485
  </button>
334
486
  {/each}
335
487
  </div>
@@ -1,4 +1,4 @@
1
- import type { DropdownChangeHandler, DropdownMenuAlign, DropdownOption, DropdownOptionGroup, DropdownPlacement, DropdownTriggerClickHandler, DropdownValue } from './types.js';
1
+ import type { DropdownMenuAlign, DropdownOption, DropdownOptionGroup, DropdownPlacement, DropdownSelectionChangeHandler, DropdownSelection, DropdownTriggerClickHandler } from './types.js';
2
2
  interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
3
  new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
4
  $$bindings?: Bindings;
@@ -14,7 +14,8 @@ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> =
14
14
  }
15
15
  declare const Dropdown: $$__sveltets_2_IsomorphicComponent<{
16
16
  id?: string | undefined;
17
- value?: DropdownValue;
17
+ value?: DropdownSelection;
18
+ multiselect?: boolean;
18
19
  options?: DropdownOption[];
19
20
  optionGroups?: DropdownOptionGroup[] | undefined;
20
21
  ariaLabel?: string | undefined;
@@ -27,7 +28,8 @@ declare const Dropdown: $$__sveltets_2_IsomorphicComponent<{
27
28
  minWidth?: string | undefined;
28
29
  maxWidth?: string | undefined;
29
30
  className?: string | undefined;
30
- onChange?: DropdownChangeHandler | undefined;
31
+ portal?: boolean;
32
+ onChange?: DropdownSelectionChangeHandler | undefined;
31
33
  onTriggerClick?: DropdownTriggerClickHandler | undefined;
32
34
  }, {
33
35
  [evt: string]: CustomEvent<any>;
@@ -1 +1 @@
1
- {"version":3,"file":"Dropdown.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/dropdown/Dropdown.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACR,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,EACd,mBAAmB,EACnB,iBAAiB,EACjB,2BAA2B,EAC3B,aAAa,EACd,MAAM,YAAY,CAAC;AA6RtB,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IAC9G,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,QAAQ;SAVuW,MAAM,GAAG,SAAS;YAAU,aAAa;cAAY,cAAc,EAAE;mBAAiB,mBAAmB,EAAE,GAAG,SAAS;gBAAc,MAAM,GAAG,SAAS;gBAAc,iBAAiB;gBAAc,iBAAiB;;;;YAAwG,MAAM,GAAG,SAAS;eAAa,MAAM,GAAG,SAAS;eAAa,MAAM,GAAG,SAAS;gBAAc,MAAM,GAAG,SAAS;eAAa,qBAAqB,GAAG,SAAS;qBAAmB,2BAA2B,GAAG,SAAS;;;kBAU7yB,CAAC;AAC5E,KAAK,QAAQ,GAAG,YAAY,CAAC,OAAO,QAAQ,CAAC,CAAC;AAChD,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"Dropdown.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/dropdown/Dropdown.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAER,iBAAiB,EAGjB,cAAc,EACd,mBAAmB,EACnB,iBAAiB,EACjB,8BAA8B,EAC9B,iBAAiB,EACjB,2BAA2B,EAE5B,MAAM,YAAY,CAAC;AA6ZtB,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IAC9G,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,QAAQ;SAVmZ,MAAM,GAAG,SAAS;YAAU,iBAAiB;;cAA8C,cAAc,EAAE;mBAAiB,mBAAmB,EAAE,GAAG,SAAS;gBAAc,MAAM,GAAG,SAAS;gBAAc,iBAAiB;gBAAc,iBAAiB;;;;YAAwG,MAAM,GAAG,SAAS;eAAa,MAAM,GAAG,SAAS;eAAa,MAAM,GAAG,SAAS;gBAAc,MAAM,GAAG,SAAS;;eAAqC,8BAA8B,GAAG,SAAS;qBAAmB,2BAA2B,GAAG,SAAS;;;kBAUh6B,CAAC;AAC5E,KAAK,QAAQ,GAAG,YAAY,CAAC,OAAO,QAAQ,CAAC,CAAC;AAChD,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,53 @@
1
+ <svelte:options runes={false} />
2
+
3
+ <script lang="ts">
4
+ import Dropdown from './Dropdown.svelte';
5
+ import type {
6
+ DropdownMenuAlign,
7
+ DropdownMultiChangeHandler,
8
+ DropdownMultiValue,
9
+ DropdownOption,
10
+ DropdownOptionGroup,
11
+ DropdownPlacement,
12
+ DropdownTriggerClickHandler
13
+ } from './types.js';
14
+
15
+ export let id: string | undefined = undefined;
16
+ export let value: DropdownMultiValue = [];
17
+ export let options: DropdownOption[] = [];
18
+ export let optionGroups: DropdownOptionGroup[] | undefined = undefined;
19
+ export let ariaLabel: string | undefined = undefined;
20
+ export let placement: DropdownPlacement = 'auto';
21
+ export let menuAlign: DropdownMenuAlign = 'left';
22
+ export let fitViewport = true;
23
+ export let fitContent = false;
24
+ export let disabled = false;
25
+ export let width: string | undefined = undefined;
26
+ export let minWidth: string | undefined = undefined;
27
+ export let maxWidth: string | undefined = undefined;
28
+ export let className: string | undefined = undefined;
29
+ export let portal = false;
30
+ export let onChange: DropdownMultiChangeHandler | undefined = undefined;
31
+ export let onTriggerClick: DropdownTriggerClickHandler | undefined = undefined;
32
+ </script>
33
+
34
+ <Dropdown
35
+ {id}
36
+ {value}
37
+ multiselect={true}
38
+ {options}
39
+ {optionGroups}
40
+ {ariaLabel}
41
+ {placement}
42
+ {menuAlign}
43
+ {fitViewport}
44
+ {fitContent}
45
+ {disabled}
46
+ {width}
47
+ {minWidth}
48
+ {maxWidth}
49
+ {className}
50
+ {portal}
51
+ {onChange}
52
+ {onTriggerClick}
53
+ />
@@ -0,0 +1,38 @@
1
+ import type { DropdownMenuAlign, DropdownMultiChangeHandler, DropdownMultiValue, DropdownOption, DropdownOptionGroup, DropdownPlacement, DropdownTriggerClickHandler } from './types.js';
2
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
+ $$bindings?: Bindings;
5
+ } & Exports;
6
+ (internal: unknown, props: Props & {
7
+ $$events?: Events;
8
+ $$slots?: Slots;
9
+ }): Exports & {
10
+ $set?: any;
11
+ $on?: any;
12
+ };
13
+ z_$$bindings?: Bindings;
14
+ }
15
+ declare const DropdownMultiSelect: $$__sveltets_2_IsomorphicComponent<{
16
+ id?: string | undefined;
17
+ value?: DropdownMultiValue;
18
+ options?: DropdownOption[];
19
+ optionGroups?: DropdownOptionGroup[] | undefined;
20
+ ariaLabel?: string | undefined;
21
+ placement?: DropdownPlacement;
22
+ menuAlign?: DropdownMenuAlign;
23
+ fitViewport?: boolean;
24
+ fitContent?: boolean;
25
+ disabled?: boolean;
26
+ width?: string | undefined;
27
+ minWidth?: string | undefined;
28
+ maxWidth?: string | undefined;
29
+ className?: string | undefined;
30
+ portal?: boolean;
31
+ onChange?: DropdownMultiChangeHandler | undefined;
32
+ onTriggerClick?: DropdownTriggerClickHandler | undefined;
33
+ }, {
34
+ [evt: string]: CustomEvent<any>;
35
+ }, {}, {}, string>;
36
+ type DropdownMultiSelect = InstanceType<typeof DropdownMultiSelect>;
37
+ export default DropdownMultiSelect;
38
+ //# sourceMappingURL=DropdownMultiSelect.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DropdownMultiSelect.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/dropdown/DropdownMultiSelect.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACR,iBAAiB,EACjB,0BAA0B,EAC1B,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,iBAAiB,EACjB,2BAA2B,EAC5B,MAAM,YAAY,CAAC;AA+BtB,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IAC9G,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,mBAAmB;SAV6W,MAAM,GAAG,SAAS;YAAU,kBAAkB;cAAY,cAAc,EAAE;mBAAiB,mBAAmB,EAAE,GAAG,SAAS;gBAAc,MAAM,GAAG,SAAS;gBAAc,iBAAiB;gBAAc,iBAAiB;;;;YAAwG,MAAM,GAAG,SAAS;eAAa,MAAM,GAAG,SAAS;eAAa,MAAM,GAAG,SAAS;gBAAc,MAAM,GAAG,SAAS;;eAAqC,0BAA0B,GAAG,SAAS;qBAAmB,2BAA2B,GAAG,SAAS;;;kBAUr1B,CAAC;AACvF,KAAK,mBAAmB,GAAG,YAAY,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACtE,eAAe,mBAAmB,CAAC"}
@@ -1,3 +1,4 @@
1
1
  export { default as Dropdown } from './Dropdown.svelte';
2
- export type { DropdownChangeHandler, DropdownMenuAlign, DropdownOption, DropdownOptionGroup, DropdownPlacement, DropdownTriggerClickHandler, DropdownValue } from './types.js';
2
+ export { default as DropdownMultiSelect } from './DropdownMultiSelect.svelte';
3
+ export type { DropdownChangeHandler, DropdownMenuAlign, DropdownMultiChangeHandler, DropdownMultiValue, DropdownOption, DropdownOptionGroup, DropdownPlacement, DropdownSelectionChangeHandler, DropdownSelection, DropdownTriggerClickHandler, DropdownValue } from './types.js';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/dropdown/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EACV,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,EACd,mBAAmB,EACnB,iBAAiB,EACjB,2BAA2B,EAC3B,aAAa,EACd,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/dropdown/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAC9E,YAAY,EACV,qBAAqB,EACrB,iBAAiB,EACjB,0BAA0B,EAC1B,kBAAkB,EAClB,cAAc,EACd,mBAAmB,EACnB,iBAAiB,EACjB,8BAA8B,EAC9B,iBAAiB,EACjB,2BAA2B,EAC3B,aAAa,EACd,MAAM,YAAY,CAAC"}
@@ -1 +1,2 @@
1
1
  export { default as Dropdown } from './Dropdown.svelte';
2
+ export { default as DropdownMultiSelect } from './DropdownMultiSelect.svelte';
@@ -1,4 +1,6 @@
1
1
  export type DropdownValue = string | number;
2
+ export type DropdownMultiValue = DropdownValue[];
3
+ export type DropdownSelection = DropdownValue | DropdownMultiValue;
2
4
  export type DropdownPlacement = 'auto' | 'up' | 'down';
3
5
  export type DropdownMenuAlign = 'left' | 'right';
4
6
  export interface DropdownOption {
@@ -11,5 +13,9 @@ export interface DropdownOptionGroup {
11
13
  options: DropdownOption[];
12
14
  }
13
15
  export type DropdownChangeHandler = (value: DropdownValue) => void | Promise<void>;
16
+ export type DropdownMultiChangeHandler = (value: DropdownMultiValue) => void | Promise<void>;
17
+ export type DropdownSelectionChangeHandler = {
18
+ bivarianceHack(value: DropdownSelection): void | Promise<void>;
19
+ }['bivarianceHack'];
14
20
  export type DropdownTriggerClickHandler = (event: MouseEvent) => void;
15
21
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/dropdown/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5C,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC;AAEvD,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,OAAO,CAAC;AAEjD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED,MAAM,MAAM,qBAAqB,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnF,MAAM,MAAM,2BAA2B,GAAG,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/dropdown/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5C,MAAM,MAAM,kBAAkB,GAAG,aAAa,EAAE,CAAC;AAEjD,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG,kBAAkB,CAAC;AAEnE,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC;AAEvD,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,OAAO,CAAC;AAEjD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED,MAAM,MAAM,qBAAqB,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnF,MAAM,MAAM,0BAA0B,GAAG,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE7F,MAAM,MAAM,8BAA8B,GAAG;IAC5C,cAAc,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/D,CAAC,gBAAgB,CAAC,CAAC;AAEpB,MAAM,MAAM,2BAA2B,GAAG,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC"}
@@ -106,8 +106,10 @@
106
106
  <style>
107
107
  .suu-sortable-table-wrap { overflow: auto; border: 1px solid var(--suu-color-border, #d1d5db); border-radius: var(--suu-radius, 8px); }
108
108
  .suu-sortable-table { width: 100%; border-collapse: collapse; }
109
- .suu-sortable-table__drag-column, .suu-sortable-table__remove-column { width: 38px; }
110
- .suu-sortable-table__drag-cell, .suu-sortable-table__remove-cell { width: 38px; padding: 6px; text-align: center; vertical-align: middle; }
109
+ .suu-sortable-table__drag-column { width: 1%; }
110
+ .suu-sortable-table__remove-column { width: 38px; }
111
+ .suu-sortable-table__drag-cell { width: 1%; padding: 6px; text-align: center; vertical-align: middle; white-space: nowrap; }
112
+ .suu-sortable-table__remove-cell { width: 38px; padding: 6px; text-align: center; vertical-align: middle; }
111
113
  .suu-sortable-table__drag-handle, .suu-sortable-table__remove { display: inline-grid; place-items: center; border: 0; background: transparent; cursor: grab; color: var(--suu-color-text-dim, #6b7280); padding: 4px; font: inherit; }
112
114
  .suu-sortable-table__remove { width: 30px; height: 30px; cursor: pointer; color: var(--suu-color-danger, #dc2626); }
113
115
  .suu-sortable-table__remove svg { width: 16px; height: 16px; }
@@ -13,6 +13,7 @@
13
13
  disabled = false,
14
14
  allowRemoveLast = false,
15
15
  tableClass = '',
16
+ showCurrentControl = true,
16
17
  currentId = null,
17
18
  getCurrentDisabled = () => false,
18
19
  getCurrentLabel = (item: Item) => `Set ${getId(item)} as current`,
@@ -30,6 +31,7 @@
30
31
  disabled?: boolean;
31
32
  allowRemoveLast?: boolean;
32
33
  tableClass?: string;
34
+ showCurrentControl?: boolean;
33
35
  currentId?: string | null;
34
36
  getCurrentDisabled?: (item: Item, index: number) => boolean;
35
37
  getCurrentLabel?: (item: Item, index: number) => string;
@@ -80,19 +82,12 @@
80
82
  {getRemoveLabel}
81
83
  {onReorder}
82
84
  {onRemove}
83
- dragAccessory={currentControl}
85
+ dragAccessory={showCurrentControl ? currentControl : undefined}
84
86
  {header}
85
87
  {children}
86
88
  />
87
89
 
88
90
  <style>
89
- :global(.suu-sortable-table-enhanced .suu-sortable-table__drag-column),
90
- :global(.suu-sortable-table-enhanced .suu-sortable-table__drag-cell) {
91
- width: 68px;
92
- }
93
- :global(.suu-sortable-table-enhanced .suu-sortable-table__drag-cell) {
94
- white-space: nowrap;
95
- }
96
91
  .suu-sortable-table-enhanced__current {
97
92
  width: 16px;
98
93
  height: 16px;
@@ -7,6 +7,7 @@ declare function $$render<Item extends SortableTableItem>(): {
7
7
  disabled?: boolean;
8
8
  allowRemoveLast?: boolean;
9
9
  tableClass?: string;
10
+ showCurrentControl?: boolean;
10
11
  currentId?: string | null;
11
12
  getCurrentDisabled?: (item: Item, index: number) => boolean;
12
13
  getCurrentLabel?: (item: Item, index: number) => string;
@@ -1 +1 @@
1
- {"version":3,"file":"SortableTableEnhanced.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/sortable-table/SortableTableEnhanced.svelte.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SortableTableEnhanced.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/sortable-table/SortableTableEnhanced.svelte.ts"],"names":[],"mappings":"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"}
package/dist/style.css CHANGED
@@ -1128,7 +1128,8 @@
1128
1128
  gap: var(--suu-dropdown-space);
1129
1129
  align-items: center;
1130
1130
  justify-content: space-between;
1131
- height: 42px;
1131
+ min-height: 42px;
1132
+ height: auto;
1132
1133
  padding: 0 var(--suu-dropdown-space);
1133
1134
  border: 1px solid var(--suu-color-border);
1134
1135
  border-radius: var(--suu-radius);
@@ -1137,6 +1138,29 @@
1137
1138
  cursor: pointer;
1138
1139
  font: inherit;
1139
1140
  line-height: 1;
1141
+ text-align: left;
1142
+ }
1143
+
1144
+ .suu-dropdown__label {
1145
+ flex: 1 1 auto;
1146
+ min-width: 0;
1147
+ overflow: hidden;
1148
+ text-overflow: ellipsis;
1149
+ display: -webkit-box;
1150
+ -webkit-box-orient: vertical;
1151
+ -webkit-line-clamp: 2;
1152
+ line-clamp: 2;
1153
+ white-space: normal;
1154
+ overflow-wrap: break-word;
1155
+ line-height: 1.2;
1156
+ }
1157
+
1158
+ .suu-dropdown--multiselect .suu-dropdown__button .suu-dropdown__label {
1159
+ display: block;
1160
+ white-space: nowrap;
1161
+ overflow-wrap: normal;
1162
+ -webkit-line-clamp: unset;
1163
+ line-clamp: unset;
1140
1164
  }
1141
1165
 
1142
1166
  .suu-dropdown--sized .suu-dropdown__button {
@@ -1155,6 +1179,7 @@
1155
1179
  }
1156
1180
 
1157
1181
  .suu-dropdown__chevron {
1182
+ flex: 0 0 auto;
1158
1183
  width: 0.58em;
1159
1184
  height: 0.58em;
1160
1185
  border-right: 2px solid currentcolor;
@@ -1176,17 +1201,26 @@
1176
1201
  box-shadow: 0 18px 42px rgb(15 23 42 / 0.16);
1177
1202
  }
1178
1203
 
1179
- .suu-dropdown__menu--left {
1204
+ .suu-dropdown__menu--portal {
1205
+ position: fixed;
1206
+ top: var(--suu-dropdown-menu-top);
1207
+ right: var(--suu-dropdown-menu-right, auto);
1208
+ left: var(--suu-dropdown-menu-left, auto);
1209
+ width: var(--suu-dropdown-menu-width, max-content);
1210
+ z-index: 1200;
1211
+ }
1212
+
1213
+ .suu-dropdown__menu--left:not(.suu-dropdown__menu--portal) {
1180
1214
  right: auto;
1181
1215
  left: 0;
1182
1216
  }
1183
1217
 
1184
- .suu-dropdown__menu--right {
1218
+ .suu-dropdown__menu--right:not(.suu-dropdown__menu--portal) {
1185
1219
  right: 0;
1186
1220
  left: auto;
1187
1221
  }
1188
1222
 
1189
- .suu-dropdown__menu--up {
1223
+ .suu-dropdown__menu--up:not(.suu-dropdown__menu--portal) {
1190
1224
  top: auto;
1191
1225
  bottom: calc(100% + 6px);
1192
1226
  }
@@ -1205,6 +1239,16 @@
1205
1239
  white-space: nowrap;
1206
1240
  }
1207
1241
 
1242
+ .suu-dropdown__menu--fit-content .suu-dropdown__option .suu-dropdown__label {
1243
+ overflow: visible;
1244
+ text-overflow: clip;
1245
+ display: block;
1246
+ -webkit-line-clamp: unset;
1247
+ line-clamp: unset;
1248
+ white-space: nowrap;
1249
+ overflow-wrap: normal;
1250
+ }
1251
+
1208
1252
  .suu-dropdown__panel {
1209
1253
  max-height: var(--suu-dropdown-panel-max-height, 100vh);
1210
1254
  overflow-y: auto;
@@ -1238,6 +1282,39 @@
1238
1282
  text-align: left;
1239
1283
  }
1240
1284
 
1285
+ .suu-dropdown--multiselect .suu-dropdown__option {
1286
+ display: flex;
1287
+ gap: 10px;
1288
+ align-items: center;
1289
+ }
1290
+
1291
+ .suu-dropdown__checkbox {
1292
+ position: relative;
1293
+ flex: 0 0 auto;
1294
+ width: 1em;
1295
+ height: 1em;
1296
+ border: 1px solid var(--suu-color-border);
1297
+ border-radius: 0.2em;
1298
+ background: var(--suu-color-bg);
1299
+ }
1300
+
1301
+ .suu-dropdown__checkbox[data-checked='true'] {
1302
+ border-color: var(--suu-color-accent);
1303
+ background: var(--suu-color-accent);
1304
+ }
1305
+
1306
+ .suu-dropdown__checkbox[data-checked='true']::after {
1307
+ position: absolute;
1308
+ top: 0.08em;
1309
+ left: 0.3em;
1310
+ width: 0.26em;
1311
+ height: 0.52em;
1312
+ border-right: 0.13em solid var(--suu-color-bg);
1313
+ border-bottom: 0.13em solid var(--suu-color-bg);
1314
+ content: '';
1315
+ transform: rotate(45deg);
1316
+ }
1317
+
1241
1318
  .suu-dropdown__option:last-child {
1242
1319
  border-bottom: 0;
1243
1320
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ibobbyts/svelte-ui-utils",
3
- "version": "0.3.1",
3
+ "version": "0.3.5",
4
4
  "description": "Reusable Svelte 5 UI utilities for dialogs, toast notifications, dropdown search, and data tables.",
5
5
  "type": "module",
6
6
  "license": "MIT",