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

View file

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

View file

@ -15,6 +15,7 @@
let isDragging = $state(false) let isDragging = $state(false)
if (editor.view) {
editor.view.dom.addEventListener('dragstart', () => { editor.view.dom.addEventListener('dragstart', () => {
isDragging = true isDragging = true
}) })
@ -27,6 +28,7 @@
isDragging = false isDragging = false
}, 100) // Adjust delay if needed }, 100) // Adjust delay if needed
}) })
}
const bubbleMenuCommands = [ const bubbleMenuCommands = [
...commands['text-formatting'].commands, ...commands['text-formatting'].commands,
@ -40,7 +42,7 @@
function shouldShow(props: ShouldShowProps) { function shouldShow(props: ShouldShowProps) {
if (!props.editor.isEditable) return false if (!props.editor.isEditable) return false
const { view, editor } = props const { view, editor } = props
if (!view || editor.view.dragging) { if (!view || !editor.view || editor.view.dragging) {
return false return false
} }
if (editor.isActive('link')) 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 * @param event - Optional MouseEvent or KeyboardEvent triggering the focus
*/ */
export function focusEditor(editor: Editor | undefined, event?: MouseEvent | KeyboardEvent) { 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) // Check if there is a text selection already (i.e. a non-empty selection)
const selection = window.getSelection() const selection = window.getSelection()
if (selection && selection.toString().length > 0) { if (selection && selection.toString().length > 0) {