@tiptap/react 2.0.0-beta.21 → 2.0.0-beta.211

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.
Files changed (37) hide show
  1. package/README.md +2 -2
  2. package/dist/index.cjs +437 -0
  3. package/dist/index.d.ts +101 -0
  4. package/dist/index.js +437 -0
  5. package/package.json +45 -16
  6. package/src/BubbleMenu.tsx +32 -16
  7. package/src/Editor.ts +9 -2
  8. package/src/EditorContent.tsx +55 -12
  9. package/src/FloatingMenu.tsx +36 -14
  10. package/src/NodeViewContent.tsx +10 -3
  11. package/src/NodeViewWrapper.tsx +11 -8
  12. package/src/ReactNodeViewRenderer.tsx +82 -34
  13. package/src/ReactRenderer.tsx +38 -29
  14. package/src/index.ts +6 -6
  15. package/src/useEditor.ts +16 -4
  16. package/src/useReactNodeView.ts +1 -0
  17. package/CHANGELOG.md +0 -190
  18. package/LICENSE.md +0 -21
  19. package/dist/packages/react/src/BubbleMenu.d.ts +0 -6
  20. package/dist/packages/react/src/Editor.d.ts +0 -6
  21. package/dist/packages/react/src/EditorContent.d.ts +0 -19
  22. package/dist/packages/react/src/FloatingMenu.d.ts +0 -6
  23. package/dist/packages/react/src/NodeViewContent.d.ts +0 -6
  24. package/dist/packages/react/src/NodeViewWrapper.d.ts +0 -6
  25. package/dist/packages/react/src/ReactNodeViewRenderer.d.ts +0 -9
  26. package/dist/packages/react/src/ReactRenderer.d.ts +0 -21
  27. package/dist/packages/react/src/index.d.ts +0 -10
  28. package/dist/packages/react/src/useEditor.d.ts +0 -3
  29. package/dist/packages/react/src/useReactNodeView.d.ts +0 -6
  30. package/dist/tiptap-react.bundle.umd.min.js +0 -54
  31. package/dist/tiptap-react.bundle.umd.min.js.map +0 -1
  32. package/dist/tiptap-react.cjs.js +0 -318
  33. package/dist/tiptap-react.cjs.js.map +0 -1
  34. package/dist/tiptap-react.esm.js +0 -293
  35. package/dist/tiptap-react.esm.js.map +0 -1
  36. package/dist/tiptap-react.umd.js +0 -318
  37. package/dist/tiptap-react.umd.js.map +0 -1
