fix: guard popper against null reference rect in composer autocomplete

The mention autocomplete passes @tiptap/suggestion's `clientRect` straight
to tippy/popper as `getReferenceClientRect`. That function can return null
once the editor tears down, and popper re-invokes it on its debounced
`instance.update` - dereferencing `clientRect.left` on null and throwing
(APP-P7: ~188k events). Wrap it to cache the last valid rect and never hand
popper a null, falling back to a zeroed DOMRect. Removes the two now-moot
@ts-ignore comments.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-06-09 16:25:22 +03:00
parent 2cb225dddd
commit 275ce7911f
@@ -40,6 +40,24 @@ export function createSuggestion({
render: () => {
let component: ReactRenderer<MentionListRef> | undefined
let popup: TippyInstance[] | undefined
// Popper holds onto getReferenceClientRect and calls it on a debounced
// update. By the time it fires the suggestion may have torn down, in
// which case props.clientRect() returns null and popper crashes on
// `clientRect.left`. Remember the last valid rect and fall back to it so
// popper always receives a real DOMRect. See APP-P7.
let lastClientRect: DOMRect | undefined
const getReferenceClientRect = (
clientRect: SuggestionProps['clientRect'],
) => {
return () => {
const rect = clientRect?.()
if (rect) {
lastClientRect = rect
}
return lastClientRect ?? new DOMRect()
}
}
const hide = () => {
popup?.[0]?.destroy()
@@ -57,9 +75,8 @@ export function createSuggestion({
return
}
// @ts-ignore getReferenceClientRect doesnt like that clientRect can return null -prf
popup = tippy('body', {
getReferenceClientRect: props.clientRect,
getReferenceClientRect: getReferenceClientRect(props.clientRect),
appendTo: () => document.body,
content: component.element,
showOnCreate: true,
@@ -77,8 +94,7 @@ export function createSuggestion({
}
popup?.[0]?.setProps({
// @ts-ignore getReferenceClientRect doesnt like that clientRect can return null -prf
getReferenceClientRect: props.clientRect,
getReferenceClientRect: getReferenceClientRect(props.clientRect),
})
},