diff --git a/web/src/lib/components/common/EmojiPickerButton.svelte b/web/src/lib/components/common/EmojiPickerButton.svelte index cd0ba5ce..e25d8f89 100644 --- a/web/src/lib/components/common/EmojiPickerButton.svelte +++ b/web/src/lib/components/common/EmojiPickerButton.svelte @@ -10,13 +10,62 @@ let { value = $bindable(), placeholder = '⚡', size = 'sm' }: Props = $props(); let open = $state(false); + let triggerEl = $state(); + let dropdownX = $state(0); + let dropdownY = $state(0); + + /** + * Portal into the nearest (stays in top-layer, escapes overflow + * children like .dialog-body), or document.body if not inside a dialog. + */ + function portal(node: HTMLElement) { + const dialog = triggerEl?.closest('dialog'); + const target = dialog || document.body; + target.appendChild(node); + + // has browser-default overflow:auto which clips children. + // Temporarily override while the dropdown is mounted. + if (dialog) { + dialog.style.overflow = 'visible'; + } + + return { + destroy() { + node.remove(); + if (dialog) { + dialog.style.overflow = ''; + } + } + }; + } function handleWindowClick(e: MouseEvent) { - if (open && !(e.target as HTMLElement)?.closest('.emoji-picker-button')) { - open = false; + if (open) { + const target = e.target as HTMLElement; + if (!target?.closest('.emoji-picker-button') && !target?.closest('.epb-dropdown')) { + open = false; + } } } + function toggleOpen() { + if (!open && triggerEl) { + const triggerRect = triggerEl.getBoundingClientRect(); + const dialog = triggerEl.closest('dialog'); + if (dialog) { + // dialog with transform creates a containing block — + // position: absolute is relative to it, so offset accordingly + const dialogRect = dialog.getBoundingClientRect(); + dropdownX = triggerRect.left - dialogRect.left; + dropdownY = triggerRect.bottom - dialogRect.top + 4; + } else { + dropdownX = triggerRect.left; + dropdownY = triggerRect.bottom + 4; + } + } + open = !open; + } + function handleSelect(emoji: string) { value = emoji; open = false; @@ -27,17 +76,18 @@
{#if open} -