fix: prevent emoji picker from closing when clicking search input (#1959)

Resolves
https://community.vikunja.io/t/cannot-search-for-reaction-emoji/4207/1

- Fixes the emoji reaction picker closing immediately when clicking
inside the search box
- The issue occurred because `vuemoji-picker` wraps
`emoji-picker-element` which uses Shadow DOM
- The `closeWhenClickedOutside` helper was using `parentElement`
traversal which doesn't cross shadow boundaries
This commit is contained in:
kolaente
2025-12-09 12:59:01 +01:00
committed by GitHub
parent c1f789c205
commit 798ffc0582
2 changed files with 7 additions and 16 deletions

View File

@@ -96,11 +96,11 @@ function getReactionTooltip(users: IUser[], value: string | number) {
}
const showEmojiPicker = ref(false)
const emojiPickerRef = ref<HTMLElement | null>(null)
const emojiPickerRef = ref<InstanceType<typeof VuemojiPicker> | null>(null)
function hideEmojiPicker(e: MouseEvent) {
if (showEmojiPicker.value && emojiPickerRef.value) {
closeWhenClickedOutside(e, emojiPickerRef.value, () => showEmojiPicker.value = false)
if (showEmojiPicker.value && emojiPickerRef.value?.$el) {
closeWhenClickedOutside(e, emojiPickerRef.value.$el, () => showEmojiPicker.value = false)
}
}

View File

@@ -6,20 +6,11 @@
* @param closeCallback A closure function to call when the click event happened outside of the rootElement.
*/
export const closeWhenClickedOutside = (event: MouseEvent, rootElement: HTMLElement, closeCallback: () => void) => {
// We walk up the tree to see if any parent of the clicked element is the root element.
// If it is not, we call the close callback. We're doing all this hassle to only call the
// closing callback when a click happens outside of the rootElement.
let parent = (event.target as HTMLElement)?.parentElement
while (parent !== rootElement) {
if (parent === null || parent.parentElement === null) {
parent = null
break
}
// Use composedPath() to get the full event path including elements inside Shadow DOM.
// This ensures clicks inside shadow roots (like emoji-picker-element) are detected correctly.
const path = event.composedPath()
parent = parent.parentElement
}
if (parent === rootElement) {
if (path.includes(rootElement)) {
return
}