package/dist/index.js ADDED
@@ -0,0 +1,437 @@
1
+ // src/BubbleMenu.tsx
2
+ import { BubbleMenuPlugin } from "@tiptap/extension-bubble-menu";
3
+ import React, { useEffect, useState } from "react";
4
+ var BubbleMenu = (props) => {
5
+ const [element, setElement] = useState(null);
6
+ useEffect(() => {
7
+ if (!element) {
8
+ return;
9
+ }
10
+ if (props.editor.isDestroyed) {
11
+ return;
12
+ }
13
+ const {
14
+ pluginKey = "bubbleMenu",
15
+ editor,
16
+ tippyOptions = {},
17
+ updateDelay,
18
+ shouldShow = null
19
+ } = props;
20
+ const plugin = BubbleMenuPlugin({
21
+ updateDelay,
22
+ editor,
23
+ element,
24
+ pluginKey,
25
+ shouldShow,
26
+ tippyOptions
27
+ });
28
+ editor.registerPlugin(plugin);
29
+ return () => editor.unregisterPlugin(pluginKey);
30
+ }, [props.editor, element]);
31
+ return /* @__PURE__ */ React.createElement("div", { ref: setElement, className: props.className, style: { visibility: "hidden" } }, props.children);
32
+ };
33
+
34
+ // src/Editor.ts
35
+ import { Editor as CoreEditor } from "@tiptap/core";
36
+ var Editor = class extends CoreEditor {
37
+ constructor() {
38
+ super(...arguments);
39
+ this.contentComponent = null;
40
+ }
41
+ };
42
+
43
+ // src/EditorContent.tsx
44
+ import React2 from "react";
45
+ import ReactDOM, { flushSync } from "react-dom";
46
+ var Portals = ({ renderers }) => {
47
+ return /* @__PURE__ */ React2.createElement(React2.Fragment, null, Object.entries(renderers).map(([key, renderer]) => {
48
+ return ReactDOM.createPortal(
49
+ renderer.reactElement,
50
+ renderer.element,
51
+ key
52
+ );
53
+ }));
54
+ };
55
+ var PureEditorContent = class extends React2.Component {
56
+ constructor(props) {
57
+ super(props);
58
+ this.editorContentRef = React2.createRef();
59
+ this.initialized = false;
60
+ this.state = {
61
+ renderers: {}
62
+ };
63
+ }
64
+ componentDidMount() {
65
+ this.init();
66
+ }
67
+ componentDidUpdate() {
68
+ this.init();
69
+ }
70
+ init() {
71
+ const { editor } = this.props;
72
+ if (editor && editor.options.element) {
73
+ if (editor.contentComponent) {
74
+ return;
75
+ }
76
+ const element = this.editorContentRef.current;
77
+ element.append(...editor.options.element.childNodes);
78
+ editor.setOptions({
79
+ element
80
+ });
81
+ editor.contentComponent = this;
82
+ editor.createNodeViews();
83
+ this.initialized = true;
84
+ }
85
+ }
86
+ maybeFlushSync(fn) {
87
+ if (this.initialized) {
88
+ flushSync(fn);
89
+ } else {
90
+ fn();
91
+ }
92
+ }
93
+ setRenderer(id, renderer) {
94
+ this.maybeFlushSync(() => {
95
+ this.setState(({ renderers }) => ({
96
+ renderers: {
97
+ ...renderers,
98
+ [id]: renderer
99
+ }
100
+ }));
101
+ });
102
+ }
103
+ removeRenderer(id) {
104
+ this.maybeFlushSync(() => {
105
+ this.setState(({ renderers }) => {
106
+ const nextRenderers = { ...renderers };
107
+ delete nextRenderers[id];
108
+ return { renderers: nextRenderers };
109
+ });
110
+ });
111
+ }
112
+ componentWillUnmount() {
113
+ const { editor } = this.props;
114
+ if (!editor) {
115
+ return;
116
+ }
117
+ if (!editor.isDestroyed) {
118
+ editor.view.setProps({
119
+ nodeViews: {}
120
+ });
121
+ }
122
+ editor.contentComponent = null;
123
+ if (!editor.options.element.firstChild) {
124
+ return;
125
+ }
126
+ const newElement = document.createElement("div");
127
+ newElement.append(...editor.options.element.childNodes);
128
+ editor.setOptions({
129
+ element: newElement
130
+ });
131
+ }
132
+ render() {
133
+ const { editor, ...rest } = this.props;
134
+ return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("div", { ref: this.editorContentRef, ...rest }), /* @__PURE__ */ React2.createElement(Portals, { renderers: this.state.renderers }));
135
+ }
136
+ };
137
+ var EditorContent = React2.memo(PureEditorContent);
138
+
139
+ // src/FloatingMenu.tsx
140
+ import { FloatingMenuPlugin } from "@tiptap/extension-floating-menu";
141
+ import React3, {
142
+ useEffect as useEffect2,
143
+ useState as useState2
144
+ } from "react";
145
+ var FloatingMenu = (props) => {
146
+ const [element, setElement] = useState2(null);
147
+ useEffect2(() => {
148
+ if (!element) {
149
+ return;
150
+ }
151
+ if (props.editor.isDestroyed) {
152
+ return;
153
+ }
154
+ const {
155
+ pluginKey = "floatingMenu",
156
+ editor,
157
+ tippyOptions = {},
158
+ shouldShow = null
159
+ } = props;
160
+ const plugin = FloatingMenuPlugin({
161
+ pluginKey,
162
+ editor,
163
+ element,
164
+ tippyOptions,
165
+ shouldShow
166
+ });
167
+ editor.registerPlugin(plugin);
168
+ return () => editor.unregisterPlugin(pluginKey);
169
+ }, [
170
+ props.editor,
171
+ element
172
+ ]);
173
+ return /* @__PURE__ */ React3.createElement("div", { ref: setElement, className: props.className, style: { visibility: "hidden" } }, props.children);
174
+ };
175
+
176
+ // src/NodeViewContent.tsx
177
+ import React4 from "react";
178
+
179
+ // src/useReactNodeView.ts
180
+ import { createContext, useContext } from "react";
181
+ var ReactNodeViewContext = createContext({
182
+ onDragStart: void 0
183
+ });
184
+ var useReactNodeView = () => useContext(ReactNodeViewContext);
185
+
186
+ // src/NodeViewContent.tsx
187
+ var NodeViewContent = (props) => {
188
+ const Tag = props.as || "div";
189
+ const { nodeViewContentRef } = useReactNodeView();
190
+ return /* @__PURE__ */ React4.createElement(
191
+ Tag,
192
+ {
193
+ ...props,
194
+ ref: nodeViewContentRef,
195
+ "data-node-view-content": "",
196
+ style: {
197
+ whiteSpace: "pre-wrap",
198
+ ...props.style
199
+ }
200
+ }
201
+ );
202
+ };
203
+
204
+ // src/NodeViewWrapper.tsx
205
+ import React5 from "react";
206
+ var NodeViewWrapper = React5.forwardRef((props, ref) => {
207
+ const { onDragStart } = useReactNodeView();
208
+ const Tag = props.as || "div";
209
+ return /* @__PURE__ */ React5.createElement(
210
+ Tag,
211
+ {
212
+ ...props,
213
+ ref,
214
+ "data-node-view-wrapper": "",
215
+ onDragStart,
216
+ style: {
217
+ whiteSpace: "normal",
218
+ ...props.style
219
+ }
220
+ }
221
+ );
222
+ });
223
+
224
+ // src/ReactNodeViewRenderer.tsx
225
+ import {
226
+ NodeView
227
+ } from "@tiptap/core";
228
+ import React7 from "react";
229
+
230
+ // src/ReactRenderer.tsx
231
+ import React6 from "react";
232
+ function isClassComponent(Component) {
233
+ return !!(typeof Component === "function" && Component.prototype && Component.prototype.isReactComponent);
234
+ }
235
+ function isForwardRefComponent(Component) {
236
+ var _a;
237
+ return !!(typeof Component === "object" && ((_a = Component.$$typeof) == null ? void 0 : _a.toString()) === "Symbol(react.forward_ref)");
238
+ }
239
+ var ReactRenderer = class {
240
+ constructor(component, {
241
+ editor,
242
+ props = {},
243
+ as = "div",
244
+ className = ""
245
+ }) {
246
+ this.ref = null;
247
+ this.id = Math.floor(Math.random() * 4294967295).toString();
248
+ this.component = component;
249
+ this.editor = editor;
250
+ this.props = props;
251
+ this.element = document.createElement(as);
252
+ this.element.classList.add("react-renderer");
253
+ if (className) {
254
+ this.element.classList.add(...className.split(" "));
255
+ }
256
+ this.render();
257
+ }
258
+ render() {
259
+ var _a, _b;
260
+ const Component = this.component;
261
+ const props = this.props;
262
+ if (isClassComponent(Component) || isForwardRefComponent(Component)) {
263
+ props.ref = (ref) => {
264
+ this.ref = ref;
265
+ };
266
+ }
267
+ this.reactElement = /* @__PURE__ */ React6.createElement(Component, { ...props });
268
+ (_b = (_a = this.editor) == null ? void 0 : _a.contentComponent) == null ? void 0 : _b.setRenderer(this.id, this);
269
+ }
270
+ updateProps(props = {}) {
271
+ this.props = {
272
+ ...this.props,
273
+ ...props
274
+ };
275
+ this.render();
276
+ }
277
+ destroy() {
278
+ var _a, _b;
279
+ (_b = (_a = this.editor) == null ? void 0 : _a.contentComponent) == null ? void 0 : _b.removeRenderer(this.id);
280
+ }
281
+ };
282
+
283
+ // src/ReactNodeViewRenderer.tsx
284
+ var ReactNodeView = class extends NodeView {
285
+ mount() {
286
+ const props = {
287
+ editor: this.editor,
288
+ node: this.node,
289
+ decorations: this.decorations,
290
+ selected: false,
291
+ extension: this.extension,
292
+ getPos: () => this.getPos(),
293
+ updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
294
+ deleteNode: () => this.deleteNode()
295
+ };
296
+ if (!this.component.displayName) {
297
+ const capitalizeFirstChar = (string) => {
298
+ return string.charAt(0).toUpperCase() + string.substring(1);
299
+ };
300
+ this.component.displayName = capitalizeFirstChar(this.extension.name);
301
+ }
302
+ const ReactNodeViewProvider = (componentProps) => {
303
+ const Component = this.component;
304
+ const onDragStart = this.onDragStart.bind(this);
305
+ const nodeViewContentRef = (element) => {
306
+ if (element && this.contentDOMElement && element.firstChild !== this.contentDOMElement) {
307
+ element.appendChild(this.contentDOMElement);
308
+ }
309
+ };
310
+ return /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(ReactNodeViewContext.Provider, { value: { onDragStart, nodeViewContentRef } }, /* @__PURE__ */ React7.createElement(Component, { ...componentProps })));
311
+ };
312
+ ReactNodeViewProvider.displayName = "ReactNodeView";
313
+ this.contentDOMElement = this.node.isLeaf ? null : document.createElement(this.node.isInline ? "span" : "div");
314
+ if (this.contentDOMElement) {
315
+ this.contentDOMElement.style.whiteSpace = "inherit";
316
+ }
317
+ let as = this.node.isInline ? "span" : "div";
318
+ if (this.options.as) {
319
+ as = this.options.as;
320
+ }
321
+ const { className = "" } = this.options;
322
+ this.renderer = new ReactRenderer(ReactNodeViewProvider, {
323
+ editor: this.editor,
324
+ props,
325
+ as,
326
+ className: `node-${this.node.type.name} ${className}`.trim()
327
+ });
328
+ }
329
+ get dom() {
330
+ var _a;
331
+ if (this.renderer.element.firstElementChild && !((_a = this.renderer.element.firstElementChild) == null ? void 0 : _a.hasAttribute("data-node-view-wrapper"))) {
332
+ throw Error("Please use the NodeViewWrapper component for your node view.");
333
+ }
334
+ return this.renderer.element;
335
+ }
336
+ get contentDOM() {
337
+ if (this.node.isLeaf) {
338
+ return null;
339
+ }
340
+ return this.contentDOMElement;
341
+ }
342
+ update(node, decorations) {
343
+ const updateProps = (props) => {
344
+ this.renderer.updateProps(props);
345
+ };
346
+ if (node.type !== this.node.type) {
347
+ return false;
348
+ }
349
+ if (typeof this.options.update === "function") {
350
+ const oldNode = this.node;
351
+ const oldDecorations = this.decorations;
352
+ this.node = node;
353
+ this.decorations = decorations;
354
+ return this.options.update({
355
+ oldNode,
356
+ oldDecorations,
357
+ newNode: node,
358
+ newDecorations: decorations,
359
+ updateProps: () => updateProps({ node, decorations })
360
+ });
361
+ }
362
+ if (node === this.node && this.decorations === decorations) {
363
+ return true;
364
+ }
365
+ this.node = node;
366
+ this.decorations = decorations;
367
+ updateProps({ node, decorations });
368
+ return true;
369
+ }
370
+ selectNode() {
371
+ this.renderer.updateProps({
372
+ selected: true
373
+ });
374
+ }
375
+ deselectNode() {
376
+ this.renderer.updateProps({
377
+ selected: false
378
+ });
379
+ }
380
+ destroy() {
381
+ this.renderer.destroy();
382
+ this.contentDOMElement = null;
383
+ }
384
+ };
385
+ function ReactNodeViewRenderer(component, options) {
386
+ return (props) => {
387
+ if (!props.editor.contentComponent) {
388
+ return {};
389
+ }
390
+ return new ReactNodeView(component, props, options);
391
+ };
392
+ }
393
+
394
+ // src/useEditor.ts
395
+ import { useEffect as useEffect3, useState as useState3 } from "react";
396
+ function useForceUpdate() {
397
+ const [, setValue] = useState3(0);
398
+ return () => setValue((value) => value + 1);
399
+ }
400
+ var useEditor = (options = {}, deps = []) => {
401
+ const [editor, setEditor] = useState3(null);
402
+ const forceUpdate = useForceUpdate();
403
+ useEffect3(() => {
404
+ let isMounted = true;
405
+ const instance = new Editor(options);
406
+ setEditor(instance);
407
+ instance.on("transaction", () => {
408
+ requestAnimationFrame(() => {
409
+ requestAnimationFrame(() => {
410
+ if (isMounted) {
411
+ forceUpdate();
412
+ }
413
+ });
414
+ });
415
+ });
416
+ return () => {
417
+ instance.destroy();
418
+ isMounted = false;
419
+ };
420
+ }, deps);
421
+ return editor;
422
+ };
423
+
424
+ // src/index.ts
425
+ export * from "@tiptap/core";
426
+ export {
427
+ BubbleMenu,
428
+ Editor,
429
+ EditorContent,
430
+ FloatingMenu,
431
+ NodeViewContent,
432
+ NodeViewWrapper,
433
+ PureEditorContent,
434
+ ReactNodeViewRenderer,
435
+ ReactRenderer,
436
+ useEditor
437
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tiptap/react",
3
3
  "description": "React components for tiptap",
4
- "version": "2.0.0-beta.21",
4
+ "version": "2.0.0-beta.211",
5
5
  "homepage": "https://tiptap.dev",
6
6
  "keywords": [
7
7
  "tiptap",
@@ -12,28 +12,57 @@
12
12
  "type": "github",
13
13
  "url": "https://github.com/sponsors/ueberdosis"
14
14
  },
15
- "main": "dist/tiptap-react.cjs.js",
16
- "umd": "dist/tiptap-react.umd.js",
17
- "module": "dist/tiptap-react.esm.js",
18
- "unpkg": "dist/tiptap-react.bundle.umd.min.js",
19
- "types": "dist/packages/react/src/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js",
19
+ "require": "./dist/index.cjs"
20
+ }
21
+ },
22
+ "main": "dist/index.cjs",
23
+ "module": "dist/index.js",
24
+ "types": "dist/index.d.ts",
25
+ "type": "module",
20
26
  "files": [
21
27
  "src",
22
28
  "dist"
23
29
  ],
