@ibobbyts/svelte-ui-utils 0.3.9 → 0.4.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 (38) hide show
  1. package/README.md +100 -21
  2. package/dist/data-table/DataTable.svelte +139 -22
  3. package/dist/data-table/DataTable.svelte.d.ts +2 -8
  4. package/dist/data-table/DataTable.svelte.d.ts.map +1 -1
  5. package/dist/data-table/FilterControl.svelte +64 -18
  6. package/dist/data-table/FilterControl.svelte.d.ts.map +1 -1
  7. package/dist/data-table/filter.d.ts +3 -1
  8. package/dist/data-table/filter.d.ts.map +1 -1
  9. package/dist/data-table/filter.js +4 -0
  10. package/dist/data-table/index.d.ts +1 -1
  11. package/dist/data-table/index.d.ts.map +1 -1
  12. package/dist/data-table/types.d.ts +37 -3
  13. package/dist/data-table/types.d.ts.map +1 -1
  14. package/dist/dropdown/Dropdown.svelte +102 -0
  15. package/dist/dropdown/Dropdown.svelte.d.ts +2 -0
  16. package/dist/dropdown/Dropdown.svelte.d.ts.map +1 -1
  17. package/dist/dropdown/DropdownMultiSelect.svelte +1 -1
  18. package/dist/dropdown/types.d.ts +2 -0
  19. package/dist/dropdown/types.d.ts.map +1 -1
  20. package/dist/dropdown-search/DropdownSearch.svelte +47 -0
  21. package/dist/dropdown-search/DropdownSearch.svelte.d.ts.map +1 -1
  22. package/dist/index.d.ts +1 -0
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +1 -0
  25. package/dist/ordered-list/OrderedListEditor.svelte +1 -1
  26. package/dist/pagination/Pagination.svelte +9 -0
  27. package/dist/pagination/Pagination.svelte.d.ts +1 -0
  28. package/dist/pagination/Pagination.svelte.d.ts.map +1 -1
  29. package/dist/sortable-list/SortableList.svelte +1 -1
  30. package/dist/sortable-table/SortableTable.svelte +1 -1
  31. package/dist/style.css +97 -12
  32. package/dist/tooltip/Tooltip.svelte +149 -0
  33. package/dist/tooltip/Tooltip.svelte.d.ts +22 -0
  34. package/dist/tooltip/Tooltip.svelte.d.ts.map +1 -0
  35. package/dist/tooltip/index.d.ts +2 -0
  36. package/dist/tooltip/index.d.ts.map +1 -0
  37. package/dist/tooltip/index.js +1 -0
  38. package/package.json +1 -1
package/dist/style.css CHANGED
@@ -12,7 +12,13 @@
12
12
  --suu-color-warning: #d97706;
13
13
  --suu-color-info: #2563eb;
14
14
  --suu-shadow: 0 12px 30px rgb(15 23 42 / 0.14);
15
- --suu-radius: 8px;
15
+ --suu-radius-xs: 4px;
16
+ --suu-radius-sm: 6px;
17
+ --suu-radius-md: 8px;
18
+ --suu-radius-pill: 999px;
19
+ --suu-radius: var(--suu-radius-md);
20
+ --suu-radius-lg: calc(var(--suu-radius) + 2px);
21
+ --suu-radius-inner: max(0px, calc(var(--suu-radius) - 1px));
16
22
  --suu-font: inherit;
17
23
  }
18
24
 
@@ -35,6 +41,7 @@
35
41
 
36
42
  .suu-visually-hidden {
37
43
  position: absolute;
44
+ pointer-events: none;
38
45
  overflow: hidden;
39
46
  width: 1px;
40
47
  height: 1px;
@@ -170,7 +177,7 @@
170
177
  width: 28px;
171
178
  height: 28px;
172
179
  border: 0;
173
- border-radius: 999px;
180
+ border-radius: var(--suu-radius-pill);
174
181
  background: transparent;
175
182
  color: var(--suu-color-muted);
176
183
  cursor: pointer;
@@ -236,7 +243,7 @@
236
243
  gap: 6px;
237
244
  padding: 4px 6px 4px 10px;
238
245
  border: 1px solid color-mix(in srgb, var(--suu-color-accent) 34%, var(--suu-color-border));
239
- border-radius: 999px;
246
+ border-radius: var(--suu-radius-pill);
240
247
  background: color-mix(in srgb, var(--suu-color-accent) 9%, var(--suu-color-bg));
241
248
  color: var(--suu-color-text);
242
249
  font-size: 0.875rem;
@@ -257,7 +264,7 @@
257
264
  height: 1.25rem;
258
265
  padding: 0;
259
266
  border: 0;
260
- border-radius: 999px;
267
+ border-radius: var(--suu-radius-pill);
261
268
  background: transparent;
262
269
  color: var(--suu-color-muted);
263
270
  cursor: pointer;
@@ -349,7 +356,7 @@
349
356
  height: 1.5rem;
350
357
  padding: 0;
351
358
  border: 0;
352
- border-radius: 999px;
359
+ border-radius: var(--suu-radius-pill);
353
360
  background: transparent;
354
361
  color: var(--suu-color-muted);
355
362
  cursor: pointer;
@@ -451,7 +458,7 @@
451
458
  width: 1rem;
452
459
  height: 1rem;
453
460
  border: 1px solid var(--suu-color-border-strong);
454
- border-radius: 4px;
461
+ border-radius: var(--suu-radius-xs);
455
462
  color: transparent;
456
463
  }
