@marianmeres/stuic 2.23.0 → 2.26.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.
@@ -1,5 +1,31 @@
1
1
  import type { THC } from "../../components/Thc/Thc.svelte";
2
2
  import "./index.css";
3
+ /**
4
+ * Open a popover by its registered ID.
5
+ *
6
+ * @param id - The popover ID to open
7
+ *
8
+ * @example
9
+ * ```svelte
10
+ * <script>
11
+ * import { openPopover } from './';
12
+ * </script>
13
+ * <button onclick={() => openPopover('my-popover')}>Open</button>
14
+ * ```
15
+ */
16
+ export declare function openPopover(id: string): void;
17
+ /**
18
+ * Close a popover by its registered ID.
19
+ *
20
+ * @param id - The popover ID to close
21
+ */
22
+ export declare function closePopover(id: string): void;
23
+ /**
24
+ * Toggle a popover by its registered ID.
25
+ *
26
+ * @param id - The popover ID to toggle
27
+ */
28
+ export declare function togglePopover(id: string): void;
3
29
  /**
4
30
  * Checks if the browser supports CSS Anchor Positioning for popovers.
5
31
  *
@@ -65,6 +91,10 @@ export interface PopoverOptions {
65
91
  onHide?: () => void;
66
92
  /** Debug mode */
67
93
  debug?: boolean;
94
+ /** Programmatically control open state (reactive) */
95
+ open?: boolean;
96
+ /** Unique ID for registry-based programmatic control (use with openPopover/closePopover/togglePopover) */
97
+ id?: string;
68
98
  }
69
99
  /**
70
100
  * A Svelte action that displays a popover anchored to an element using CSS Anchor Positioning.
@@ -6,6 +6,54 @@ import PopoverContent from "./PopoverContent.svelte";
6
6
  import "./index.css";
7
7
  // Registry of open popover hide functions for closeOthers feature
8
8
  const openPopovers = new Set();
9
+ // Registry of popovers by ID for programmatic control
10
+ const popoverRegistry = new Map();
11
+ // Track if an open was just requested (to prevent same-click outside close)
12
+ let openRequestedThisCycle = false;
13
+ /**
14
+ * Open a popover by its registered ID.
15
+ *
16
+ * @param id - The popover ID to open
17
+ *
18
+ * @example
19
+ * ```svelte
20
+ * <script>
21
+ * import { openPopover } from './';
22
+ * </script>
23
+ * <button onclick={() => openPopover('my-popover')}>Open</button>
24
+ * ```
25
+ */
26
+ export function openPopover(id) {
27
+ const entry = popoverRegistry.get(id);
28
+ if (entry) {
29
+ openRequestedThisCycle = true;
30
+ setTimeout(() => {
31
+ openRequestedThisCycle = false;
32
+ }, 0);
33
+ // Close all other open popovers first
34
+ openPopovers.forEach((hideFn) => {
35
+ if (hideFn !== entry.hide)
36
+ hideFn();
37
+ });
38
+ entry.show();
39
+ }
40
+ }
41
+ /**
42
+ * Close a popover by its registered ID.
43
+ *
44
+ * @param id - The popover ID to close
45
+ */
46
+ export function closePopover(id) {
47
+ popoverRegistry.get(id)?.hide();
48
+ }
49
+ /**
50
+ * Toggle a popover by its registered ID.
51
+ *
52
+ * @param id - The popover ID to toggle
53
+ */
54
+ export function togglePopover(id) {
55
+ popoverRegistry.get(id)?.toggle();
56
+ }
9
57
  const SHOW_DELAY = 100;
10
58
  const HIDE_DELAY = 200;
11
59
  const TRANSITION = 200;
@@ -155,6 +203,7 @@ export function popover(anchorEl, fn) {
155
203
  let hideTimer = null;
156
204
  let isVisible = false;
157
205
  let do_debug = false;
206
+ let prevOpen = undefined;
158
207
  // Unique identifiers
159
208
  const rnd = Math.random().toString(36).slice(2);
160
209
  const id = `popover-${rnd}`;
@@ -200,11 +249,20 @@ export function popover(anchorEl, fn) {
200
249
  if (popoverEl &&
201
250
  !popoverEl.contains(e.target) &&
202
251
  !anchorEl.contains(e.target)) {
252
+ // Skip if an open was just requested via registry (same click event)
253
+ if (openRequestedThisCycle) {
254
+ return;
255
+ }
203
256
  hide();
204
257
  }
205
258
  }
206
259
  function onClickTrigger(e) {
207
260
  e.stopPropagation();
261
+ // Close all other open popovers (since stopPropagation prevents onClickOutside from firing)
262
+ openPopovers.forEach((hideFn) => {
263
+ if (hideFn !== hide)
264
+ hideFn();
265
+ });
208
266
  if (isVisible)
209
267
  hide();
210
268
  else
@@ -408,8 +466,22 @@ export function popover(anchorEl, fn) {
408
466
  onShow: opts.onShow,
409
467
  onHide: opts.onHide,
410
468
  debug: opts.debug,
469
+ id: opts.id,
411
470
  };
412
471
  do_debug = !!opts.debug;
472
+ // Register in global registry if id provided
473
+ if (opts.id) {
474
+ popoverRegistry.set(opts.id, {
475
+ show,
476
+ hide,
477
+ toggle: () => {
478
+ if (isVisible)
479
+ hide();
480
+ else
481
+ show();
482
+ },
483
+ });
484
+ }
413
485
  // Update popover if visible
414
486
  if (isVisible && popoverEl) {
415
487
  // Update position (only in anchor positioning mode)
@@ -421,6 +493,17 @@ export function popover(anchorEl, fn) {
421
493
  }
422
494
  // Note: trigger mode change while visible is not fully handled
423
495
  // User should close and reopen for trigger mode change to take effect
496
+ // Handle programmatic open/close
497
+ const openValue = opts.open;
498
+ if (openValue !== undefined && openValue !== prevOpen) {
499
+ if (openValue && !isVisible) {
500
+ show();
501
+ }
502
+ else if (!openValue && isVisible) {
503
+ hide();
504
+ }
505
+ }
506
+ prevOpen = openValue;
424
507
  });
425
508
  // Event listeners effect
426
509
  $effect(() => {
@@ -461,6 +544,10 @@ export function popover(anchorEl, fn) {
461
544
  clearTimers();
462
545
  // Unregister from open popovers
463
546
  openPopovers.delete(hide);
547
+ // Unregister from popover registry
548
+ if (currentOptions.id) {
549
+ popoverRegistry.delete(currentOptions.id);
550
+ }
464
551
  document.removeEventListener("keydown", onEscape);
465
552
  document.removeEventListener("click", onClickOutside);
466
553
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marianmeres/stuic",
3
- "version": "2.23.0",
3
+ "version": "2.26.0",
4
4
  "files": [
5
5
  "dist",
6
6
  "!dist/**/*.test.*",