@marianmeres/stuic 2.3.2 → 2.5.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 (99) hide show
  1. package/README.md +0 -1
  2. package/dist/README.md +17 -11
  3. package/dist/actions/autogrow.svelte.js +4 -4
  4. package/dist/actions/autoscroll.d.ts +2 -2
  5. package/dist/actions/autoscroll.js +17 -8
  6. package/dist/actions/file-dropzone.svelte.d.ts +1 -1
  7. package/dist/actions/file-dropzone.svelte.js +1 -1
  8. package/dist/actions/focus-trap.js +33 -24
  9. package/dist/actions/highlight-dragover.svelte.js +6 -5
  10. package/dist/actions/index.d.ts +1 -0
  11. package/dist/actions/index.js +1 -0
  12. package/dist/actions/on-submit-validity-check.svelte.js +2 -2
  13. package/dist/actions/popover/PopoverContent.svelte +15 -0
  14. package/dist/actions/popover/PopoverContent.svelte.d.ts +7 -0
  15. package/dist/actions/popover/index.css +78 -0
  16. package/dist/actions/popover/index.d.ts +1 -0
  17. package/dist/actions/popover/index.js +1 -0
  18. package/dist/actions/popover/popover.svelte.d.ts +112 -0
  19. package/dist/actions/popover/popover.svelte.js +449 -0
  20. package/dist/actions/resizable-width.svelte.d.ts +1 -1
  21. package/dist/actions/resizable-width.svelte.js +7 -5
  22. package/dist/actions/tooltip/index.css +2 -7
  23. package/dist/actions/tooltip/tooltip.svelte.js +5 -4
  24. package/dist/actions/trim.svelte.d.ts +1 -1
  25. package/dist/actions/trim.svelte.js +2 -2
  26. package/dist/actions/validate.svelte.d.ts +4 -4
  27. package/dist/actions/validate.svelte.js +9 -9
  28. package/dist/components/AlertConfirmPrompt/alert-confirm-prompt-stack.svelte.d.ts +7 -6
  29. package/dist/components/AlertConfirmPrompt/alert-confirm-prompt-stack.svelte.js +1 -2
  30. package/dist/components/AlertConfirmPrompt/index.d.ts +1 -1
  31. package/dist/components/AlertConfirmPrompt/index.js +1 -1
  32. package/dist/components/AnimatedElipsis/index.d.ts +1 -1
  33. package/dist/components/AnimatedElipsis/index.js +1 -1
  34. package/dist/components/Button/index.d.ts +1 -1
  35. package/dist/components/Button/index.js +1 -1
  36. package/dist/components/ButtonGroupRadio/ButtonGroupRadio.svelte +4 -1
  37. package/dist/components/ButtonGroupRadio/index.d.ts +1 -1
  38. package/dist/components/ButtonGroupRadio/index.js +1 -1
  39. package/dist/components/ColorScheme/index.d.ts +2 -2
  40. package/dist/components/ColorScheme/index.js +2 -2
  41. package/dist/components/CommandMenu/CommandMenu.svelte +1 -1
  42. package/dist/components/CommandMenu/index.d.ts +1 -1
  43. package/dist/components/CommandMenu/index.js +1 -1
  44. package/dist/components/DismissibleMessage/index.d.ts +1 -1
  45. package/dist/components/DismissibleMessage/index.js +1 -1
  46. package/dist/components/HoverExpandableWidth/index.d.ts +1 -1
  47. package/dist/components/HoverExpandableWidth/index.js +1 -1
  48. package/dist/components/Input/FieldAssets.svelte +7 -3
  49. package/dist/components/Input/FieldLikeButton.svelte +1 -1
  50. package/dist/components/Input/FieldOptions.svelte +1 -1
  51. package/dist/components/Input/index.d.ts +7 -7
  52. package/dist/components/Input/index.js +7 -7
  53. package/dist/components/KbdShortcut/index.d.ts +1 -1
  54. package/dist/components/KbdShortcut/index.js +1 -1
  55. package/dist/components/ModalDialog/index.d.ts +1 -1
  56. package/dist/components/ModalDialog/index.js +1 -1
  57. package/dist/components/Notifications/index.d.ts +1 -1
  58. package/dist/components/Notifications/index.js +1 -1
  59. package/dist/components/Notifications/notifications-stack.svelte.d.ts +5 -5
  60. package/dist/components/Notifications/notifications-stack.svelte.js +8 -7
  61. package/dist/components/SlidingPanels/index.d.ts +1 -1
  62. package/dist/components/SlidingPanels/index.js +1 -1
  63. package/dist/components/Spinner/index.d.ts +1 -1
  64. package/dist/components/Spinner/index.js +1 -1
  65. package/dist/components/Switch/Switch.svelte +5 -2
  66. package/dist/components/Switch/index.d.ts +1 -1
  67. package/dist/components/Switch/index.js +1 -1
  68. package/dist/components/TypeaheadInput/index.d.ts +1 -1
  69. package/dist/components/TypeaheadInput/index.js +1 -1
  70. package/dist/utils/body-scroll-locker.js +4 -3
  71. package/dist/utils/breakpoint.svelte.js +0 -2
  72. package/dist/utils/colors.js +3 -3
  73. package/dist/utils/debounce.d.ts +1 -1
  74. package/dist/utils/debounce.js +1 -2
  75. package/dist/utils/escape-regex.js +1 -1
  76. package/dist/utils/event-emitter.d.ts +2 -3
  77. package/dist/utils/event-emitter.js +1 -2
  78. package/dist/utils/event-modifiers.d.ts +4 -4
  79. package/dist/utils/event-modifiers.js +4 -6
  80. package/dist/utils/get-file-type-label.js +1 -1
  81. package/dist/utils/is-image.js +2 -2
  82. package/dist/utils/is-nullish.d.ts +1 -1
  83. package/dist/utils/is-plain-object.d.ts +1 -1
  84. package/dist/utils/is-plain-object.js +4 -1
  85. package/dist/utils/maybe-json-parse.d.ts +1 -1
  86. package/dist/utils/maybe-json-parse.js +1 -1
  87. package/dist/utils/maybe-json-stringify.d.ts +1 -1
  88. package/dist/utils/move-array-item.d.ts +1 -1
  89. package/dist/utils/preload-img.js +2 -1
  90. package/dist/utils/sleep.d.ts +1 -1
  91. package/dist/utils/storage-abstraction.d.ts +13 -13
  92. package/dist/utils/storage-abstraction.js +2 -0
  93. package/dist/utils/svg-circle.js +2 -1
  94. package/dist/utils/switch.svelte.d.ts +1 -1
  95. package/dist/utils/switch.svelte.js +1 -1
  96. package/dist/utils/throttle.d.ts +1 -1
  97. package/dist/utils/throttle.js +7 -8
  98. package/dist/utils/to-integer.d.ts +1 -1
  99. package/package.json +6 -2
