add basic react nodeviewrenderer
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useEditor, EditorContent, ReactNodeViewRenderer } from '@tiptap/react'
|
import { useEditor, EditorContent, ReactNodeViewRenderer, NodeViewWrapper, NodeViewContent } from '@tiptap/react'
|
||||||
import { defaultExtensions } from '@tiptap/starter-kit'
|
import { defaultExtensions } from '@tiptap/starter-kit'
|
||||||
|
import Heading from '@tiptap/extension-heading'
|
||||||
import Paragraph from '@tiptap/extension-paragraph'
|
import Paragraph from '@tiptap/extension-paragraph'
|
||||||
import './styles.scss'
|
import './styles.scss'
|
||||||
|
|
||||||
@@ -126,19 +127,35 @@ const MenuBar = ({ editor }) => {
|
|||||||
export default () => {
|
export default () => {
|
||||||
const editor = useEditor({
|
const editor = useEditor({
|
||||||
extensions: [
|
extensions: [
|
||||||
Paragraph.extend({
|
...defaultExtensions().filter(item => item.config.name !== 'heading'),
|
||||||
|
Heading.extend({
|
||||||
|
draggable: true,
|
||||||
addNodeView() {
|
addNodeView() {
|
||||||
return ReactNodeViewRenderer(({ selected }) => {
|
return ReactNodeViewRenderer((props) => {
|
||||||
console.log({selected})
|
// console.log({props})
|
||||||
return (
|
return (
|
||||||
<div className="paragraph">noooode view {selected}</div>
|
<NodeViewWrapper>
|
||||||
|
<div className="heading">
|
||||||
|
<span
|
||||||
|
data-drag-handle
|
||||||
|
contentEditable={false}
|
||||||
|
draggable={true}
|
||||||
|
suppressContentEditableWarning={true}
|
||||||
|
>⠿</span>
|
||||||
|
<NodeViewContent />
|
||||||
|
</div>
|
||||||
|
</NodeViewWrapper>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
...defaultExtensions().filter(item => item.config.name !== 'paragraph'),
|
|
||||||
],
|
],
|
||||||
content: `<p>test</p>`,
|
content: `
|
||||||
|
<h1>h1</h1>
|
||||||
|
<h2>h2</h2>
|
||||||
|
<p>paragraph</p>
|
||||||
|
`,
|
||||||
// content: `
|
// content: `
|
||||||
// <h2>
|
// <h2>
|
||||||
// Hi there,
|
// Hi there,
|
||||||
|
|||||||
@@ -6,50 +6,15 @@ type EditorContentProps = {
|
|||||||
editor: Editor | null
|
editor: Editor | null
|
||||||
}
|
}
|
||||||
|
|
||||||
// const Portals = ({ editor }: { editor: Editor | null }) => {
|
|
||||||
// if (!editor?.contentComponent) {
|
|
||||||
// return null
|
|
||||||
// }
|
|
||||||
|
|
||||||
// console.log('render portals')
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <div>portaaals</div>
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
const Portals = ({ renderers }: { renderers: Map<any, any> }) => {
|
const Portals = ({ renderers }: { renderers: Map<any, any> }) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{Array.from(renderers).map(([key, renderer]) => {
|
{Array.from(renderers).map(([key, renderer]) => {
|
||||||
|
return ReactDOM.createPortal(
|
||||||
// console.log({renderer})
|
renderer.comp,
|
||||||
// return (
|
renderer.teleportElement,
|
||||||
// <div key={key}>{value}</div>
|
renderer.id,
|
||||||
// )
|
|
||||||
|
|
||||||
// return React.createElement(renderer.component)
|
|
||||||
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <React.Fragment key={renderer.id}>
|
|
||||||
// {ReactDOM.createPortal(
|
|
||||||
// React.createElement(renderer.component),
|
|
||||||
// renderer.teleportElement,
|
|
||||||
// )}
|
|
||||||
// </React.Fragment>
|
|
||||||
// )
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment key={renderer.id}>
|
|
||||||
{renderer.bla}
|
|
||||||
</React.Fragment>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// return ReactDOM.createPortal(
|
|
||||||
// React.createElement(renderer.component),
|
|
||||||
// renderer.teleportElement,
|
|
||||||
// )
|
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -72,14 +37,6 @@ export class PureEditorContent extends React.Component<EditorContentProps, any>
|
|||||||
editor: this.props.editor,
|
editor: this.props.editor,
|
||||||
renderers: new Map(),
|
renderers: new Map(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// setInterval(() => {
|
|
||||||
// if (this.props?.editor?.contentComponent) {
|
|
||||||
// this.props.editor.contentComponent.setState({
|
|
||||||
// renderers: this.state.renderers.set(Math.random(), Math.random())
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// }, 1000)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
@@ -94,8 +51,6 @@ export class PureEditorContent extends React.Component<EditorContentProps, any>
|
|||||||
// editor,
|
// editor,
|
||||||
// })
|
// })
|
||||||
|
|
||||||
console.log('UPDATE')
|
|
||||||
|
|
||||||
const element = this.editorContentRef.current
|
const element = this.editorContentRef.current
|
||||||
|
|
||||||
element.appendChild(editor.options.element.firstChild)
|
element.appendChild(editor.options.element.firstChild)
|
||||||
@@ -114,14 +69,11 @@ export class PureEditorContent extends React.Component<EditorContentProps, any>
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
console.log('render', this.state)
|
// console.log('render', this.state)
|
||||||
// console.log('render', this.props.editor, this.state.editor)
|
// console.log('render', this.props.editor, this.state.editor)
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div ref={this.editorContentRef} />
|
<div ref={this.editorContentRef} />
|
||||||
{/* <Content reference={this.editorContentRef} /> */}
|
|
||||||
{/* <Portals editor={this.props.editor} /> */}
|
|
||||||
|
|
||||||
<Portals renderers={this.state.renderers} />
|
<Portals renderers={this.state.renderers} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
@@ -129,5 +81,3 @@ export class PureEditorContent extends React.Component<EditorContentProps, any>
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const EditorContent = React.memo(PureEditorContent)
|
export const EditorContent = React.memo(PureEditorContent)
|
||||||
|
|
||||||
// export const EditorContent = PureEditorContent
|
|
||||||
|
|||||||
18
packages/react/src/NodeViewContent.tsx
Normal file
18
packages/react/src/NodeViewContent.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export const NodeViewContent: React.FC = props => {
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
const isEditable = true
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-node-view-content=""
|
||||||
|
contentEditable={isEditable}
|
||||||
|
style={{
|
||||||
|
whiteSpace: 'pre-wrap'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
23
packages/react/src/NodeViewWrapper.tsx
Normal file
23
packages/react/src/NodeViewWrapper.tsx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export const NodeViewWrapper: React.FC = props => {
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
const onDragStart = () => {
|
||||||
|
console.log('drag start')
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-node-view-wrapper=""
|
||||||
|
// contentEditable={false}
|
||||||
|
style={{
|
||||||
|
whiteSpace: 'normal'
|
||||||
|
}}
|
||||||
|
onDragStart={onDragStart}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -72,11 +72,25 @@ class ReactNodeView implements NodeView {
|
|||||||
editor: this.editor,
|
editor: this.editor,
|
||||||
props,
|
props,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// console.log(this.renderer.element.firstChild)
|
||||||
|
|
||||||
|
this.domWrapper.appendChild(this.renderer.element)
|
||||||
|
|
||||||
|
const contentElement = this.renderer.element.querySelector('[data-node-view-content]')
|
||||||
|
|
||||||
|
// console.log({ contentElement })
|
||||||
|
|
||||||
|
contentElement.appendChild(this.contentDOMWrapper)
|
||||||
|
|
||||||
|
// this.renderer.element.firstChild?.appendChild(this.contentDOMWrapper)
|
||||||
|
// this.domWrapper.appendChild(this.contentDOMWrapper)
|
||||||
}
|
}
|
||||||
|
|
||||||
get dom() {
|
get dom() {
|
||||||
return this.renderer.element
|
// return this.renderer.element
|
||||||
// return this.domWrapper
|
// return this.renderer.element.firstChild
|
||||||
|
return this.domWrapper
|
||||||
|
|
||||||
// if (!this.renderer.element) {
|
// if (!this.renderer.element) {
|
||||||
// return null
|
// return null
|
||||||
@@ -90,6 +104,7 @@ class ReactNodeView implements NodeView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get contentDOM() {
|
get contentDOM() {
|
||||||
|
return this.contentDOMWrapper
|
||||||
// return this.renderer.element
|
// return this.renderer.element
|
||||||
return undefined
|
return undefined
|
||||||
// return this.renderer.element
|
// return this.renderer.element
|
||||||
@@ -196,6 +211,8 @@ class ReactNodeView implements NodeView {
|
|||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.renderer.destroy()
|
this.renderer.destroy()
|
||||||
|
this.domWrapper = undefined
|
||||||
|
this.contentDOMWrapper = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
update(node: ProseMirrorNode, decorations: Decoration[]) {
|
update(node: ProseMirrorNode, decorations: Decoration[]) {
|
||||||
|
|||||||
@@ -40,16 +40,37 @@ export class ReactRenderer {
|
|||||||
// this.render()
|
// this.render()
|
||||||
// // this.element = this.teleportElement.firstElementChild as Element
|
// // this.element = this.teleportElement.firstElementChild as Element
|
||||||
|
|
||||||
console.log({ props })
|
// console.log({ props })
|
||||||
|
|
||||||
// this.bla = ReactDOM.createPortal(
|
// this.bla = ReactDOM.createPortal(
|
||||||
// React.createElement(this.component, props),
|
// React.createElement(this.component, props),
|
||||||
// this.teleportElement,
|
// this.teleportElement,
|
||||||
// )
|
// )
|
||||||
|
this.render()
|
||||||
|
// this.comp = React.createElement(this.component, { foo: 1 })
|
||||||
|
|
||||||
this.bla = React.createElement(this.component, props)
|
// // this.bla = React.createElement(this.component, props)
|
||||||
|
|
||||||
// console.log({ bla })
|
// // console.log({ bla })
|
||||||
|
|
||||||
|
// if (this.editor?.contentComponent) {
|
||||||
|
// this.editor.contentComponent.setState({
|
||||||
|
// renderers: this.editor.contentComponent.state.renderers.set(
|
||||||
|
// this.id,
|
||||||
|
// this,
|
||||||
|
// ),
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// get comp() {
|
||||||
|
// console.log('get comp')
|
||||||
|
// return React.createElement(this.component, { foo: 1 })
|
||||||
|
// }
|
||||||
|
|
||||||
|
render() {
|
||||||
|
this.comp = React.createElement(this.component, { foo: 1 })
|
||||||
|
// render(React.createElement(this.component), this.teleportElement)
|
||||||
|
|
||||||
if (this.editor?.contentComponent) {
|
if (this.editor?.contentComponent) {
|
||||||
this.editor.contentComponent.setState({
|
this.editor.contentComponent.setState({
|
||||||
@@ -61,13 +82,9 @@ export class ReactRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
|
||||||
render(React.createElement(this.component), this.teleportElement)
|
|
||||||
}
|
|
||||||
|
|
||||||
updateProps(props: { [key: string]: any } = {}) {
|
updateProps(props: { [key: string]: any } = {}) {
|
||||||
// TODO
|
// TODO
|
||||||
console.log('update props', { props })
|
// console.log('update props', { props })
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
|
|||||||
@@ -5,3 +5,5 @@ export * from './useEditor'
|
|||||||
export * from './ReactRenderer'
|
export * from './ReactRenderer'
|
||||||
export * from './ReactNodeViewRenderer'
|
export * from './ReactNodeViewRenderer'
|
||||||
export * from './EditorContent'
|
export * from './EditorContent'
|
||||||
|
export * from './NodeViewWrapper'
|
||||||
|
export * from './NodeViewContent'
|
||||||
|
|||||||
@@ -28,7 +28,8 @@
|
|||||||
"./shims/vue.d.ts"
|
"./shims/vue.d.ts"
|
||||||
],
|
],
|
||||||
"include": [
|
"include": [
|
||||||
"**/*.ts"
|
"**/*.ts",
|
||||||
|
"**/*.tsx"
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"**/node_modules",
|
"**/node_modules",
|
||||||
|
|||||||
Reference in New Issue
Block a user