30
+ "devDependencies": {
31
+ "@tiptap/core": "^2.0.0-beta.211",
32
+ "@tiptap/pm": "^2.0.0-beta.211",
33
+ "@types/react": "^18.0.1",
34
+ "@types/react-dom": "^18.0.0",
35
+ "react": "^18.0.0",
36
+ "react-dom": "^18.0.0"
37
+ },
24
38
  "peerDependencies": {
25
- "@tiptap/core": "^2.0.0-beta.1",
26
- "react": "^17.0.1",
27
- "react-dom": "^17.0.1"
39
+ "@tiptap/core": "^2.0.0-beta.209",
40
+ "@tiptap/pm": "^2.0.0-beta.209",
41
+ "react": "^17.0.0 || ^18.0.0",
42
+ "react-dom": "^17.0.0 || ^18.0.0"
28
43
  },
29
44
  "dependencies": {
30
- "@tiptap/extension-bubble-menu": "^2.0.0-beta.7",
31
- "@tiptap/extension-floating-menu": "^2.0.0-beta.4",
32
- "prosemirror-view": "^1.18.2"
45
+ "@tiptap/extension-bubble-menu": "^2.0.0-beta.211",
46
+ "@tiptap/extension-floating-menu": "^2.0.0-beta.211"
33
47
  },