457
464
 
@@ -496,7 +503,7 @@
496
503
  height: 0.875rem;
497
504
  border: 2px solid color-mix(in srgb, var(--suu-color-muted) 28%, transparent);
498
505
  border-top-color: var(--suu-color-accent);
499
- border-radius: 999px;
506
+ border-radius: var(--suu-radius-pill);
500
507
  animation: suu-dropdown-spinner 700ms linear infinite;
501
508
  }
502
509
 
@@ -690,6 +697,22 @@
690
697
  background: var(--suu-color-border);
691
698
  }
692
699
 
700
+ .suu-filter-table__filters:not(.suu-filter-table__filters--borderless) tr:first-child th {
701
+ border-top-left-radius: var(--suu-radius-inner);
702
+ }
703
+
704
+ .suu-filter-table__filters:not(.suu-filter-table__filters--borderless) tr:first-child td {
705
+ border-top-right-radius: var(--suu-radius-inner);
706
+ }
707
+
708
+ .suu-filter-table__filters:not(.suu-filter-table__filters--borderless) tr:last-child th {
709
+ border-bottom-left-radius: var(--suu-radius-inner);
710
+ }
711
+
712
+ .suu-filter-table__filters:not(.suu-filter-table__filters--borderless) tr:last-child td {
713
+ border-bottom-right-radius: var(--suu-radius-inner);
714
+ }
715
+
693
716
  .suu-filter-table__filters td {
694
717
  min-width: 0;
695
718
  }
@@ -712,6 +735,20 @@
712
735
  align-items: center;
713
736
  }
714
737
 
738
+ .suu-filter-table__option--group-end::after {
739
+ position: absolute;
740
+ top: 5%;
741
+ left: calc(100% + 12px);
742
+ bottom: 5%;
743
+ width: 1px;
744
+ background: var(--suu-color-border);
745
+ content: '';
746
+ }
747
+
748
+ .suu-filter-table__option--group-end {
749
+ margin-right: 10px;
750
+ }
751
+
715
752
  .suu-filter-table__option {
716
753
  position: relative;
717
754
  display: inline-flex;
@@ -772,7 +809,7 @@
772
809
  height: 1em;
773
810
  place-items: center;
774
811
  border: 1px solid var(--suu-color-border-strong);
775
- border-radius: 999px;
812
+ border-radius: var(--suu-radius-pill);
776
813
  color: transparent;
777
814
  transform: translateY(0.06em);
778
815
  }
@@ -1288,7 +1325,7 @@
1288
1325
  width: 1em;
1289
1326
  height: 1em;
1290
1327
  border: 1px solid var(--suu-color-border);
1291
- border-radius: 0.2em;
1328
+ border-radius: var(--suu-radius-xs);
1292
1329
  background: var(--suu-color-bg);
1293
1330
  }
1294
1331
 
@@ -1359,7 +1396,7 @@
1359
1396
  max-height: min(720px, calc(100vh - 48px));
1360
1397
  overflow: hidden;
1361
1398
  border: 1px solid var(--suu-color-border);
1362
- border-radius: calc(var(--suu-radius) + 2px);
1399
+ border-radius: var(--suu-radius-lg);
1363
1400
  background: var(--suu-color-bg);
1364
1401
  box-shadow: var(--suu-shadow);
1365
1402
  }
@@ -1431,7 +1468,7 @@
1431
1468
  height: 2rem;
1432
1469
  padding: 0;
1433
1470
  border: 0;
1434
- border-radius: 999px;
1471
+ border-radius: var(--suu-radius-pill);
1435
1472
  background: transparent;
1436
1473
  color: var(--suu-color-muted);
1437
1474
  cursor: pointer;
@@ -1465,6 +1502,54 @@
1465
1502
  background: color-mix(in srgb, var(--suu-color-surface) 64%, var(--suu-color-bg));
1466
1503
  }
1467
1504
 