@@ -0,0 +1,449 @@
1
+ import { mount, unmount } from "svelte";
2
+ import { twMerge } from "../../utils/tw-merge.js";
3
+ import PopoverContent from "./PopoverContent.svelte";
4
+ //
5
+ import "./index.css";
6
+ // Registry of open popover hide functions for closeOthers feature
7
+ const openPopovers = new Set();
8
+ const SHOW_DELAY = 100;
9
+ const HIDE_DELAY = 200;
10
+ const TRANSITION = 200;
11
+ /**
12
+ * Checks if the browser supports CSS Anchor Positioning for popovers.
13
+ *
14
+ * Tests for support of `anchor-name`, `position-area`, `position-try`,
15
+ * and `position-try-fallbacks` CSS properties.
16
+ *
17
+ * @returns `true` if CSS Anchor Positioning is fully supported
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * if (isPopoverSupported()) {
22
+ * // Use native anchor positioning
23
+ * } else {
24
+ * // Fall back to centered modal
25
+ * }
26
+ * ```
27
+ */
28
+ export function isPopoverSupported() {
29
+ return (CSS.supports("anchor-name", "--anchor") &&
30
+ CSS.supports("position-area", "top") &&
31
+ CSS.supports("position-try", "top") &&
32
+ CSS.supports("position-try-fallbacks", "top"));
33
+ }
34
+ const POSITION_MAP = {
35
+ top: "top",
36
+ "top-left": "top left",
37
+ "top-right": "top right",
38
+ "top-span-left": "top span-left",
39
+ "top-span-right": "top span-right",
40
+ bottom: "bottom",
41
+ "bottom-left": "bottom left",
42
+ "bottom-right": "bottom right",
43
+ "bottom-span-left": "bottom span-left",
44
+ "bottom-span-right": "bottom span-right",
45
+ left: "left",
46
+ right: "right",
47
+ };
48
+ const _classPopover = `
49
+ bg-popover-bg dark:bg-popover-bg-dark text-popover-text dark:text-popover-text-dark
50
+ shadow-lg rounded-md
51
+ border border-popover-border dark:border-popover-border-dark
52
+ z-50
53
+ `;
54
+ const _classBackdrop = `
55
+ fixed inset-0 bg-black/25
56
+ z-40
57
+ `;
58
+ /**
59
+ * Checks if content is simple (string/html) vs complex (component/snippet).
60
+ */
61
+ function isSimpleContent(content) {
62
+ if (!content)
63
+ return true;
64
+ if (typeof content === "string")
65
+ return true;
66
+ if (typeof content === "object") {
67
+ if ("text" in content || "html" in content)
68
+ return true;
69
+ }
70
+ return false;
71
+ }
72
+ /**
73
+ * Extracts string content for simple THC types.
74
+ */
75
+ function getStringContent(content) {
76
+ if (!content)
77
+ return "";
78
+ if (typeof content === "string")
79
+ return content;
80
+ if (typeof content === "object") {
81
+ if ("html" in content)
82
+ return content.html;
83
+ if ("text" in content)
84
+ return content.text;
85
+ }
86
+ return "";
87
+ }
88
+ /**
89
+ * A Svelte action that displays a popover anchored to an element using CSS Anchor Positioning.
90
+ *
91
+ * The popover appears on click or hover (configurable) and supports multiple positions
92
+ * with automatic fallback. When CSS Anchor Positioning is not supported, falls back
93
+ * to a centered modal overlay.
94
+ *
95
+ * @param anchorEl - The element to attach the popover to
96
+ * @param fn - Function returning popover options (reactive)
97
+ *
98
+ * @example
99
+ * ```svelte
100
+ * <!-- Click trigger (default) -->
101
+ * <button use:popover={() => ({ content: "Hello World" })}>
102
+ * Open Popover
103
+ * </button>
104
+ * ```
105
+ *
106
+ * @example
107
+ * ```svelte
108
+ * <!-- Hover trigger -->
109
+ * <button use:popover={() => ({
110
+ * content: "Hover content",
111
+ * trigger: "hover",
112
+ * position: "top"
113
+ * })}>
114
+ * Hover Me
115
+ * </button>
116
+ * ```
117
+ *
118
+ * @example
119
+ * ```svelte
120
+ * <!-- With component content -->
121
+ * <button use:popover={() => ({
122
+ * content: { component: MyComponent, props: { foo: "bar" } }
123
+ * })}>
124
+ * With Component
125
+ * </button>
126
+ * ```
127
+ *
128
+ * @remarks
129
+ * - Falls back to centered modal when CSS Anchor Positioning is not supported
130
+ * - Closes on click outside (for click trigger) and Escape key by default
131
+ * - For hover trigger, popover persists when hovering over it
132
+ * - Automatically cleans up DOM elements on unmount
133
+ */
134
+ export function popover(anchorEl, fn) {
135
+ const isSupported = isPopoverSupported();
136
+ // State
137
+ let popoverEl = null;
138
+ let backdropEl = null;
139
+ let wrapperEl = null;
140
+ let mountedComponent = null;
141
+ let showTimer = null;
142
+ let hideTimer = null;
143
+ let isVisible = false;
144
+ let do_debug = false;
145
+ // Unique identifiers
146
+ const rnd = Math.random().toString(36).slice(2);
147
+ const id = `popover-${rnd}`;
148
+ const anchorName = `--anchor-popover-${rnd}`;
149
+ // Current options (updated reactively)
150
+ let currentOptions = {};
151
+ // Initialize anchor element - anchor-name is always set
152
+ // In forceFallback mode, the CSS is just ignored
153
+ anchorEl.style.cssText += `anchor-name: ${anchorName};`;
154
+ anchorEl.setAttribute("aria-haspopup", "dialog");
155
+ anchorEl.setAttribute("aria-expanded", "false");
156
+ anchorEl.setAttribute("aria-controls", id);
157
+ // Debug helper
158
+ const debug = (...args) => {
159
+ if (do_debug)
160
+ console.debug("[popover]", rnd, ...args);
161
+ };
162
+ // Timer helpers
163
+ function clearTimers() {
164
+ if (showTimer)
165
+ clearTimeout(showTimer);
166
+ if (hideTimer)
167
+ clearTimeout(hideTimer);
168
+ showTimer = null;
169
+ hideTimer = null;
170
+ }
171
+ function cancelHide() {
172
+ if (hideTimer)
173
+ clearTimeout(hideTimer);
174
+ hideTimer = null;
175
+ }
176
+ // Event handlers
177
+ function onEscape(e) {
178
+ if (e.key === "Escape") {
179
+ e.preventDefault();
180
+ e.stopPropagation();
181
+ e.stopImmediatePropagation();
182
+ hide();
183
+ }
184
+ }
185
+ function onClickOutside(e) {
186
+ if (popoverEl &&
187
+ !popoverEl.contains(e.target) &&
188
+ !anchorEl.contains(e.target)) {
189
+ hide();
190
+ }
191
+ }
192
+ function onClickTrigger(e) {
193
+ e.stopPropagation();
194
+ if (isVisible) {
195
+ hide();
196
+ }
197
+ else {
198
+ show();
199
+ }
200
+ }
201
+ // Render content into popover element
202
+ function renderContent() {
203
+ if (!popoverEl || !currentOptions.content)
204
+ return;
205
+ debug("renderContent()", currentOptions.content);
206
+ // Cleanup previous mounted component
207
+ if (mountedComponent) {
208
+ unmount(mountedComponent);
209
+ mountedComponent = null;
210
+ }
211
+ popoverEl.innerHTML = "";
212
+ const content = currentOptions.content;
213
+ if (isSimpleContent(content)) {
214
+ // Simple string/html content
215
+ popoverEl.innerHTML = getStringContent(content);
216
+ }
217
+ else {
218
+ // Complex content (component/snippet) - use mount()
219
+ mountedComponent = mount(PopoverContent, {
220
+ target: popoverEl,
221
+ props: { thc: content },
222
+ });
223
+ }
224
+ }
225
+ // Show popover
226
+ function show() {
227
+ debug("show()", { enabled: currentOptions.enabled, content: currentOptions.content });
228
+ clearTimers();
229
+ if (!currentOptions.enabled || !currentOptions.content)
230
+ return;
231
+ if (isVisible)
232
+ return;
233
+ // Close other popovers if requested
234
+ if (currentOptions.closeOthers) {
235
+ openPopovers.forEach((hideFn) => {
236
+ if (hideFn !== hide)
237
+ hideFn();
238
+ });
239
+ }
240
+ // Register this popover
241
+ openPopovers.add(hide);
242
+ isVisible = true;
243
+ anchorEl.setAttribute("aria-expanded", "true");
244
+ const offsetValue = currentOptions.offset || "0.25rem";
245
+ const useAnchorPositioning = isSupported && !currentOptions.forceFallback;
246
+ // Create elements
247
+ if (useAnchorPositioning) {
248
+ // CSS Anchor Positioning mode
249
+ popoverEl = document.createElement("div");
250
+ popoverEl.setAttribute("id", id);
251
+ popoverEl.setAttribute("role", "dialog");
252
+ popoverEl.style.cssText = `
253
+ position: fixed;
254
+ position-anchor: ${anchorName};
255
+ position-area: ${POSITION_MAP[currentOptions.position || "bottom"] || "bottom"};
256
+ transition-duration: ${TRANSITION}ms;
257
+ margin: ${offsetValue};
258
+ `;
259
+ popoverEl.classList.add(...twMerge("stuic-popover", _classPopover, currentOptions.class).split(/\s/));
260
+ document.body.appendChild(popoverEl);
261
+ }
262
+ else {
263
+ // Fallback centered modal mode
264
+ if (currentOptions.showBackdrop !== false) {
265
+ backdropEl = document.createElement("div");
266
+ backdropEl.classList.add(...twMerge("stuic-popover-backdrop", _classBackdrop).split(/\s/));
267
+ backdropEl.style.cssText = `transition-duration: ${TRANSITION}ms;`;
268
+ document.body.appendChild(backdropEl);
269
+ // Backdrop click closes popover
270
+ if (currentOptions.closeOnClickOutside !== false) {
271
+ backdropEl.addEventListener("click", hide);
272
+ }
273
+ }
274
+ // Create wrapper for centering
275
+ wrapperEl = document.createElement("div");
276
+ wrapperEl.classList.add("stuic-popover-wrapper");
277
+ wrapperEl.style.cssText = `
278
+ position: fixed;
279
+ inset: 0;
280
+ display: flex;
281
+ align-items: center;
282
+ justify-content: center;
283
+ z-index: 50;
284
+ pointer-events: none;
285
+ `;
286
+ // Create popover element
287
+ popoverEl = document.createElement("div");
288
+ popoverEl.setAttribute("id", id);
289
+ popoverEl.setAttribute("role", "dialog");
290
+ popoverEl.style.cssText = `
291
+ position: relative;
292
+ max-width: 90vw;
293
+ max-height: 90vh;
294
+ overflow: auto;
295
+ transition-duration: ${TRANSITION}ms;
296
+ pointer-events: auto;
297
+ `;
298
+ popoverEl.classList.add(...twMerge("stuic-popover-fallback", _classPopover, currentOptions.class).split(/\s/));
299
+ wrapperEl.appendChild(popoverEl);
300
+ document.body.appendChild(wrapperEl);
301
+ // Click on wrapper (outside popover) closes
302
+ if (currentOptions.closeOnClickOutside !== false) {
303
+ wrapperEl.addEventListener("click", (e) => {
304
+ if (e.target === wrapperEl)
305
+ hide();
306
+ });
307
+ }
308
+ }
309
+ // Render content
310
+ renderContent();
311
+ // Transition in
312
+ popoverEl.classList.add("pop-block");
313
+ requestAnimationFrame(() => {
314
+ popoverEl?.classList.add("pop-visible");
315
+ backdropEl?.classList.add("pop-visible");
316
+ currentOptions.onShow?.();
317
+ });
318
+ // Add event listeners
319
+ if (currentOptions.closeOnEscape !== false) {
320
+ document.addEventListener("keydown", onEscape);
321
+ }
322
+ // For hover mode, allow hovering over the popover itself
323
+ if (currentOptions.trigger === "hover" && popoverEl) {
324
+ popoverEl.addEventListener("mouseenter", cancelHide);
325
+ popoverEl.addEventListener("mouseleave", scheduleHide);
326
+ }
327
+ // For click mode with closeOnClickOutside
328
+ if (currentOptions.trigger === "click" &&
329
+ currentOptions.closeOnClickOutside !== false &&
330
+ useAnchorPositioning) {
331
+ // Delay adding click listener to avoid immediate close
332
+ setTimeout(() => {
333
+ document.addEventListener("click", onClickOutside);
334
+ }, 0);
335
+ }
336
+ }
337
+ // Hide popover
338
+ function hide() {
339
+ debug("hide()");
340
+ clearTimers();
341
+ if (!isVisible)
342
+ return;
343
+ isVisible = false;
344
+ // Unregister from open popovers
345
+ openPopovers.delete(hide);
346
+ anchorEl.setAttribute("aria-expanded", "false");
347
+ // Remove event listeners
348
+ document.removeEventListener("keydown", onEscape);
349
+ document.removeEventListener("click", onClickOutside);
350
+ // Transition out
351
+ popoverEl?.classList.remove("pop-visible");
352
+ backdropEl?.classList.remove("pop-visible");
353
+ setTimeout(() => {
354
+ // Cleanup mounted component
355
+ if (mountedComponent) {
356
+ unmount(mountedComponent);
357
+ mountedComponent = null;
358
+ }
359
+ // Remove elements
360
+ popoverEl?.remove();
361
+ backdropEl?.remove();
362
+ wrapperEl?.remove();
363
+ popoverEl = null;
364
+ backdropEl = null;
365
+ wrapperEl = null;
366
+ currentOptions.onHide?.();
367
+ }, TRANSITION);
368
+ }
369
+ function scheduleShow() {
370
+ debug("scheduleShow()");
371
+ clearTimers();
372
+ const delay = currentOptions.showDelay ?? SHOW_DELAY;
373
+ showTimer = setTimeout(show, delay);
374
+ }
375
+ function scheduleHide() {
376
+ debug("scheduleHide()");
377
+ clearTimers();
378
+ const delay = currentOptions.hideDelay ?? HIDE_DELAY;
379
+ hideTimer = setTimeout(hide, delay);
380
+ }
381
+ // Reactive params effect
382
+ $effect(() => {
383
+ const opts = fn?.() || {};
384
+ currentOptions = {
385
+ enabled: opts.enabled ?? true,
386
+ content: opts.content,
387
+ position: opts.position || "bottom",
388
+ trigger: opts.trigger || "click",
389
+ showDelay: opts.showDelay ?? SHOW_DELAY,
390
+ hideDelay: opts.hideDelay ?? HIDE_DELAY,
391
+ class: opts.class,
392
+ offset: opts.offset,
393
+ closeOthers: opts.closeOthers ?? false,
394
+ closeOnClickOutside: opts.closeOnClickOutside ?? true,
395
+ closeOnEscape: opts.closeOnEscape ?? true,
396
+ showBackdrop: opts.showBackdrop ?? true,
397
+ forceFallback: opts.forceFallback ?? false,
398
+ onShow: opts.onShow,
399
+ onHide: opts.onHide,
400
+ debug: opts.debug,
401
+ };
402
+ do_debug = !!opts.debug;
403
+ // Update popover if visible
404
+ if (isVisible && popoverEl) {
405
+ // Update position (only in anchor positioning mode)
406
+ if (isSupported && !currentOptions.forceFallback) {
407
+ popoverEl.style.setProperty("position-area", POSITION_MAP[currentOptions.position || "bottom"] || "bottom");
408
+ }
409
+ // Re-render content
410
+ renderContent();
411
+ }
412
+ // Note: trigger mode change while visible is not fully handled
413
+ // User should close and reopen for trigger mode change to take effect
414
+ });
415
+ // Event listeners effect
416
+ $effect(() => {
417
+ const trigger = currentOptions.trigger || "click";
418
+ if (trigger === "click") {
419
+ anchorEl.addEventListener("click", onClickTrigger);
420
+ }
421
+ else {
422
+ // hover mode
423
+ anchorEl.addEventListener("mouseenter", scheduleShow);
424
+ anchorEl.addEventListener("mouseleave", scheduleHide);
425
+ anchorEl.addEventListener("focus", scheduleShow);
426
+ anchorEl.addEventListener("blur", scheduleHide);
427
+ }
428
+ return () => {
429
+ anchorEl.removeEventListener("click", onClickTrigger);
430
+ anchorEl.removeEventListener("mouseenter", scheduleShow);
431
+ anchorEl.removeEventListener("mouseleave", scheduleHide);
432
+ anchorEl.removeEventListener("focus", scheduleShow);
433
+ anchorEl.removeEventListener("blur", scheduleHide);
434
+ // Cleanup popover on unmount
435
+ if (mountedComponent) {
436
+ unmount(mountedComponent);
437
+ mountedComponent = null;
438
+ }
439
+ popoverEl?.remove();
440
+ backdropEl?.remove();
441
+ wrapperEl?.remove();
442
+ clearTimers();
443
+ // Unregister from open popovers
444
+ openPopovers.delete(hide);
445
+ document.removeEventListener("keydown", onEscape);
446
+ document.removeEventListener("click", onClickOutside);
447
+ };
448
+ });
449
+ }
@@ -16,7 +16,7 @@ export interface ResizableWidthOptions {
16
16
  units: "px" | "%";
17
17
  container: number;
18
18
  }) => void;