34
- "devDependencies": {
35
- "@types/react": "^17.0.3",
36
- "@types/react-dom": "^17.0.3"
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "https://github.com/ueberdosis/tiptap",
51
+ "directory": "packages/react"
52
+ },
53
+ "sideEffects": false,
54
+ "scripts": {
55
+ "build": "tsup"
37
56
  },
38
- "gitHead": "ea9f5410915f45f7ba12d5c816965976f1956862"
57
+ "tsup": {
58
+ "entry": [
59
+ "src/index.ts"
60
+ ],
61
+ "dts": true,
62
+ "splitting": true,
63
+ "format": [
64
+ "esm",
65
+ "cjs"
66
+ ]
67
+ }
39
68
  }
@@ -1,29 +1,45 @@
1
- import React, { useEffect, useRef } from 'react'
2
- import { BubbleMenuPlugin, BubbleMenuPluginKey, BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu'
1
+ import { BubbleMenuPlugin, BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu'
2
+ import React, { useEffect, useState } from 'react'
3
3
 
4
- export type BubbleMenuProps = Omit<BubbleMenuPluginProps, 'element'> & {
5
- className?: string,
6
- }
4
+ type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
5
+
6
+ export type BubbleMenuProps = Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>, 'element'> & {
7
+ className?: string;
8
+ children: React.ReactNode;
9
+ updateDelay?: number;
10
+ };
7
11
 
8
- export const BubbleMenu: React.FC<BubbleMenuProps> = props => {
9
- const element = useRef<HTMLDivElement>(null)
12
+ export const BubbleMenu = (props: BubbleMenuProps) => {
13
+ const [element, setElement] = useState<HTMLDivElement | null>(null)
10
14
 
11
15
  useEffect(() => {
12
- const { editor, tippyOptions } = props
16
+ if (!element) {
17
+ return
18
+ }
13
19
 
14
- editor.registerPlugin(BubbleMenuPlugin({
20
+ if (props.editor.isDestroyed) {
21
+ return
22
+ }
23
+
24
+ const {
25
+ pluginKey = 'bubbleMenu', editor, tippyOptions = {}, updateDelay, shouldShow = null,
26
+ } = props
27
+
28
+ const plugin = BubbleMenuPlugin({
29
+ updateDelay,
15
30
  editor,
16
- element: element.current as HTMLElement,
31
+ element,
32
+ pluginKey,
33
+ shouldShow,
17
34
  tippyOptions,
18
- }))
35
+ })
19
36
 
20
- return () => {
21
- editor.unregisterPlugin(BubbleMenuPluginKey)
22
- }
23
- }, [])
37
+ editor.registerPlugin(plugin)
38
+ return () => editor.unregisterPlugin(pluginKey)
39
+ }, [props.editor, element])
24
40
 
25
41
  return (
26
- <div ref={element} className={props.className} style={{ visibility: 'hidden' }}>
42
+ <div ref={setElement} className={props.className} style={{ visibility: 'hidden' }}>
27
43
  {props.children}
28
44
  </div>
29
45
  )
package/src/Editor.ts CHANGED
@@ -1,7 +1,14 @@
1
- import React from 'react'
2
1
  import { Editor as CoreEditor } from '@tiptap/core'
2
+ import React from 'react'
3
+
3
4
  import { EditorContentProps, EditorContentState } from './EditorContent'
5
+ import { ReactRenderer } from './ReactRenderer'
6
+
7
+ type ContentComponent = React.Component<EditorContentProps, EditorContentState> & {
8
+ setRenderer(id: string, renderer: ReactRenderer): void;
9
+ removeRenderer(id: string): void;
10
+ }
4
11
 
5
12
  export class Editor extends CoreEditor {
6
- public contentComponent: React.Component<EditorContentProps, EditorContentState> | null = null
13
+ public contentComponent: ContentComponent | null = null
7
14
  }