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:
@@ -40,6 +40,24 @@ export function createSuggestion({
|
|||||||
render: () => {
|
render: () => {
|
||||||
let component: ReactRenderer<MentionListRef> | undefined
|
let component: ReactRenderer<MentionListRef> | undefined
|
||||||
let popup: TippyInstance[] | 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 = () => {
|
const hide = () => {
|
||||||
popup?.[0]?.destroy()
|
popup?.[0]?.destroy()
|
||||||
@@ -57,9 +75,8 @@ export function createSuggestion({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore getReferenceClientRect doesnt like that clientRect can return null -prf
|
|
||||||
popup = tippy('body', {
|
popup = tippy('body', {
|
||||||
getReferenceClientRect: props.clientRect,
|
getReferenceClientRect: getReferenceClientRect(props.clientRect),
|
||||||
appendTo: () => document.body,
|
appendTo: () => document.body,
|
||||||
content: component.element,
|
content: component.element,
|
||||||
showOnCreate: true,
|
showOnCreate: true,
|
||||||
@@ -77,8 +94,7 @@ export function createSuggestion({
|
|||||||
}
|
}
|
||||||
|
|
||||||
popup?.[0]?.setProps({
|
popup?.[0]?.setProps({
|
||||||
// @ts-ignore getReferenceClientRect doesnt like that clientRect can return null -prf
|
getReferenceClientRect: getReferenceClientRect(props.clientRect),
|
||||||
getReferenceClientRect: props.clientRect,
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user