fix: 块编辑器输入斜杠无法弹出slash菜单

This commit is contained in:
jingrow 2026-06-03 02:54:08 +08:00
parent 609393867d
commit 2aa3fa507b

View File

@ -66,9 +66,26 @@ const key = new PluginKey(props.pluginKey)
let renderer: VueRenderer | null = null
let isRegistered = false
// Click-outside-to-close handler
/** Dismiss the suggestion when a mousedown lands outside both the editor
* and the popup menu. This matches the standard UX pattern. */
function onDocumentMousedown(event: MouseEvent) {
if (!renderer?.el) return
const target = event.target as Node
// Click on the menu itself allow interaction (keep open)
if (renderer.el.contains(target)) return
// Any click outside the menu dismiss.
// Defer to avoid interfering with ProseMirror's click handling.
setTimeout(() => close(), 0)
}
function onCreateHandler() {
register()
}
function register() {
const editor = props.editor
if (!editor || !editor.isInitialized) return
if (!editor) return
if (isRegistered) return
const plugin = Suggestion({
@ -95,13 +112,15 @@ function register() {
if (!renderer) {
renderer = new VueRenderer(markRaw(props.component), {
editor: suggestionProps.editor,
props: suggestionProps,
})
}
const container = suggestionProps.editor.view.dom.closest('.blockeditor-mount')
if (container && renderer.element) {
container.appendChild(renderer.element)
if (container && renderer.el) {
container.appendChild(renderer.el)
}
renderer.updateProps(suggestionProps)
document.addEventListener('mousedown', onDocumentMousedown)
},
onUpdate: (suggestionProps: SuggestionProps) => {
@ -109,6 +128,7 @@ function register() {
},
onExit: () => {
document.removeEventListener('mousedown', onDocumentMousedown)
renderer?.destroy()
renderer = null
},
@ -128,7 +148,12 @@ function register() {
}
function unregister() {
if (!props.editor || !isRegistered) return
if (!props.editor) return
if (!isRegistered) {
// Even if not registered, remove the event listener to avoid leaks
try { props.editor.off('create', onCreateHandler) } catch { /* ignore */ }
return
}
if (renderer) {
renderer.destroy()
renderer = null
@ -140,21 +165,22 @@ function unregister() {
}
watch(
() => props.editor?.isInitialized,
(initialized) => {
if (initialized && props.editor) register()
() => props.editor,
(editor, oldEditor) => {
if (oldEditor && oldEditor !== editor) unregister()
if (!editor) return
if (editor.isInitialized) {
register()
} else {
// TipTap v3 sets isInitialized = true inside setTimeout(0),
// so Vue's watch cannot detect the change on this non-reactive property.
// We listen for the editor's 'create' event instead.
editor.on('create', onCreateHandler, { once: true })
}
},
{ immediate: true },
)
watch(
() => props.editor,
(newEditor, oldEditor) => {
if (oldEditor && oldEditor !== newEditor) unregister()
if (newEditor && newEditor.isInitialized) register()
},
)
onBeforeUnmount(() => { unregister() })
function close() {