19
- debug?: (...args: any[]) => void;
19
+ debug?: (...args: unknown[]) => void;
20
20
  }
21
21
  /**
22
22
  * A Svelte action that makes an element's width resizable via drag handle.
@@ -73,7 +73,8 @@ export function resizableWidth(el, fn) {
73
73
  return handle;
74
74
  }
75
75
  $effect(() => {
76
- let { enabled = true, initial = 0, min = 0, max = 0, units = "px", key, storage = "session", handleClass = "", handleDragClass = "", onResize, debug, } = fn?.() || {};
76
+ const { enabled = true, initial: initialValue = 0, min = 0, max = 0, units = "px", key, storage = "session", handleClass = "", handleDragClass = "", onResize, debug, } = fn?.() || {};
77
+ let initial = initialValue;
77
78
  const _debug = (...args) => debug?.("[resizable-width]", ...args);
78
79
  _debug("$effect");
79
80
  if (!enabled)
@@ -108,7 +109,8 @@ export function resizableWidth(el, fn) {
108
109
  value = Math.max(min, value);
109
110
  if (max)
110
111
  value = Math.min(max, value);
111
- _initial !== value && _debug("clamped", value, units);
112
+ if (_initial !== value)
113
+ _debug("clamped", value, units);
112
114
  return value;
113
115
  };
114
116
  let width;
@@ -132,7 +134,7 @@ export function resizableWidth(el, fn) {
132
134
  e.preventDefault(); // prevent scrolling on touch devices
133
135
  isResizing = true;
134
136
  //
135
- const clientX = e.touches ? e.touches[0].clientX : e.clientX;
137
+ const clientX = "touches" in e ? e.touches[0].clientX : e.clientX;
136
138
  startX = clientX;
137
139
  startWidth = parseInt(getComputedStyle(el).width, 10);
138
140
  containerW = container.offsetWidth;
@@ -145,9 +147,9 @@ export function resizableWidth(el, fn) {
145
147
  return;
146
148
  e.preventDefault(); // prevent scrolling on touch devices
147
149
  //
148
- const clientX = e.touches ? e.touches[0].clientX : e.clientX;
150
+ const clientX = "touches" in e ? e.touches[0].clientX : e.clientX;
149
151
  const deltaX = clientX - startX;
150
- let width = startWidth + deltaX;
152
+ const width = startWidth + deltaX;
151
153
  set_width_px(width);
152
154
  }
153
155
  function resize_stop() {
@@ -41,13 +41,8 @@
41
41
  /* position-area is set via inline style based on position param */
