diff --git a/src/lib/utils/Popper.svelte b/src/lib/utils/Popper.svelte
index 24927541d..1cd19a682 100644
--- a/src/lib/utils/Popper.svelte
+++ b/src/lib/utils/Popper.svelte
@@ -34,6 +34,9 @@
const dispatch = createEventDispatcher();
+ let focusable: boolean;
+ $: focusable = trigger === 'focus';
+
let clickable: boolean;
$: clickable = trigger === 'click';
@@ -49,32 +52,34 @@
let contentEl: HTMLElement;
let triggerEls: HTMLElement[] = [];
- let _blocked: boolean = false; // management of the race condition between focusin and click events
- const block = () => ((_blocked = true), setTimeout(() => (_blocked = false), 250));
-
const showHandler = (ev: Event) => {
if (referenceEl === undefined) console.error('trigger undefined');
if (!reference && triggerEls.includes(ev.target as HTMLElement) && referenceEl !== ev.target) {
referenceEl = ev.target as HTMLElement;
- block();
+ if (open) return; // If the popper is already open after the reference element has changed
}
- if (clickable && ev.type === 'focusin' && !open) block();
- open = clickable && ev.type === 'click' && !_blocked ? !open : true;
+
+ open = ev.type === 'click' ? !open : true;
};
const hasHover = (el: Element) => el.matches(':hover');
const hasFocus = (el: Element) => el.contains(document.activeElement);
- const px = (n: number | undefined) => (n != null ? `${n}px` : '');
+ const px = (n: number | undefined) => (n ? `${n}px` : '');
const hideHandler = (ev: Event) => {
- if (activeContent) {
+ if (activeContent && hoverable) {
+ const elements = [referenceEl, floatingEl, ...triggerEls].filter(Boolean);
+ // Add a delay before hiding the floating element to account for hoverable elements.
+ // This ensures that the floating element does not hide immediately when the mouse
+ // moves from the reference element to the floating element.
setTimeout(() => {
- const elements = [referenceEl, floatingEl, ...triggerEls].filter(Boolean);
- if (ev.type === 'mouseleave' && elements.some(hasHover)) return;
- if (ev.type === 'focusout' && elements.some(hasFocus)) return;
- open = false;
+ if (ev.type === 'mouseleave' && !elements.some(hasHover)) {
+ open = false;
+ }
}, 100);
- } else open = false;
+ } else {
+ open = false;
+ }
};
let arrowSide: Side;
@@ -120,8 +125,8 @@
onMount(() => {
const events: [string, any, boolean][] = [
- ['focusin', showHandler, true],
- ['focusout', hideHandler, true],
+ ['focusin', showHandler, focusable],
+ ['focusout', hideHandler, focusable],
['click', showHandler, clickable],
['mouseenter', showHandler, hoverable],
['mouseleave', hideHandler, hoverable]
@@ -144,14 +149,14 @@
if (referenceEl === document.body) {
console.error(`Popup reference not found: '${reference}'`);
} else {
- referenceEl.addEventListener('focusout', hideHandler);
+ if (focusable) referenceEl.addEventListener('focusout', hideHandler);
if (hoverable) referenceEl.addEventListener('mouseleave', hideHandler);
}
} else {
referenceEl = triggerEls[0];
}
- document.addEventListener('click', closeOnClickOutside);
+ if (clickable) document.addEventListener('click', closeOnClickOutside);
return () => {
// This is onDestroy function
@@ -160,10 +165,12 @@
for (const [name, handler] of events) element.removeEventListener(name, handler);
}
});
+
if (referenceEl) {
referenceEl.removeEventListener('focusout', hideHandler);
referenceEl.removeEventListener('mouseleave', hideHandler);
}
+
document.removeEventListener('click', closeOnClickOutside);
};
});
@@ -202,7 +209,7 @@
{/if}
{#if referenceEl}
-
+