Skip to content

Commit e708529

Browse files
committed
fix: #1626
fix: Popper to clickable content for Popover and Dropdown
1 parent 6a06167 commit e708529

File tree

3 files changed

+45
-14
lines changed

3 files changed

+45
-14
lines changed

src/lib/utils/Popper.svelte

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
let focusable: boolean = true;
1313
let clickable: boolean = $derived(trigger === "click");
1414
let hoverable: boolean = $derived(trigger === "hover");
15+
1516
let popover: HTMLElement | null = $state(null);
1617
let invoker: HTMLElement | null = null;
1718
let referenceElement: HTMLElement | null = null;
@@ -54,7 +55,9 @@
5455
// throttle
5556
isTriggered = true;
5657
await new Promise((resolve) => setTimeout(resolve, triggerDelay));
57-
if (!isTriggered) return;
58+
if (!isTriggered) {
59+
return;
60+
}
5861
5962
ev.preventDefault();
6063
@@ -73,13 +76,34 @@
7376
}
7477
7578
async function close_popover(ev: Event) {
79+
// For click triggers, don't close on focusout events from inside the popover
80+
if (trigger === "click" && ev.type === "focusout") {
81+
const relatedTarget = (ev as FocusEvent).relatedTarget as HTMLElement;
82+
83+
// If focus is moving to somewhere inside the popover, don't close
84+
if (popover && relatedTarget && popover.contains(relatedTarget)) {
85+
return;
86+
}
87+
88+
// If focus is moving to nowhere (like when clicking), don't close for click triggers
89+
if (!relatedTarget) {
90+
return;
91+
}
92+
}
93+
7694
isTriggered = false;
7795
await new Promise((resolve) => setTimeout(resolve, triggerDelay));
78-
if (isTriggered) return;
96+
if (isTriggered) {
97+
return;
98+
}
7999
80100
// if popover has focus don't close when leaving the invoker
81-
if (ev?.type === "mouseleave" && popover?.contains(popover.ownerDocument.activeElement)) return;
82-
if (ev?.type === "focusout" && popover?.contains(popover.ownerDocument.activeElement)) return;
101+
if (ev?.type === "mouseleave" && popover?.contains(popover.ownerDocument.activeElement)) {
102+
return;
103+
}
104+
if (ev?.type === "focusout" && popover?.contains(popover.ownerDocument.activeElement)) {
105+
return;
106+
}
83107
84108
isOpen = false;
85109
}
@@ -97,11 +121,11 @@
97121
if (ev.newState === "open") {
98122
autoUpdateDestroy = dom.autoUpdate(referenceElement ?? invoker, popover, updatePopoverPosition);
99123
popover.ownerDocument.addEventListener("click", closeOnClickOutside);
100-
popover.ownerDocument.addEventListener("keydown", closeOnEscape); // ✅ Add this line
124+
popover.ownerDocument.addEventListener("keydown", closeOnEscape);
101125
} else {
102126
autoUpdateDestroy();
103127
popover.ownerDocument.removeEventListener("click", closeOnClickOutside);
104-
popover.ownerDocument.removeEventListener("keydown", closeOnEscape); // ✅ Add this line
128+
popover.ownerDocument.removeEventListener("keydown", closeOnEscape);
105129
}
106130
}
107131
@@ -156,14 +180,19 @@
156180
}
157181
}
158182
159-
/**
160-
* Close the popper when clicking outside of it.
161-
* This is necessary to get around a bug in Safari where clicking outside of the open popper does not close it.
162-
*/
163183
function closeOnClickOutside(event: MouseEvent) {
164-
if (popover && !event.composedPath().includes(popover) && !triggerEls.some((el) => event.composedPath().includes(el))) {
184+
if (!popover) {
185+
return;
186+
}
187+
188+
const clickPath = event.composedPath();
189+
190+
const isClickInsidePopover = clickPath.includes(popover);
191+
const isClickOnTrigger = triggerEls.some((el) => clickPath.includes(el));
192+
193+
// Only close if click is outside both popover and trigger elements
194+
if (!isClickInsidePopover && !isClickOnTrigger) {
165195
close_popover(event);
166-
// Update isOpen state when clicking outside
167196
isOpen = false;
168197
}
169198
}
@@ -204,4 +233,4 @@
204233
@prop transition = fade
205234
@prop children
206235
@prop ...restProps
207-
-->
236+
-->

src/routes/docs/components/mega-menu.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ Since the `MegaMenu` component extends `Popper`, it also supports the `transitio
169169
{/snippet}
170170
</MegaMenu>
171171
<NavLi class="cursor-pointer" onclick={() => (open = true)}>
172-
Blur<ChevronDownOutline class="text-primary-800 ms-2 inline h-6 w-6 dark:text-white" />
172+
Scale<ChevronDownOutline class="text-primary-800 ms-2 inline h-6 w-6 dark:text-white" />
173173
</NavLi>
174174
<MegaMenu items={menu} transition={scale} transitionParams={{ duration: 1000 }}>
175175
{#snippet children({ item })}

src/routes/docs/components/tooltip.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ thumnailSize: w-24
1717

1818
flowbite-svelte allows you to show extra information when hovering or focusing over an element in multiple positions, styles, and animations.
1919

20+
For interactive elements that need to display additional content on click, use the [popover](/docs/components/popover) component.
21+
2022
## Setup
2123

2224
```svelte example hideOutput

0 commit comments

Comments
 (0)