fix: don’t initialize tippy on requestAnimationFrame to avoid race conditions (#1820)

Instead of initializting tippy when
the bubble menu and floating menu plugins are initialized,
defer the initialization of tippy to the moment when
the the editor should display the floating or bubble menu

Co-authored-by: Enrique Alcantara <ealcantara@gitlab.com>
This commit is contained in:
Enrique
2021-09-07 14:46:45 -04:00
committed by GitHub
parent 67ce72a9ee
commit ca3763d3c2
2 changed files with 56 additions and 36 deletions

View File

@@ -38,6 +38,8 @@ export class BubbleMenuView {
public tippy: Instance | undefined
public tippyOptions?: Partial<Props>
public shouldShow: Exclude<BubbleMenuPluginProps['shouldShow'], null> = ({ state, from, to }) => {
const { doc, selection } = state
const { empty } = selection
@@ -59,7 +61,7 @@ export class BubbleMenuView {
editor,
element,
view,
tippyOptions,
tippyOptions = {},
shouldShow,
}: BubbleMenuViewProps) {
this.editor = editor
@@ -74,13 +76,10 @@ export class BubbleMenuView {
this.view.dom.addEventListener('dragstart', this.dragstartHandler)
this.editor.on('focus', this.focusHandler)
this.editor.on('blur', this.blurHandler)
this.tippyOptions = tippyOptions
// Detaches menu content from its current parent
this.element.remove()
this.element.style.visibility = 'visible'
// We create tippy asynchronously to make sure that `editor.options.element`
// has already been moved to the right position in the DOM
requestAnimationFrame(() => {
this.createTooltip(tippyOptions)
})
}
mousedownHandler = () => {
@@ -113,17 +112,26 @@ export class BubbleMenuView {
this.hide()
}
createTooltip(options: Partial<Props> = {}) {
this.tippy = tippy(this.editor.options.element, {
duration: 0,
getReferenceClientRect: null,
content: this.element,
interactive: true,
trigger: 'manual',
placement: 'top',
hideOnClick: 'toggle',
...options,
})
createTooltip() {
if (this.tippy) {
return
}
const { element: editorElement } = this.editor.options
// Wait until editor element is attached to the document
if (editorElement.parentElement) {
this.tippy = tippy(editorElement, {
duration: 0,
getReferenceClientRect: null,
content: this.element,
interactive: true,
trigger: 'manual',
placement: 'top',
hideOnClick: 'toggle',
...this.tippyOptions,
})
}
}
update(view: EditorView, oldState?: EditorState) {
@@ -135,6 +143,8 @@ export class BubbleMenuView {
return
}
this.createTooltip()
// support for CellSelections
const { ranges } = selection
const from = Math.min(...ranges.map(range => range.$from.pos))

View File

@@ -31,6 +31,8 @@ export class FloatingMenuView {
public tippy: Instance | undefined
public tippyOptions?: Partial<Props>
public shouldShow: Exclude<FloatingMenuPluginProps['shouldShow'], null> = ({ state }) => {
const { selection } = state
const { $anchor, empty } = selection
@@ -50,7 +52,7 @@ export class FloatingMenuView {
editor,
element,
view,
tippyOptions,
tippyOptions = {},
shouldShow,
}: FloatingMenuViewProps) {
this.editor = editor
@@ -64,13 +66,10 @@ export class FloatingMenuView {
this.element.addEventListener('mousedown', this.mousedownHandler, { capture: true })
this.editor.on('focus', this.focusHandler)
this.editor.on('blur', this.blurHandler)
this.tippyOptions = tippyOptions
// Detaches menu content from its current parent
this.element.remove()
this.element.style.visibility = 'visible'
// We create tippy asynchronously to make sure that `editor.options.element`
// has already been moved to the right position in the DOM
requestAnimationFrame(() => {
this.createTooltip(tippyOptions)
})
}
mousedownHandler = () => {
@@ -99,17 +98,26 @@ export class FloatingMenuView {
this.hide()
}
createTooltip(options: Partial<Props> = {}) {
this.tippy = tippy(this.editor.options.element, {
duration: 0,
getReferenceClientRect: null,
content: this.element,
interactive: true,
trigger: 'manual',
placement: 'right',
hideOnClick: 'toggle',
...options,
})
createTooltip() {
if (this.tippy) {
return
}
const { element: editorElement } = this.editor.options
// Wait until editor element is attached to the document
if (editorElement.parentElement) {
this.tippy = tippy(editorElement, {
duration: 0,
getReferenceClientRect: null,
content: this.element,
interactive: true,
trigger: 'manual',
placement: 'right',
hideOnClick: 'toggle',
...this.tippyOptions,
})
}
}
update(view: EditorView, oldState?: EditorState) {
@@ -122,6 +130,8 @@ export class FloatingMenuView {
return
}
this.createTooltip()
const shouldShow = this.shouldShow?.({
editor: this.editor,
view,