@tiptap/react 2.0.0-beta.7 → 2.0.0-beta.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,22 +2,24 @@ import React from 'react'
2
2
  import { useReactNodeView } from './useReactNodeView'
3
3
 
4
4
  export interface NodeViewWrapperProps {
5
- className?: string,
6
- as: React.ElementType,
5
+ [key: string]: any,
6
+ as?: React.ElementType,
7
7
  }
8
8
 
9
- export const NodeViewWrapper: React.FC<NodeViewWrapperProps> = props => {
9
+ export const NodeViewWrapper: React.FC<NodeViewWrapperProps> = React.forwardRef((props, ref) => {
10
10
  const { onDragStart } = useReactNodeView()
11
11
  const Tag = props.as || 'div'
12
12
 
13
13
  return (
14
14
  <Tag
15
- className={props.className}
15
+ {...props}
16
+ ref={ref}
16
17
  data-node-view-wrapper=""
17
18
  onDragStart={onDragStart}
18
- style={{ whiteSpace: 'normal' }}
19
- >
20
- {props.children}
21
- </Tag>
19
+ style={{
20
+ ...props.style,
21
+ whiteSpace: 'normal',
22
+ }}
23
+ />
22
24
  )
23
- }
25
+ })
@@ -1,9 +1,10 @@
1
- import React, { useState, useEffect } from 'react'
1
+ import React from 'react'
2
2
  import {
3
3
  NodeView,
4
4
  NodeViewProps,
5
5
  NodeViewRenderer,
6
6
  NodeViewRendererProps,
7
+ NodeViewRendererOptions,
7
8
  } from '@tiptap/core'
8
9
  import { Decoration, NodeView as ProseMirrorNodeView } from 'prosemirror-view'
9
10
  import { Node as ProseMirrorNode } from 'prosemirror-model'
@@ -11,15 +12,22 @@ import { Editor } from './Editor'
11
12
  import { ReactRenderer } from './ReactRenderer'
12
13
  import { ReactNodeViewContext } from './useReactNodeView'
13
14
 
