mirror of
https://github.com/ueberdosis/tiptap.git
synced 2025-01-18 14:13:21 +08:00
* remove async createNodeViews * focus asynchronously to fix weird bugs in react
This commit is contained in:
parent
812c49bcb1
commit
956566eaad
@ -11,10 +11,12 @@ declare module '@tiptap/core' {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const blur: RawCommands['blur'] = () => ({ view }) => {
|
export const blur: RawCommands['blur'] = () => ({ editor, view }) => {
|
||||||
const element = view.dom as HTMLElement
|
requestAnimationFrame(() => {
|
||||||
|
if (!editor.isDestroyed) {
|
||||||
element.blur()
|
(view.dom as HTMLElement).blur()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -47,13 +47,23 @@ export const focus: RawCommands['focus'] = (position = null) => ({
|
|||||||
tr,
|
tr,
|
||||||
dispatch,
|
dispatch,
|
||||||
}) => {
|
}) => {
|
||||||
|
const delayedFocus = () => {
|
||||||
|
// For React we have to focus asynchronously. Otherwise wild things happen.
|
||||||
|
// see: https://github.com/ueberdosis/tiptap/issues/1520
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
if (!editor.isDestroyed) {
|
||||||
|
view.focus()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if ((view.hasFocus() && position === null) || position === false) {
|
if ((view.hasFocus() && position === null) || position === false) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// we don’t try to resolve a NodeSelection or CellSelection
|
// we don’t try to resolve a NodeSelection or CellSelection
|
||||||
if (dispatch && position === null && !isTextSelection(editor.state.selection)) {
|
if (dispatch && position === null && !isTextSelection(editor.state.selection)) {
|
||||||
view.focus()
|
delayedFocus()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +77,9 @@ export const focus: RawCommands['focus'] = (position = null) => ({
|
|||||||
const isSameSelection = editor.state.selection.eq(selection)
|
const isSameSelection = editor.state.selection.eq(selection)
|
||||||
|
|
||||||
if (dispatch) {
|
if (dispatch) {
|
||||||
tr.setSelection(selection)
|
if (!isSameSelection) {
|
||||||
|
tr.setSelection(selection)
|
||||||
|
}
|
||||||
|
|
||||||
// `tr.setSelection` resets the stored marks
|
// `tr.setSelection` resets the stored marks
|
||||||
// so we’ll restore them if the selection is the same as before
|
// so we’ll restore them if the selection is the same as before
|
||||||
@ -75,7 +87,7 @@ export const focus: RawCommands['focus'] = (position = null) => ({
|
|||||||
tr.setStoredMarks(storedMarks)
|
tr.setStoredMarks(storedMarks)
|
||||||
}
|
}
|
||||||
|
|
||||||
view.focus()
|
delayedFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
@ -36,7 +36,7 @@ export class BubbleMenuView {
|
|||||||
|
|
||||||
public preventHide = false
|
public preventHide = false
|
||||||
|
|
||||||
public tippy!: Instance
|
public tippy: Instance | undefined
|
||||||
|
|
||||||
public shouldShow: Exclude<BubbleMenuPluginProps['shouldShow'], null> = ({ state, from, to }) => {
|
public shouldShow: Exclude<BubbleMenuPluginProps['shouldShow'], null> = ({ state, from, to }) => {
|
||||||
const { doc, selection } = state
|
const { doc, selection } = state
|
||||||
@ -74,8 +74,13 @@ export class BubbleMenuView {
|
|||||||
this.view.dom.addEventListener('dragstart', this.dragstartHandler)
|
this.view.dom.addEventListener('dragstart', this.dragstartHandler)
|
||||||
this.editor.on('focus', this.focusHandler)
|
this.editor.on('focus', this.focusHandler)
|
||||||
this.editor.on('blur', this.blurHandler)
|
this.editor.on('blur', this.blurHandler)
|
||||||
this.createTooltip(tippyOptions)
|
|
||||||
this.element.style.visibility = 'visible'
|
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 = () => {
|
mousedownHandler = () => {
|
||||||
@ -109,7 +114,7 @@ export class BubbleMenuView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createTooltip(options: Partial<Props> = {}) {
|
createTooltip(options: Partial<Props> = {}) {
|
||||||
this.tippy = tippy(this.view.dom, {
|
this.tippy = tippy(this.editor.options.element, {
|
||||||
duration: 0,
|
duration: 0,
|
||||||
getReferenceClientRect: null,
|
getReferenceClientRect: null,
|
||||||
content: this.element,
|
content: this.element,
|
||||||
@ -150,7 +155,7 @@ export class BubbleMenuView {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tippy.setProps({
|
this.tippy?.setProps({
|
||||||
getReferenceClientRect: () => {
|
getReferenceClientRect: () => {
|
||||||
if (isNodeSelection(state.selection)) {
|
if (isNodeSelection(state.selection)) {
|
||||||
const node = view.nodeDOM(from) as HTMLElement
|
const node = view.nodeDOM(from) as HTMLElement
|
||||||
@ -168,15 +173,15 @@ export class BubbleMenuView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
this.tippy.show()
|
this.tippy?.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
this.tippy.hide()
|
this.tippy?.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.tippy.destroy()
|
this.tippy?.destroy()
|
||||||
this.element.removeEventListener('mousedown', this.mousedownHandler)
|
this.element.removeEventListener('mousedown', this.mousedownHandler)
|
||||||
this.view.dom.removeEventListener('dragstart', this.dragstartHandler)
|
this.view.dom.removeEventListener('dragstart', this.dragstartHandler)
|
||||||
this.editor.off('focus', this.focusHandler)
|
this.editor.off('focus', this.focusHandler)
|
||||||
|
@ -29,7 +29,7 @@ export class FloatingMenuView {
|
|||||||
|
|
||||||
public preventHide = false
|
public preventHide = false
|
||||||
|
|
||||||
public tippy!: Instance
|
public tippy: Instance | undefined
|
||||||
|
|
||||||
public shouldShow: Exclude<FloatingMenuPluginProps['shouldShow'], null> = ({ state }) => {
|
public shouldShow: Exclude<FloatingMenuPluginProps['shouldShow'], null> = ({ state }) => {
|
||||||
const { selection } = state
|
const { selection } = state
|
||||||
@ -64,8 +64,13 @@ export class FloatingMenuView {
|
|||||||
this.element.addEventListener('mousedown', this.mousedownHandler, { capture: true })
|
this.element.addEventListener('mousedown', this.mousedownHandler, { capture: true })
|
||||||
this.editor.on('focus', this.focusHandler)
|
this.editor.on('focus', this.focusHandler)
|
||||||
this.editor.on('blur', this.blurHandler)
|
this.editor.on('blur', this.blurHandler)
|
||||||
this.createTooltip(tippyOptions)
|
|
||||||
this.element.style.visibility = 'visible'
|
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 = () => {
|
mousedownHandler = () => {
|
||||||
@ -95,7 +100,7 @@ export class FloatingMenuView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createTooltip(options: Partial<Props> = {}) {
|
createTooltip(options: Partial<Props> = {}) {
|
||||||
this.tippy = tippy(this.view.dom, {
|
this.tippy = tippy(this.editor.options.element, {
|
||||||
duration: 0,
|
duration: 0,
|
||||||
getReferenceClientRect: null,
|
getReferenceClientRect: null,
|
||||||
content: this.element,
|
content: this.element,
|
||||||
@ -130,7 +135,7 @@ export class FloatingMenuView {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tippy.setProps({
|
this.tippy?.setProps({
|
||||||
getReferenceClientRect: () => posToDOMRect(view, from, to),
|
getReferenceClientRect: () => posToDOMRect(view, from, to),
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -138,15 +143,15 @@ export class FloatingMenuView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
this.tippy.show()
|
this.tippy?.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
this.tippy.hide()
|
this.tippy?.hide()
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.tippy.destroy()
|
this.tippy?.destroy()
|
||||||
this.element.removeEventListener('mousedown', this.mousedownHandler)
|
this.element.removeEventListener('mousedown', this.mousedownHandler)
|
||||||
this.editor.off('focus', this.focusHandler)
|
this.editor.off('focus', this.focusHandler)
|
||||||
this.editor.off('blur', this.blurHandler)
|
this.editor.off('blur', this.blurHandler)
|
||||||
|
@ -63,12 +63,7 @@ export class PureEditorContent extends React.Component<EditorContentProps, Edito
|
|||||||
|
|
||||||
editor.contentComponent = this
|
editor.contentComponent = this
|
||||||
|
|
||||||
// TODO: alternative to setTimeout?
|
editor.createNodeViews()
|
||||||
setTimeout(() => {
|
|
||||||
if (!editor.isDestroyed) {
|
|
||||||
editor.createNodeViews()
|
|
||||||
}
|
|
||||||
}, 0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user