1505
+ .suu-tooltip {
1506
+ position: relative;
1507
+ display: inline-flex;
1508
+ vertical-align: middle;
1509
+ }
1510
+
1511
+ .suu-tooltip__panel {
1512
+ position: fixed;
1513
+ box-sizing: border-box;
1514
+ top: var(--suu-tooltip-top, 0px);
1515
+ left: var(--suu-tooltip-left, 0px);
1516
+ z-index: 1300;
1517
+ max-width: min(320px, calc(100vw - 16px));
1518
+ max-height: calc(100vh - 16px);
1519
+ padding: 8px 10px;
1520
+ border: 1px solid var(--suu-color-border);
1521
+ border-radius: var(--suu-radius);
1522
+ background: var(--suu-color-surface);
1523
+ color: var(--suu-color-text);
1524
+ box-shadow: var(--suu-shadow);
1525
+ font-size: 0.875rem;
1526
+ line-height: 1.4;
1527
+ overflow-wrap: anywhere;
1528
+ overflow-y: auto;
1529
+ pointer-events: none;
1530
+ }
1531
+
1532
+ .suu-tooltip__panel img {
1533
+ display: block;
1534
+ width: auto;
1535
+ max-width: calc(100vw - 36px);
1536
+ max-height: calc(100vh - 36px);
1537
+ height: auto;
1538
+ object-fit: contain;
1539
+ }
1540
+
1541
+ .suu-tooltip__panel:has(img) {
1542
+ display: flex;
1543
+ flex-direction: column;
1544
+ max-width: calc(100vw - 16px);
1545
+ overflow: hidden;
1546
+ }
1547
+
1548
+ .suu-tooltip__panel:has(img) img {
1549
+ flex: 1 1 auto;
1550
+ min-height: 0;
1551
+ }
1552
+
1468
1553
  .suu-dialog__button {
1469
1554
  min-height: 38px;
1470
1555
  padding: 7px 14px;
@@ -1626,7 +1711,7 @@
1626
1711
  height: 1rem;
1627
1712
  border: 2px solid color-mix(in srgb, var(--suu-color-muted) 28%, transparent);
1628
1713
  border-top-color: var(--suu-color-accent);
1629
- border-radius: 999px;
1714
+ border-radius: var(--suu-radius-pill);
1630
1715
  animation: suu-dropdown-spinner 700ms linear infinite;
1631
1716
  }
1632
1717
 