14
- interface ReactNodeViewRendererOptions {
15
- stopEvent: ((event: Event) => boolean) | null,
16
- update: ((node: ProseMirrorNode, decorations: Decoration[]) => boolean) | null,
15
+ export interface ReactNodeViewRendererOptions extends NodeViewRendererOptions {
16
+ update: ((props: {
17
+ oldNode: ProseMirrorNode,
18
+ oldDecorations: Decoration[],
19
+ newNode: ProseMirrorNode,
20
+ newDecorations: Decoration[],
21
+ updateProps: () => void,
22
+ }) => boolean) | null,
17
23
  }
18
24
 
19
- class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
25
+ class ReactNodeView extends NodeView<React.FunctionComponent, Editor, ReactNodeViewRendererOptions> {
20
26
 
21
27
  renderer!: ReactRenderer
22
28
 
29
+ contentDOMElement!: HTMLElement | null
30
+
23
31
  mount() {
24
32
  const props: NodeViewProps = {
25
33
  editor: this.editor,
@@ -29,6 +37,7 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
29
37
  extension: this.extension,
30
38
  getPos: () => this.getPos(),
31
39
  updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
40
+ deleteNode: () => this.deleteNode(),
32
41
  }
33
42
 
34
43
  if (!(this.component as any).displayName) {
@@ -36,26 +45,16 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
36
45
  return string.charAt(0).toUpperCase() + string.substring(1)
37
46
  }
38
47
 
39
- // @ts-ignore
40
- this.component.displayName = capitalizeFirstChar(this.extension.config.name)
48
+ this.component.displayName = capitalizeFirstChar(this.extension.name)
41
49
  }
42
50
 
43
51
  const ReactNodeViewProvider: React.FunctionComponent = componentProps => {
44
- const [isEditable, setIsEditable] = useState(this.editor.isEditable)
45
52
  const onDragStart = this.onDragStart.bind(this)
46
- const onViewUpdate = () => setIsEditable(this.editor.isEditable)
53
+ const maybeMoveContentDOM = this.maybeMoveContentDOM.bind(this)
47
54
  const Component = this.component
48
55
 
49
- useEffect(() => {
50
- this.editor.on('viewUpdate', onViewUpdate)
51
-
52
- return () => {
53
- this.editor.off('viewUpdate', onViewUpdate)
54
- }
55
- }, [])
56
-
57
56
  return (
58
- <ReactNodeViewContext.Provider value={{ onDragStart, isEditable }}>
57
+ <ReactNodeViewContext.Provider value={{ onDragStart, maybeMoveContentDOM }}>
59
58
  <Component {...componentProps} />
60
59
  </ReactNodeViewContext.Provider>
61
60
  )
@@ -63,15 +62,32 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
63
62
 
64
63
  ReactNodeViewProvider.displayName = 'ReactNodeView'
65
64
 
65
+ this.contentDOMElement = this.node.isLeaf
66
+ ? null
67
+ : document.createElement(this.node.isInline ? 'span' : 'div')
68
+
69
+ if (this.contentDOMElement) {
70
+ // For some reason the whiteSpace prop is not inherited properly in Chrome and Safari
71
+ // With this fix it seems to work fine
72
+ // See: https://github.com/ueberdosis/tiptap/issues/1197
73
+ this.contentDOMElement.style.whiteSpace = 'inherit'
74
+ }
75
+
66
76
  this.renderer = new ReactRenderer(ReactNodeViewProvider, {
67
77
  editor: this.editor,
68
78
  props,
79
+ as: this.node.isInline
80
+ ? 'span'
81
+ : 'div',
69
82
  })
70
83
  }
71
84
 
72
85
  get dom() {
73
- if (!this.renderer.element.firstElementChild?.hasAttribute('data-node-view-wrapper')) {
74
- throw Error('Please use the ReactViewWrapper component for your node view.')
86
+ if (
87
+ this.renderer.element.firstElementChild
88
+ && !this.renderer.element.firstElementChild?.hasAttribute('data-node-view-wrapper')
89
+ ) {
90
+ throw Error('Please use the NodeViewWrapper component for your node view.')
75
91
  }
76
92
 
77
93
  return this.renderer.element
@@ -82,14 +98,43 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
82
98
  return null
83
99
  }
84
100
 
101
+ this.maybeMoveContentDOM()
102
+
103
+ return this.contentDOMElement
104
+ }
105
+
106
+ maybeMoveContentDOM(): void {
85
107
  const contentElement = this.dom.querySelector('[data-node-view-content]')
86
108
 
87
- return contentElement || this.dom
109
+ if (
110
+ this.contentDOMElement
111
+ && contentElement
112
+ && !contentElement.contains(this.contentDOMElement)
113
+ ) {
114
+ contentElement.appendChild(this.contentDOMElement)
115
+ }
88
116
  }
89
117
 
90
118
  update(node: ProseMirrorNode, decorations: Decoration[]) {
119
+ const updateProps = (props?: Record<string, any>) => {
120
+ this.renderer.updateProps(props)
121
+ this.maybeMoveContentDOM()
122
+ }
123
+
91
124
  if (typeof this.options.update === 'function') {
92
- return this.options.update(node, decorations)
125
+ const oldNode = this.node
126
+ const oldDecorations = this.decorations
127
+
128
+ this.node = node
129
+ this.decorations = decorations
130
+
131
+ return this.options.update({
132
+ oldNode,
133
+ oldDecorations,
134
+ newNode: node,
135
+ newDecorations: decorations,
136
+ updateProps: () => updateProps({ node, decorations }),
137
+ })
93
138
  }
94
139
 
95
140
  if (node.type !== this.node.type) {
@@ -102,7 +147,8 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
102
147
 
103
148
  this.node = node
104
149
  this.decorations = decorations
105
- this.renderer.updateProps({ node, decorations })
150
+
151
+ updateProps({ node, decorations })
106
152
 
107
153
  return true
108
154
  }
@@ -121,6 +167,7 @@ class ReactNodeView extends NodeView<React.FunctionComponent, Editor> {
121
167
 
122
168
  destroy() {
123
169
  this.renderer.destroy()
170
+ this.contentDOMElement = null
124
171
  }
125
172
  }
126
173
 
@@ -1,5 +1,4 @@
1
1
  import React from 'react'
2
- import { AnyObject } from '@tiptap/core'
3
2
  import { Editor } from './Editor'
4
3
 
5
4
  function isClassComponent(Component: any) {
@@ -10,12 +9,24 @@ function isClassComponent(Component: any) {
10
9
  )
11
10
  }
12
11
 
12
+ function isForwardRefComponent(Component: any) {
13
+ return !!(
14
+ typeof Component === 'object'
15
+ && Component.$$typeof?.toString() === 'Symbol(react.forward_ref)'
16
+ )
17
+ }
18
+
13
19
  export interface ReactRendererOptions {
14
- as?: string,
15
20
  editor: Editor,
16
- props?: AnyObject,
21
+ props?: Record<string, any>,
22
+ as?: string,
17
23
  }
18
24
 
25
+ type ComponentType =
26
+ | React.ComponentClass
27
+ | React.FunctionComponent
28
+ | React.ForwardRefExoticComponent<{ items: any[], command: any } & React.RefAttributes<unknown>>
29
+
19
30
  export class ReactRenderer {
20
31
  id: string
21
32
 
@@ -25,18 +36,18 @@ export class ReactRenderer {
25
36
 
26
37
  element: Element
27
38
 
28
- props: AnyObject
39
+ props: Record<string, any>
29
40
 
30
41
  reactElement: React.ReactNode
31
42
 
32
43
  ref: React.Component | null = null
33
44
 
34
- constructor(component: React.Component | React.FunctionComponent, { props = {}, editor }: ReactRendererOptions) {
45
+ constructor(component: ComponentType, { editor, props = {}, as = 'div' }: ReactRendererOptions) {
35
46
  this.id = Math.floor(Math.random() * 0xFFFFFFFF).toString()
36
47
  this.component = component
37
48
  this.editor = editor
38
49
  this.props = props
39
- this.element = document.createElement('div')
50
+ this.element = document.createElement(as)
40
51
  this.element.classList.add('react-renderer')
41
52
  this.render()
42
53
  }
@@ -45,7 +56,7 @@ export class ReactRenderer {
45
56
  const Component = this.component
46
57
  const props = this.props
47
58
 
48
- if (isClassComponent(Component)) {
59
+ if (isClassComponent(Component) || isForwardRefComponent(Component)) {
49
60
  props.ref = (ref: React.Component) => {
50
61
  this.ref = ref
51
62
  }
@@ -63,7 +74,7 @@ export class ReactRenderer {
63
74
  }
64
75
  }
65
76
 
66
- updateProps(props: AnyObject = {}): void {
77
+ updateProps(props: Record<string, any> = {}): void {
67
78
  this.props = {
68
79
  ...this.props,
69
80
  ...props,
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from '@tiptap/core'
2
2
  export * from './BubbleMenu'
3
3
  export { Editor } from './Editor'
4
+ export * from './FloatingMenu'
4
5
  export * from './useEditor'
5
6
  export * from './ReactRenderer'
6
7
  export * from './ReactNodeViewRenderer'
package/src/useEditor.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { useState, useEffect } from 'react'
1
+ import { useState, useEffect, DependencyList } from 'react'
2
2
  import { EditorOptions } from '@tiptap/core'
3
3
  import { Editor } from './Editor'
4
4
 
@@ -8,7 +8,7 @@ function useForceUpdate() {
8
8
  return () => setValue(value => value + 1)
9
9
  }
10
10
 
11
- export const useEditor = (options: Partial<EditorOptions> = {}) => {
11
+ export const useEditor = (options: Partial<EditorOptions> = {}, deps: DependencyList = []) => {
12
12
  const [editor, setEditor] = useState<Editor | null>(null)
13
13
  const forceUpdate = useForceUpdate()
14
14
 
@@ -22,7 +22,7 @@ export const useEditor = (options: Partial<EditorOptions> = {}) => {
22
22
  return () => {
23
23
  instance.destroy()
24
24
  }
25
- }, [])
25
+ }, deps)
26
26
 
27
27
  return editor
28
28
  }
@@ -1,12 +1,11 @@
1
1
  import { createContext, useContext } from 'react'
2
2
 
3
3
  export interface ReactNodeViewContextProps {
4
- isEditable: boolean,
5
4
  onDragStart: (event: DragEvent) => void,
5
+ maybeMoveContentDOM: () => void,
6
6
  }
7
7
 
8
8
  export const ReactNodeViewContext = createContext<Partial<ReactNodeViewContextProps>>({
9
- isEditable: undefined,
10
9
  onDragStart: undefined,
11
10
  })
12
11
 
package/CHANGELOG.md DELETED
@@ -1,64 +0,0 @@
1
- # Change Log
2
-
3
- All notable changes to this project will be documented in this file.
4
- See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
-
6
- # [2.0.0-beta.7](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/react@2.0.0-beta.6...@tiptap/react@2.0.0-beta.7) (2021-03-31)
7
-
8
- **Note:** Version bump only for package @tiptap/react
9
-
10
-
11
-
12
-
13
-
14
- # [2.0.0-beta.6](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/react@2.0.0-beta.5...@tiptap/react@2.0.0-beta.6) (2021-03-28)
15
-
16
- **Note:** Version bump only for package @tiptap/react
17
-
18
-
19
-
20
-
21
-
22
- # [2.0.0-beta.5](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/react@2.0.0-beta.4...@tiptap/react@2.0.0-beta.5) (2021-03-24)
23
-
24
- **Note:** Version bump only for package @tiptap/react
25
-
26
-
27
-
28
-
29
-
30
- # [2.0.0-beta.4](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/react@2.0.0-beta.3...@tiptap/react@2.0.0-beta.4) (2021-03-18)
31
-
32
- **Note:** Version bump only for package @tiptap/react
33
-
34
-
35
-
36
-
37
-
38
- # [2.0.0-beta.3](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/react@2.0.0-beta.2...@tiptap/react@2.0.0-beta.3) (2021-03-16)
39
-
40
- **Note:** Version bump only for package @tiptap/react
41
-
42
-
43
-
44
-
45
-
46
- # [2.0.0-beta.2](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/react@2.0.0-beta.1...@tiptap/react@2.0.0-beta.2) (2021-03-09)
47
-
48
- **Note:** Version bump only for package @tiptap/react
49
-
50
-
51
-
52
-
53
-
54
- # [2.0.0-beta.1](https://github.com/ueberdosis/tiptap-next/compare/@tiptap/react@2.0.0-alpha.2...@tiptap/react@2.0.0-beta.1) (2021-03-05)
55
-
56
- **Note:** Version bump only for package @tiptap/react
57
-
58
-
59
-
60
-
61
-
62
- # 2.0.0-alpha.2 (2021-02-26)
63
-
64
- **Note:** Version bump only for package @tiptap/react