fix(editor): render floating popups inside the task dialog (Kanban popup)
The Kanban task detail opens as a native <dialog> via showModal(), which paints in the browser top-layer. Floating UI appended to document.body (or teleported to <body>) then renders behind the dialog regardless of z-index, matching the bug class of #2940 / #1746 / #1899 / #1929. - Emoji autocomplete popup: append to getPopupContainer(editor) (the open dialog ancestor, else body), the same helper the slash menu and mentions already use. Also switch its unmount to popupElement.remove() so it works no matter which container it was appended to. - Attachment dropzone overlay: teleport into the topmost open dialog.modal-dialog instead of always <body>, mirroring Notification.vue, so the drag-and-drop hint is visible while a task detail dialog is open.
This commit is contained in:
parent
b6af132845
commit
ffea278be1
|
|
@ -5,6 +5,7 @@ import {PluginKey, type EditorState} from '@tiptap/pm/state'
|
|||
|
||||
import EmojiList from './EmojiList.vue'
|
||||
import {loadEmojis, filterEmojis, type EmojiEntry} from './emojiData'
|
||||
import {getPopupContainer} from '../popupContainer'
|
||||
|
||||
export const EmojiSuggestionPluginKey = new PluginKey('emojiSuggestion')
|
||||
|
||||
|
|
@ -78,7 +79,7 @@ export default function emojiSuggestionSetup() {
|
|||
popupElement.style.left = '0'
|
||||
popupElement.style.zIndex = '4700'
|
||||
popupElement.appendChild(component.element!)
|
||||
document.body.appendChild(popupElement)
|
||||
getPopupContainer(props.editor).appendChild(popupElement)
|
||||
|
||||
const rect = props.clientRect()
|
||||
if (!rect) {
|
||||
|
|
@ -108,7 +109,7 @@ export default function emojiSuggestionSetup() {
|
|||
cleanupFloating = null
|
||||
}
|
||||
if (popupElement) {
|
||||
document.body.removeChild(popupElement)
|
||||
popupElement.remove()
|
||||
popupElement = null
|
||||
}
|
||||
component?.destroy()
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@
|
|||
</XButton>
|
||||
|
||||
<!-- Dropzone -->
|
||||
<Teleport to="body">
|
||||
<Teleport :to="dropzoneTeleportTarget">
|
||||
<div
|
||||
v-if="editEnabled"
|
||||
:class="{hidden: !showDropzone}"
|
||||
|
|
@ -185,7 +185,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {ref, shallowReactive, computed, watch} from 'vue'
|
||||
import {ref, shallowReactive, computed, watch, onMounted, onBeforeUnmount} from 'vue'
|
||||
import {useDropZone} from '@vueuse/core'
|
||||
|
||||
import User from '@/components/misc/User.vue'
|
||||
|
|
@ -322,6 +322,34 @@ const showDropzone = computed(() =>
|
|||
props.editEnabled && isDraggingFiles.value && !isDragOverEditor.value,
|
||||
)
|
||||
|
||||
// A <dialog> opened with showModal() (e.g. the Kanban task detail) renders in
|
||||
// the browser's top layer, so the full-screen dropzone overlay teleported to
|
||||
// <body> would paint behind it regardless of z-index. Teleport it into the
|
||||
// topmost open dialog instead, mirroring Notification.vue.
|
||||
const dropzoneTeleportTarget = ref<string | HTMLElement>('body')
|
||||
let dialogObserver: MutationObserver | null = null
|
||||
|
||||
function syncDropzoneTeleportTarget() {
|
||||
const dialogs = document.querySelectorAll<HTMLDialogElement>('dialog.modal-dialog[open]')
|
||||
dropzoneTeleportTarget.value = dialogs.item(dialogs.length - 1) ?? 'body'
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
syncDropzoneTeleportTarget()
|
||||
dialogObserver = new MutationObserver(syncDropzoneTeleportTarget)
|
||||
dialogObserver.observe(document.body, {
|
||||
attributes: true,
|
||||
attributeFilter: ['open'],
|
||||
childList: true,
|
||||
subtree: true,
|
||||
})
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
dialogObserver?.disconnect()
|
||||
dialogObserver = null
|
||||
})
|
||||
|
||||
watch(() => props.editEnabled, enabled => {
|
||||
if (!enabled) {
|
||||
resetDragState()
|
||||
|
|
@ -478,7 +506,7 @@ defineExpose({
|
|||
inset-inline-start: 0;
|
||||
inset-block-end: 0;
|
||||
inset-inline-end: 0;
|
||||
z-index: 4001; // modal z-index is 4000
|
||||
z-index: 4001; // above app chrome when teleported to body (no modal open)
|
||||
text-align: center;
|
||||
|
||||
&.hidden {
|
||||
|
|
|
|||
Loading…
Reference in New Issue