@@ -0,0 +1,149 @@
1
+ <svelte:options runes={false} />
2
+
3
+ <script lang="ts">
4
+ import { afterUpdate, onDestroy } from 'svelte';
5
+
6
+ let rootElement: HTMLSpanElement | undefined;
7
+ let panelElement: HTMLDivElement | undefined;
8
+ let hovered = false;
9
+ let focused = false;
10
+ let visible = false;
11
+ let panelId = `suu-tooltip-${Math.random().toString(36).slice(2)}`;
12
+ let frame = 0;
13
+ let triggerElement: HTMLElement | undefined;
14
+ let panelObserver: ResizeObserver | undefined;
15
+
16
+ $: visible = hovered || focused;
17
+
18
+ function updatePosition() {
19
+ if (!visible || !rootElement || !panelElement || typeof window === 'undefined') {
20
+ return;
21
+ }
22
+
23
+ const trigger = rootElement.getBoundingClientRect();
24
+ const panel = panelElement.getBoundingClientRect();
25
+ const gap = 8;
26
+ const canPlaceAbove = trigger.top >= panel.height + gap;
27
+ const preferredTop = canPlaceAbove ? trigger.top - panel.height - gap : trigger.bottom + gap;
28
+ const maxTop = Math.max(gap, window.innerHeight - panel.height - gap);
29
+ const top = Math.min(maxTop, Math.max(gap, preferredTop));
30
+ const maxLeft = Math.max(gap, window.innerWidth - panel.width - gap);
31
+ const left = Math.min(
32
+ Math.max(gap, trigger.left + (trigger.width - panel.width) / 2),
33
+ maxLeft,
34
+ );
35
+
36
+ panelElement.style.setProperty('--suu-tooltip-top', `${top}px`);
37
+ panelElement.style.setProperty('--suu-tooltip-left', `${left}px`);
38
+ }
39
+
40
+ function schedulePosition() {
41
+ if (typeof window === 'undefined') return;
42
+ cancelAnimationFrame(frame);
43
+ frame = requestAnimationFrame(updatePosition);
44
+ }
45
+
46
+ function showFromHover() {
47
+ hovered = true;
48
+ triggerElement = findTrigger();
49
+ schedulePosition();
50
+ }
51
+
52
+ function hideFromHover() {
53
+ hovered = false;
54
+ }
55
+
56
+ function focusIn(event: FocusEvent) {
57
+ focused = true;
58
+ triggerElement = event.target instanceof HTMLElement ? event.target : findTrigger();
59
+ schedulePosition();
60
+ }
61
+
62
+ function focusOut(event: FocusEvent) {
63
+ const next = event.relatedTarget;
64
+ if (next instanceof Node && rootElement?.contains(next)) return;
65
+ focused = false;
66
+ }
67
+
68
+ function handleViewportChange() {
69
+ schedulePosition();
70
+ }
71
+
72
+ function findTrigger() {
73
+ return rootElement?.querySelector<HTMLElement>(
74
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
75
+ ) ?? undefined;
76
+ }
77
+
78
+ function syncDescription() {
79
+ if (!rootElement || !$$slots.content) return;
80
+ const nextTrigger = triggerElement ?? findTrigger();
81
+ if (triggerElement && triggerElement !== nextTrigger) {
82
+ removePanelDescription(triggerElement);
83
+ }
84
+ triggerElement = nextTrigger;
85
+ if (triggerElement) {
86
+ if (visible) addPanelDescription(triggerElement);
87
+ else removePanelDescription(triggerElement);
88
+ }
89
+ }
90
+
91
+ function addPanelDescription(element: HTMLElement) {
92
+ const ids = new Set((element.getAttribute('aria-describedby') ?? '').split(/\s+/).filter(Boolean));
93
+ ids.add(panelId);
94
+ element.setAttribute('aria-describedby', [...ids].join(' '));
95
+ }
96
+
97
+ function removePanelDescription(element: HTMLElement) {
98
+ const ids = (element.getAttribute('aria-describedby') ?? '')
99
+ .split(/\s+/)
100
+ .filter((id) => id && id !== panelId);
101
+ if (ids.length > 0) element.setAttribute('aria-describedby', ids.join(' '));
102
+ else element.removeAttribute('aria-describedby');
103
+ }
104
+
105
+ function syncPanelObserver() {
106
+ panelObserver?.disconnect();
107
+ panelObserver = undefined;
108
+ if (visible && panelElement && typeof ResizeObserver !== 'undefined') {
109
+ panelObserver = new ResizeObserver(() => schedulePosition());
110
+ panelObserver.observe(panelElement);
111
+ }
112
+ syncDescription();
113
+ }
114
+
115
+ if (typeof window !== 'undefined') {
116
+ window.addEventListener('resize', handleViewportChange);
117
+ window.addEventListener('scroll', handleViewportChange, true);
118
+ }
119
+
120
+ afterUpdate(syncPanelObserver);
121
+
122
+ onDestroy(() => {
123
+ if (typeof window !== 'undefined') {
124
+ window.removeEventListener('resize', handleViewportChange);
125
+ window.removeEventListener('scroll', handleViewportChange, true);
126
+ cancelAnimationFrame(frame);
127
+ }
128
+ panelObserver?.disconnect();
129
+ if (triggerElement) removePanelDescription(triggerElement);
130
+ });
131
+ </script>
132
+
133
+ <span
134
+ bind:this={rootElement}
135
+ class="suu-tooltip"
136
+ role="group"
137
+ on:mouseenter={showFromHover}
138
+ on:mouseleave={hideFromHover}
139
+ on:focusin={focusIn}
140
+ on:focusout={focusOut}
141
+ >
142
+ <slot />
143
+
144
+ {#if visible && $$slots.content}
145
+ <div bind:this={panelElement} class="suu-tooltip__panel" id={panelId} role="tooltip">
146
+ <slot name="content" />
147
+ </div>
148
+ {/if}
149
+ </span>
@@ -0,0 +1,22 @@
1
+ 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> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const Tooltip: $$__sveltets_2_IsomorphicComponent<any, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {
17
+ default: {};
18
+ content: {};
19
+ }, {}, string>;
20
+ type Tooltip = InstanceType<typeof Tooltip>;
21
+ export default Tooltip;
22
+ //# sourceMappingURL=Tooltip.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Tooltip.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/tooltip/Tooltip.svelte.ts"],"names":[],"mappings":"AAqJA,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;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;IACtG,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAUD,QAAA,MAAM,OAAO;;;;;cAAqF,CAAC;AACjF,KAAK,OAAO,GAAG,YAAY,CAAC,OAAO,OAAO,CAAC,CAAC;AAC9C,eAAe,OAAO,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { default as Tooltip } from './Tooltip.svelte';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/tooltip/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1 @@
1
+ export { default as Tooltip } from './Tooltip.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ibobbyts/svelte-ui-utils",
3
- "version": "0.3.9",
3
+ "version": "0.4.1",
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",