refactoring

This commit is contained in:
Philipp Kühn
2021-03-08 23:10:10 +01:00
parent 7f698a7553
commit d64ddf6c1b
5 changed files with 217 additions and 211 deletions

View File

@@ -1,10 +1,16 @@
import React from 'react' import React from 'react'
import { Editor } from './Editor'
export class PureEditorContent extends React.Component { type EditorContentProps = {
constructor(props) { editor: Editor | null
}
export class PureEditorContent extends React.Component<EditorContentProps, EditorContentProps> {
editorContentRef: React.RefObject<any>
constructor(props: EditorContentProps) {
super(props) super(props)
this.editorContentRef = React.createRef() this.editorContentRef = React.createRef()
this.editorPortalRef = React.createRef()
this.state = { this.state = {
editor: this.props.editor editor: this.props.editor

View File

@@ -1,208 +1,208 @@
// @ts-nocheck // // @ts-nocheck
import { Node, NodeViewRenderer, NodeViewRendererProps } from '@tiptap/core' // import { Node, NodeViewRenderer, NodeViewRendererProps } from '@tiptap/core'
import { Decoration, NodeView } from 'prosemirror-view' // import { Decoration, NodeView } from 'prosemirror-view'
import { NodeSelection } from 'prosemirror-state' // import { NodeSelection } from 'prosemirror-state'
import { Node as ProseMirrorNode } from 'prosemirror-model' // import { Node as ProseMirrorNode } from 'prosemirror-model'
import React from 'react' // import React from 'react'
import ReactDOM from 'react-dom' // import ReactDOM from 'react-dom'
import { Editor } from './Editor' // import { Editor } from './Editor'
import { ReactRenderer } from './ReactRenderer' // import { ReactRenderer } from './ReactRenderer'
interface ReactNodeViewRendererOptions { // interface ReactNodeViewRendererOptions {
stopEvent: ((event: Event) => boolean) | null, // stopEvent: ((event: Event) => boolean) | null,
update: ((node: ProseMirrorNode, decorations: Decoration[]) => boolean) | null, // update: ((node: ProseMirrorNode, decorations: Decoration[]) => boolean) | null,
} // }
class ReactNodeView implements NodeView { // class ReactNodeView implements NodeView {
renderer!: ReactRenderer // renderer!: ReactRenderer
editor: Editor // editor: Editor
extension!: Node // extension!: Node
node!: ProseMirrorNode // node!: ProseMirrorNode
decorations!: Decoration[] // decorations!: Decoration[]
getPos!: any // getPos!: any
isDragging = false // isDragging = false
options: ReactNodeViewRendererOptions = { // options: ReactNodeViewRendererOptions = {
stopEvent: null, // stopEvent: null,
update: null, // update: null,
} // }
constructor(component: any, props: NodeViewRendererProps, options?: Partial<ReactNodeViewRendererOptions>) { // constructor(component: any, props: NodeViewRendererProps, options?: Partial<ReactNodeViewRendererOptions>) {
this.options = { ...this.options, ...options } // this.options = { ...this.options, ...options }
this.editor = props.editor as Editor // this.editor = props.editor as Editor
this.extension = props.extension // this.extension = props.extension
this.node = props.node // this.node = props.node
this.getPos = props.getPos // this.getPos = props.getPos
this.mount(component) // this.mount(component)
} // }
mount(component: any) { // mount(component: any) {
const props = {} // const props = {}
if (!component.displayName) { // if (!component.displayName) {
component.displayName = this.extension.config.name // component.displayName = this.extension.config.name
} // }
this.renderer = new ReactRenderer(component, { // this.renderer = new ReactRenderer(component, {
editor: this.editor, // editor: this.editor,
props, // props,
}) // })
} // }
get dom() { // get dom() {
// if (!this.renderer.element) { // // if (!this.renderer.element) {
// // return null
// // }
// // if (!this.renderer.element.hasAttribute('data-node-view-wrapper')) {
// // throw Error('Please use the NodeViewWrapper component for your node view.')
// // }
// return this.renderer.element
// }
// get contentDOM() {
// // console.log(this.dom)
// // return null
// // if (!this.renderer.element) {
// // return null
// // }
// const hasContent = !this.node.type.isAtom
// if (!hasContent) {
// return null // return null
// } // }
// if (!this.renderer.element.hasAttribute('data-node-view-wrapper')) { // const contentElement = this.dom.querySelector('[data-node-view-content]')
// throw Error('Please use the NodeViewWrapper component for your node view.')
// return contentElement || this.dom
// } // }
return this.renderer.element // stopEvent(event: Event) {
} // if (typeof this.options.stopEvent === 'function') {
// return this.options.stopEvent(event)
get contentDOM() {
// console.log(this.dom)
// return null
// if (!this.renderer.element) {
// return null
// } // }
const hasContent = !this.node.type.isAtom // const target = (event.target as HTMLElement)
// const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target)
if (!hasContent) { // // ignore all events from child nodes
return null // if (!isInElement) {
} // return false
// }
const contentElement = this.dom.querySelector('[data-node-view-content]') // const { isEditable } = this.editor
// const { isDragging } = this
// const isDraggable = !!this.node.type.spec.draggable
// const isSelectable = NodeSelection.isSelectable(this.node)
// const isCopyEvent = event.type === 'copy'
// const isPasteEvent = event.type === 'paste'
// const isCutEvent = event.type === 'cut'
// const isClickEvent = event.type === 'mousedown'
// const isDragEvent = event.type.startsWith('drag') || event.type === 'drop'
return contentElement || this.dom // // ProseMirror tries to drag selectable nodes
} // // even if `draggable` is set to `false`
// // this fix prevents that
// if (!isDraggable && isSelectable && isDragEvent) {
// event.preventDefault()
// }
stopEvent(event: Event) { // if (isDraggable && isDragEvent && !isDragging) {
if (typeof this.options.stopEvent === 'function') { // event.preventDefault()
return this.options.stopEvent(event) // return false
} // }
const target = (event.target as HTMLElement) // // we have to store that dragging started
const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target) // if (isDraggable && isEditable && !isDragging && isClickEvent) {
// const dragHandle = target.closest('[data-drag-handle]')
// const isValidDragHandle = dragHandle
// && (this.dom === dragHandle || (this.dom.contains(dragHandle)))
// ignore all events from child nodes // if (isValidDragHandle) {
if (!isInElement) { // this.isDragging = true
return false // document.addEventListener('dragend', () => {
} // this.isDragging = false
// }, { once: true })
// }
// }
const { isEditable } = this.editor // // these events are handled by prosemirror
const { isDragging } = this // if (
const isDraggable = !!this.node.type.spec.draggable // isDragging
const isSelectable = NodeSelection.isSelectable(this.node) // || isCopyEvent
const isCopyEvent = event.type === 'copy' // || isPasteEvent
const isPasteEvent = event.type === 'paste' // || isCutEvent
const isCutEvent = event.type === 'cut' // || (isClickEvent && isSelectable)
const isClickEvent = event.type === 'mousedown' // ) {
const isDragEvent = event.type.startsWith('drag') || event.type === 'drop' // return false
// }
// ProseMirror tries to drag selectable nodes // return true
// even if `draggable` is set to `false` // }
// this fix prevents that
if (!isDraggable && isSelectable && isDragEvent) {
event.preventDefault()
}
if (isDraggable && isDragEvent && !isDragging) { // ignoreMutation(mutation: MutationRecord | { type: 'selection'; target: Element }) {
event.preventDefault() // if (mutation.type === 'selection') {
return false // if (this.node.isLeaf) {
} // return true
// }
// we have to store that dragging started // return false
if (isDraggable && isEditable && !isDragging && isClickEvent) { // }
const dragHandle = target.closest('[data-drag-handle]')
const isValidDragHandle = dragHandle
&& (this.dom === dragHandle || (this.dom.contains(dragHandle)))
if (isValidDragHandle) { // if (!this.contentDOM) {
this.isDragging = true // return true
document.addEventListener('dragend', () => { // }
this.isDragging = false
}, { once: true })
}
}
// these events are handled by prosemirror // const contentDOMHasChanged = !this.contentDOM.contains(mutation.target)
if ( // || this.contentDOM === mutation.target
isDragging
|| isCopyEvent
|| isPasteEvent
|| isCutEvent
|| (isClickEvent && isSelectable)
) {
return false
}
return true // return contentDOMHasChanged
} // }
ignoreMutation(mutation: MutationRecord | { type: 'selection'; target: Element }) { // destroy() {
if (mutation.type === 'selection') { // this.renderer.destroy()
if (this.node.isLeaf) { // }
return true
}
return false // update(node: ProseMirrorNode, decorations: Decoration[]) {
} // if (typeof this.options.update === 'function') {
// return this.options.update(node, decorations)
// }
if (!this.contentDOM) { // if (node.type !== this.node.type) {
return true // return false
} // }
const contentDOMHasChanged = !this.contentDOM.contains(mutation.target) // if (node === this.node && this.decorations === decorations) {
|| this.contentDOM === mutation.target // return true
// }
return contentDOMHasChanged // this.node = node
} // this.decorations = decorations
// // this.renderer.updateProps({ node, decorations })
// this.renderer.render()
destroy() { // return true
this.renderer.destroy() // }
} // }
update(node: ProseMirrorNode, decorations: Decoration[]) { // export function ReactNodeViewRenderer(component: any, options?: Partial<ReactNodeViewRendererOptions>): NodeViewRenderer {
if (typeof this.options.update === 'function') { // return (props: NodeViewRendererProps) => {
return this.options.update(node, decorations) // // try to get the parent component
} // // this is important for vue devtools to show the component hierarchy correctly
// // maybe its `undefined` because <editor-content> isnt rendered yet
// if (!(props.editor as Editor).contentComponent) {
// return {}
// }
if (node.type !== this.node.type) { // return new ReactNodeView(component, props, options) as NodeView
return false // }
} // }
if (node === this.node && this.decorations === decorations) {
return true
}
this.node = node
this.decorations = decorations
// this.renderer.updateProps({ node, decorations })
this.renderer.render()
return true
}
}
export function ReactNodeViewRenderer(component: any, options?: Partial<ReactNodeViewRendererOptions>): NodeViewRenderer {
return (props: NodeViewRendererProps) => {
// try to get the parent component
// this is important for vue devtools to show the component hierarchy correctly
// maybe its `undefined` because <editor-content> isnt rendered yet
if (!(props.editor as Editor).contentComponent) {
return {}
}
return new ReactNodeView(component, props, options) as NodeView
}
}

View File

@@ -1,58 +1,58 @@
// @ts-nocheck // // @ts-nocheck
import React from 'react' // import React from 'react'
import { render, unmountComponentAtNode } from 'react-dom' // import { render, unmountComponentAtNode } from 'react-dom'
import { Editor } from './Editor' // import { Editor } from './Editor'
export interface VueRendererOptions { // export interface VueRendererOptions {
as?: string; // as?: string;
editor: Editor; // editor: Editor;
props?: { [key: string]: any }; // props?: { [key: string]: any };
} // }
export class ReactRenderer { // export class ReactRenderer {
id: string // id: string
editor: Editor // editor: Editor
component: any // component: any
teleportElement: Element // teleportElement: Element
element: Element // element: Element
props: { [key: string]: any } // props: { [key: string]: any }
constructor(component: any, { props = {}, editor }: VueRendererOptions) { // constructor(component: any, { props = {}, editor }: VueRendererOptions) {
this.id = Math.floor(Math.random() * 0xFFFFFFFF).toString() // this.id = Math.floor(Math.random() * 0xFFFFFFFF).toString()
this.component = component // this.component = component
this.editor = editor // this.editor = editor
this.props = props // this.props = props
this.teleportElement = document.createElement('div') // this.teleportElement = document.createElement('div')
// this.teleportElement.setAttribute('data-bla', '') // // this.teleportElement.setAttribute('data-bla', '')
// render(React.createElement(component), this.teleportElement) // // render(React.createElement(component), this.teleportElement)
// render(React.createElement(component), this.teleportElement) // // render(React.createElement(component), this.teleportElement)
this.render() // this.render()
// this.element = this.teleportElement.firstElementChild as Element // // this.element = this.teleportElement.firstElementChild as Element
this.element = this.teleportElement // this.element = this.teleportElement
} // }
render() { // render() {
render(React.createElement(this.component), this.teleportElement) // render(React.createElement(this.component), this.teleportElement)
} // }
updateProps(props: { [key: string]: any } = {}) { // updateProps(props: { [key: string]: any } = {}) {
// TODO // // TODO
} // }
destroy() { // destroy() {
// TODO // // TODO
// console.log('DESTROY', { bla: this.teleportElement }) // // console.log('DESTROY', { bla: this.teleportElement })
// console.log(document.querySelector('[data-bla]')) // // console.log(document.querySelector('[data-bla]'))
unmountComponentAtNode(this.teleportElement) // unmountComponentAtNode(this.teleportElement)
// unmountComponentAtNode(document.querySelector('[data-bla]')) // // unmountComponentAtNode(document.querySelector('[data-bla]'))
} // }
} // }

View File

@@ -3,7 +3,7 @@ import { EditorOptions } from '@tiptap/core'
import { Editor } from './Editor' import { Editor } from './Editor'
function useForceUpdate() { function useForceUpdate() {
const [_, setValue] = useState(0) const [, setValue] = useState(0)
return () => setValue(value => value + 1) return () => setValue(value => value + 1)
} }

View File

@@ -3,7 +3,7 @@
"target": "es2019", "target": "es2019",
"module": "esnext", "module": "esnext",
"strict": true, "strict": true,
"jsx": "preserve", "jsx": "react",
"importHelpers": true, "importHelpers": true,
"moduleResolution": "node", "moduleResolution": "node",
"esModuleInterop": true, "esModuleInterop": true,