diff --git a/frontend/src/components/input/editor/mention/mentionSuggestion.ts b/frontend/src/components/input/editor/mention/mentionSuggestion.ts index 0aaf756a8..1afd2f66a 100644 --- a/frontend/src/components/input/editor/mention/mentionSuggestion.ts +++ b/frontend/src/components/input/editor/mention/mentionSuggestion.ts @@ -3,6 +3,7 @@ import { computePosition, flip, shift, offset, autoUpdate } from '@floating-ui/d import type { Editor } from '@tiptap/core' import MentionList from './MentionList.vue' +import { getPopupContainer } from '../popupContainer' import ProjectUserService from '@/services/projectUsers' import { fetchAvatarBlobUrl, getDisplayName } from '@/models/user' import type { IUser } from '@/modelTypes/IUser' @@ -113,7 +114,8 @@ export default function mentionSuggestionSetup(projectId: number) { popupElement.style.left = '0' popupElement.style.zIndex = '4700' popupElement.appendChild(component.element!) - document.body.appendChild(popupElement) // Update virtual reference + getPopupContainer(props.editor).appendChild(popupElement) + // Update virtual reference const rect = props.clientRect() if (rect) { virtualReference.getBoundingClientRect = () => rect @@ -179,7 +181,7 @@ export default function mentionSuggestionSetup(projectId: number) { cleanupFloating() } if (popupElement) { - document.body.removeChild(popupElement) + popupElement.remove() popupElement = null } component.destroy() diff --git a/frontend/src/components/input/editor/popupContainer.ts b/frontend/src/components/input/editor/popupContainer.ts new file mode 100644 index 000000000..92679d936 --- /dev/null +++ b/frontend/src/components/input/editor/popupContainer.ts @@ -0,0 +1,11 @@ +import type {Editor} from '@tiptap/core' + +// Native elements opened with showModal() render in the browser's +// top-layer, so popups appended to document.body end up visually behind them +// regardless of z-index. Appending to the open dialog itself lifts the popup +// into the same top-layer stacking context. +export function getPopupContainer(editor?: Editor): HTMLElement { + const editorEl = editor?.view?.dom as HTMLElement | undefined + const dialog = editorEl?.closest('dialog[open]') as HTMLElement | null + return dialog ?? document.body +} diff --git a/frontend/src/components/input/editor/suggestion.ts b/frontend/src/components/input/editor/suggestion.ts index 151afc7f7..55146c34f 100644 --- a/frontend/src/components/input/editor/suggestion.ts +++ b/frontend/src/components/input/editor/suggestion.ts @@ -3,6 +3,7 @@ import {VueRenderer} from '@tiptap/vue-3' import {computePosition, flip, shift, offset, autoUpdate} from '@floating-ui/dom' import CommandsList from './CommandsList.vue' +import {getPopupContainer} from './popupContainer' type TranslateFunction = (key: string) => string @@ -206,7 +207,7 @@ export default function suggestionSetup(t: TranslateFunction) { popupElement.style.left = '0' popupElement.style.zIndex = '4700' popupElement.appendChild(component.element!) - document.body.appendChild(popupElement) + getPopupContainer(props.editor).appendChild(popupElement) // Update virtual reference const rect = props.clientRect() @@ -266,7 +267,7 @@ export default function suggestionSetup(t: TranslateFunction) { cleanupFloating() } if (popupElement) { - document.body.removeChild(popupElement) + popupElement.remove() popupElement = null } component.destroy()