fix: Add editor.view null checks to prevent click errors

Added defensive checks for editor.view across editor components to prevent
"Cannot read properties of undefined (reading 'posAtCoords')" errors when
clicking before editor is fully initialized.

Fixed in:
- focusEditor utility function
- ComposerLinkManager dropdown handlers
- bubble-menu event listeners and shouldShow
- SearchAndReplace goToSelection
This commit is contained in:
Justin Edmund 2025-11-24 07:42:50 -08:00
parent 0b46ebd433
commit 7c08daffe8
4 changed files with 18 additions and 16 deletions

View file

@ -32,7 +32,7 @@
// URL convert handlers
export function handleShowUrlConvertDropdown(pos: number, _url: string) {
if (!editor) return
if (!editor || !editor.view) return
const coords = editor.view.coordsAtPos(pos)
urlConvertDropdownPosition = { x: coords.left, y: coords.bottom + 5 }
urlConvertPos = pos
@ -48,7 +48,7 @@
// Link context menu handlers
export function handleShowLinkContextMenu(pos: number, url: string) {
if (!editor) return
if (!editor || !editor.view) return
const coords = editor.view.coordsAtPos(pos)
linkContextMenuPosition = { x: coords.left, y: coords.bottom + 5 }
linkContextUrl = url
@ -65,7 +65,7 @@
}
function handleEditLink() {
if (!editor || linkContextPos === null || !linkContextUrl) return
if (!editor || !editor.view || linkContextPos === null || !linkContextUrl) return
const coords = editor.view.coordsAtPos(linkContextPos)
linkEditDialogPosition = { x: coords.left, y: coords.bottom + 5 }
linkEditUrl = linkContextUrl

View file

@ -32,7 +32,7 @@
function goToSelection() {
const { results, resultIndex } = editor.storage.searchAndReplace
const position = results[resultIndex]
if (!position) return
if (!position || !editor.view) return
editor.commands.setTextSelection(position)
const { node } = editor.view.domAtPos(editor.state.selection.anchor)
if (node instanceof HTMLElement) node.scrollIntoView({ behavior: 'smooth', block: 'center' })

View file

@ -15,18 +15,20 @@
let isDragging = $state(false)
editor.view.dom.addEventListener('dragstart', () => {
isDragging = true
})
if (editor.view) {
editor.view.dom.addEventListener('dragstart', () => {
isDragging = true
})
editor.view.dom.addEventListener('drop', () => {
isDragging = true
editor.view.dom.addEventListener('drop', () => {
isDragging = true
// Allow some time for the drop action to complete before re-enabling
setTimeout(() => {
isDragging = false
}, 100) // Adjust delay if needed
})
// Allow some time for the drop action to complete before re-enabling
setTimeout(() => {
isDragging = false
}, 100) // Adjust delay if needed
})
}
const bubbleMenuCommands = [
...commands['text-formatting'].commands,
@ -40,7 +42,7 @@
function shouldShow(props: ShouldShowProps) {
if (!props.editor.isEditable) return false
const { view, editor } = props
if (!view || editor.view.dragging) {
if (!view || !editor.view || editor.view.dragging) {
return false
}
if (editor.isActive('link')) return false

View file

@ -114,7 +114,7 @@ export function getHandlePaste(editor: Editor, maxSize: number = 2) {
* @param event - Optional MouseEvent or KeyboardEvent triggering the focus
*/
export function focusEditor(editor: Editor | undefined, event?: MouseEvent | KeyboardEvent) {
if (!editor) return
if (!editor || !editor.view) return
// Check if there is a text selection already (i.e. a non-empty selection)
const selection = window.getSelection()
if (selection && selection.toString().length > 0) {