Skip to content

Commit 4fad658

Browse files
kolaenteclaude
andcommitted
fix: prevent emoji picker from closing when clicking search input
The emoji picker (vuemoji-picker) uses emoji-picker-element which renders its internal elements inside Shadow DOM. The closeWhenClickedOutside helper was using parentElement traversal which doesn't work across shadow boundaries, causing clicks on the search input to incorrectly trigger the close callback. This fix: - Uses event.composedPath() instead of parentElement traversal, which properly includes elements inside Shadow DOM - Updates Reactions.vue to pass the actual DOM element ($el) instead of the Vue component instance 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent c1f789c commit 4fad658

File tree

2 files changed

+7
-16
lines changed

2 files changed

+7
-16
lines changed

frontend/src/components/input/Reactions.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,11 @@ function getReactionTooltip(users: IUser[], value: string | number) {
9696
}
9797
9898
const showEmojiPicker = ref(false)
99-
const emojiPickerRef = ref<HTMLElement | null>(null)
99+
const emojiPickerRef = ref<InstanceType<typeof VuemojiPicker> | null>(null)
100100
101101
function hideEmojiPicker(e: MouseEvent) {
102-
if (showEmojiPicker.value && emojiPickerRef.value) {
103-
closeWhenClickedOutside(e, emojiPickerRef.value, () => showEmojiPicker.value = false)
102+
if (showEmojiPicker.value && emojiPickerRef.value?.$el) {
103+
closeWhenClickedOutside(e, emojiPickerRef.value.$el, () => showEmojiPicker.value = false)
104104
}
105105
}
106106

frontend/src/helpers/closeWhenClickedOutside.ts

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,11 @@
66
* @param closeCallback A closure function to call when the click event happened outside of the rootElement.
77
*/
88
export const closeWhenClickedOutside = (event: MouseEvent, rootElement: HTMLElement, closeCallback: () => void) => {
9-
// We walk up the tree to see if any parent of the clicked element is the root element.
10-
// If it is not, we call the close callback. We're doing all this hassle to only call the
11-
// closing callback when a click happens outside of the rootElement.
12-
let parent = (event.target as HTMLElement)?.parentElement
13-
while (parent !== rootElement) {
14-
if (parent === null || parent.parentElement === null) {
15-
parent = null
16-
break
17-
}
9+
// Use composedPath() to get the full event path including elements inside Shadow DOM.
10+
// This ensures clicks inside shadow roots (like emoji-picker-element) are detected correctly.
11+
const path = event.composedPath()
1812

19-
parent = parent.parentElement
20-
}
21-
22-
if (parent === rootElement) {
13+
if (path.includes(rootElement)) {
2314
return
2415
}
2516

0 commit comments

Comments
 (0)