42
42
  /* fallbacks ensure tooltip stays within viewport */
43
43
  position-try-fallbacks:
44
- --tt-top-span-right,
45
- --tt-top-span-left,
46
- flip-block,
47
- --tt-bottom-span-right,
48
- --tt-bottom-span-left,
49
- --tt-left,
50
- --tt-right;
44
+ --tt-top-span-right, --tt-top-span-left, flip-block, --tt-bottom-span-right,
45
+ --tt-bottom-span-left, --tt-left, --tt-right;
51
46
 
52
47
  &.tt-block {
53
48
  display: block;
@@ -103,7 +103,8 @@ export function tooltip(anchorEl, fn) {
103
103
  anchorEl.setAttribute("aria-describedby", id);
104
104
  anchorEl.setAttribute("aria-expanded", "false");
105
105
  const debug = (...args) => {
106
- do_debug && console.debug("[tooltip]", rnd, ...args);
106
+ if (do_debug)
107
+ console.debug("[tooltip]", rnd, ...args);
107
108
  };
108
109
  function ensure_tooltip() {
109
110
  debug("ensure_tooltip()", content, classTooltip);
@@ -132,7 +133,7 @@ export function tooltip(anchorEl, fn) {
132
133
  // update position-area in case it changed
133
134
  tooltipEl.style.setProperty("position-area", POSITION_MAP[position] || "top");
134
135
  if (classTooltip) {
135
- let old = tooltipEl.className;
136
+ const old = tooltipEl.className;
136
137
  tooltipEl.className = ""; // reset
137
138
  tooltipEl.classList.add(...twMerge(old, classTooltip).split(/\s/));
138
139
  }
@@ -201,12 +202,12 @@ export function tooltip(anchorEl, fn) {
201
202
  }
202
203
  // "reactive" params re/set
203
204
  $effect(() => {
204
- let { enabled: _enabled, content: _content, position: _position, debug: debugParam, class: _classTooltip, onShow, onHide, } = fn?.() || {};
205
+ const { enabled: _enabled, content: _content, position: _position, debug: debugParam, class: _classTooltip, onShow, onHide, } = fn?.() || {};
205
206
  // re/assign new params
206
207
  do_debug = !!debugParam;
207
208
  on_show = onShow;
208
209
  on_hide = onHide;
209
- content = _content ||= anchorEl.getAttribute("aria-label");
210
+ content = _content || anchorEl.getAttribute("aria-label");
210
211
  classTooltip = _classTooltip;
211
212
  enabled = _enabled ?? true;
212
213
  position = _position || "top";
@@ -22,5 +22,5 @@
22
22
  */
23
23
  export declare function trim(el: HTMLInputElement | HTMLTextAreaElement, fn?: () => {
24
24
  enabled?: boolean;
25
- setValue?: (v: string) => any;
25
+ setValue?: (v: string) => void;
26
26
  }): void;
@@ -24,8 +24,8 @@ export function trim(el, fn) {
24
24
  // the node has been mounted in the DOM
25
25
  $effect(() => {
26
26
  // setup goes here
27
- let { enabled, setValue } = fn?.() || { enabled: true };
28
- function trim(e) {
27
+ const { enabled, setValue } = fn?.() || { enabled: true };
28
+ function trim() {
29
29
  if (enabled && typeof el.value === "string") {
30
30
  el.value = el.value.trim();
31
31
  setValue?.(el.value);
@@ -71,14 +71,14 @@ export interface ValidationResult {
71
71
  valid: boolean;
72
72
  message: string;
73
73
  }
74
- type ReasonTranslate = (reason: keyof ValidityStateFlags, value: any, fallback: string) => string;
74
+ type ReasonTranslate = (reason: keyof ValidityStateFlags, value: unknown, fallback: string) => string;
75
75
  /**
76
76
  * Options for the validate action.
77
77
  */
78
78
  export interface ValidateOptions {
79
79
  enabled?: boolean;
80
- context?: Record<string, any>;
81
- customValidator?: (value: any, context: Record<string, any> | undefined, el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement) => string | undefined;
80
+ context?: Record<string, unknown>;
81
+ customValidator?: (value: unknown, context: Record<string, unknown> | undefined, el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement) => string | undefined;
82
82
  on?: "input" | "change";
83
83
  setValidationResult?: (res: ValidationResult) => void;
84
84
  t?: false | ReasonTranslate;
@@ -148,6 +148,6 @@ export interface ValidateOptions {
148
148
  */
149
149
  export declare function validate(el: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement, fn?: () => boolean | ValidateOptions): void;
150
150
  export declare namespace validate {
151
- var t: null;
151
+ var t: ReasonTranslate | null;
152
152
  }
153
153
  export {};
@@ -1,5 +1,3 @@
1
- import { tick } from "svelte";
2
- import { waitForNextRepaint, waitForTwoRepaints } from "../utils/paint.js";
3
1
  /**
4
2
  * Creates a ValidityStateLike object for testing/mocking validation states.
5
3
  *
@@ -133,8 +131,8 @@ const KNOWN_REASONS = [
133
131
  export function validate(el, fn) {
134
132
  $effect(() => {
135
133
  //
136
- let fnResult = fn?.() ?? {};
137
- let { enabled, context, customValidator, on = "change", setValidationResult, t, } = typeof fnResult === "boolean" ? { enabled: !!fnResult } : fnResult;
134
+ const fnResult = fn?.() ?? {};
135
+ const { enabled, context, customValidator, on = "change", setValidationResult, t, } = typeof fnResult === "boolean" ? { enabled: !!fnResult } : fnResult;
138
136
  //
139
137
  const _t = (reason, value, fallback) => {
140
138
  // explicit false
@@ -187,10 +185,13 @@ export function validate(el, fn) {
187
185
  el.addEventListener(on, _doValidate);
188
186
  //
189
187
  let _touchCount = 0;
190
- const onFocus = (e) => _touchCount++;
188
+ const onFocus = () => _touchCount++;
191
189
  el.addEventListener("focus", onFocus);
192
190
  // also validate on first blur
193
- const onBlur = (e) => _touchCount === 1 && _doValidate();
191
+ const onBlur = () => {
192
+ if (_touchCount === 1)
193
+ _doValidate();
194
+ };
194
195
  el.addEventListener("blur", onBlur);
195
196
  return () => {
196
197
  el.removeEventListener(on, _doValidate);
@@ -199,6 +200,5 @@ export function validate(el, fn) {
199
200
  };
200
201
  });
201
202
  }
202
- // ReasonTranslate
203
- const t = null;
204
- validate.t = t;
203
+ // Global translation function - can be set by consumers
204
+ validate.t = null;
@@ -1,3 +1,4 @@
1
+ import type { Component } from "svelte";
1
2
  import type { THC } from "../Thc/Thc.svelte";
2
3
  /**
3
4
  * Types of alert/confirm/prompt dialogs.
@@ -12,13 +13,13 @@ export declare enum AlertConfirmPromptType {
12
13
  */
13
14
  export type AlertConfirmPromptVariant = "info" | "success" | "warn" | "error";
14
15
  /** Callback type for OK button click. */
15
- export type FnOnOK = (value: any) => any;
16
+ export type FnOnOK = (value: any) => void;
16
17
  /** Callback type for Cancel button click. */
17
- export type FnOnCancel = (value: false) => any;
18
+ export type FnOnCancel = (value: false) => void;
18
19
  /** Callback type for Escape key press. */
19
20
  export type FnOnEscape = () => void;
20
21
  /** Callback type for custom button click. */
21
- export type FnOnCustom = (value: any) => any;
22
+ export type FnOnCustom = (value: any) => void;
22
23
  /**
23
24
  * Configuration object for an alert/confirm/prompt dialog.
24
25
  */
@@ -38,9 +39,9 @@ export interface AlertConfirmPromptObj extends Record<string, any> {
38
39
  variant: AlertConfirmPromptVariant;
39
40
  iconFn: (() => string) | boolean;
40
41
  forceAsHtml?: boolean;
41
- CmpButtonOk?: any;
42
- CmpButtonCancel?: any;
43
- CmpButtonCustom?: any;
42
+ CmpButtonOk?: Component;
43
+ CmpButtonCancel?: Component;
44
+ CmpButtonCustom?: Component;
44
45
  }
45
46
  /**
46
47
  * A reactive stack manager for alert, confirm, and prompt dialogs.
@@ -1,5 +1,4 @@
1
- import { createClog } from "@marianmeres/clog";
2
- const clog = createClog("alert-confirm-prompt-stack").debug;
1
+ // const clog = createClog("alert-confirm-prompt-stack").debug;
3
2
  /**
4
3
  * Types of alert/confirm/prompt dialogs.
5
4
  */