@handlewithcare/react-prosemirror 3.2.4 → 3.2.6

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.
package/README.md CHANGED
@@ -713,6 +713,10 @@ Select a piece of the EditorState, a la Redux’s `useSelector`.
713
713
  This hook will only trigger a re-render of the consuming component if the return
714
714
  value of the selector changes.
715
715
 
716
+ The provided selector must return the same value when it receieves the same
717
+ inputs. If the selector creates and returns a new object when it runs, you'll
718
+ enter a rerender loop.
719
+
716
720
  ### `useNodePos`
717
721
 
718
722
  ```ts
@@ -39,7 +39,8 @@ function useEditor(mount, options) {
39
39
  options.plugins
40
40
  ]);
41
41
  const dispatchTransaction = (0, _react.useCallback)(function dispatchTransaction(tr) {
42
- if (flushSyncRef.current) {
42
+ const async = tr.getMeta("async");
43
+ if (flushSyncRef.current && !async) {
43
44
  (0, _reactdom.flushSync)(()=>{
44
45
  if (!options.state) {
45
46
  setState((s)=>s.apply(tr));
@@ -12,8 +12,6 @@ const _react = require("react");
12
12
  const _EditorStateStoreContext = require("../contexts/EditorStateStoreContext.js");
13
13
  function useEditorStateSelector(selector) {
14
14
  const store = (0, _react.useContext)(_EditorStateStoreContext.EditorStateStoreContext);
15
- const selectorRef = (0, _react.useRef)(selector);
16
- selectorRef.current = selector;
17
- const getSnapshot = ()=>selectorRef.current(store.getState());
15
+ const getSnapshot = ()=>selector(store.getState());
18
16
  return (0, _react.useSyncExternalStore)(store.subscribe, getSnapshot, getSnapshot);
19
17
  }
@@ -11,12 +11,26 @@ Object.defineProperty(exports, "useEditor", {
11
11
  }
12
12
  });
13
13
  const _core = require("@tiptap/core");
14
- const _react = require("@tiptap/react");
15
- const _react1 = require("react");
14
+ const _react = require("react");
16
15
  const _index = require("use-sync-external-store/shim/index.js");
17
- const isDev = process.env.NODE_ENV !== "production";
18
- const isSSR = typeof window === "undefined";
19
- const isNext = isSSR || Boolean(typeof window !== "undefined" && window.next);
16
+ const _StaticEditorView = require("../../StaticEditorView.js");
17
+ let ReactEditor = class ReactEditor extends _core.Editor {
18
+ constructor(options){
19
+ super(options);
20
+ // @ts-expect-error Internal property
21
+ this.editorView = new _StaticEditorView.StaticEditorView({
22
+ // @ts-expect-error Internal property
23
+ state: this.editorState.reconfigure({
24
+ plugins: this.extensionManager.plugins
25
+ }),
26
+ ...this.options.editorProps,
27
+ attributes: {
28
+ role: "textbox",
29
+ ...this.options.editorProps.attributes
30
+ }
31
+ });
32
+ }
33
+ };
20
34
  /**
21
35
  * This class handles the creation, destruction, and re-creation of the editor instance.
22
36
  */ let EditorInstanceManager = class EditorInstanceManager {
@@ -45,7 +59,7 @@ const isNext = isSSR || Boolean(typeof window !== "undefined" && window.next);
45
59
  constructor(options){
46
60
  this.options = options;
47
61
  this.subscriptions = new Set();
48
- this.setEditor(this.getInitialEditor());
62
+ this.setEditor(this.createEditor());
49
63
  this.scheduleDestroy();
50
64
  this.getEditor = this.getEditor.bind(this);
51
65
  this.getServerSnapshot = this.getServerSnapshot.bind(this);
@@ -61,34 +75,6 @@ const isNext = isSSR || Boolean(typeof window !== "undefined" && window.next);
61
75
  // Notify all subscribers that the editor instance has been created
62
76
  this.subscriptions.forEach((cb)=>cb());
63
77
  }
64
- getInitialEditor() {
65
- if (this.options.current.immediatelyRender === undefined) {
66
- if (isSSR || isNext) {
67
- if (isDev) {
68
- /**
69
- * Throw an error in development, to make sure the developer is aware that tiptap cannot be SSR'd
70
- * and that they need to set `immediatelyRender` to `false` to avoid hydration mismatches.
71
- */ throw new Error("Tiptap Error: SSR has been detected, please set `immediatelyRender` explicitly to `false` to avoid hydration mismatches.");
72
- }
73
- // Best faith effort in production, run the code in the legacy mode to avoid hydration mismatches and errors in production
74
- return null;
75
- }
76
- // Default to immediately rendering when client-side rendering
77
- return this.createEditor();
78
- }
79
- // ----- FORKED CHANGES -----
80
- // if (this.options.current.immediatelyRender && isSSR && isDev) {
81
- // // Warn in development, to make sure the developer is aware that tiptap cannot be SSR'd, set `immediatelyRender` to `false` to avoid hydration mismatches.
82
- // throw new Error(
83
- // "Tiptap Error: SSR has been detected, and `immediatelyRender` has been set to `true` this is an unsupported configuration that may result in errors, explicitly set `immediatelyRender` to `false` to avoid hydration mismatches."
84
- // );
85
- // }
86
- // ----- END FORKED CHANGES -----
87
- if (this.options.current.immediatelyRender) {
88
- return this.createEditor();
89
- }
90
- return null;
91
- }
92
78
  /**
93
79
  * Create a new editor instance. And attach event listeners.
94
80
  */ createEditor() {
@@ -169,7 +155,7 @@ const isNext = isSSR || Boolean(typeof window !== "undefined" && window.next);
169
155
  return _this.options.current.onDelete?.(...args);
170
156
  }
171
157
  };
172
- const editor = new _core.Editor(optionsToApply);
158
+ const editor = new ReactEditor(optionsToApply);
173
159
  // no need to keep track of the event listeners, they will be removed when the editor is destroyed
174
160
  return editor;
175
161
  }
@@ -320,30 +306,13 @@ const isNext = isSSR || Boolean(typeof window !== "undefined" && window.next);
320
306
  };
321
307
  function useEditor() {
322
308
  let options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {}, deps = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : [];
323
- const mostRecentOptions = (0, _react1.useRef)(options);
309
+ const mostRecentOptions = (0, _react.useRef)(options);
324
310
  mostRecentOptions.current = options;
325
- const [instanceManager] = (0, _react1.useState)(()=>new EditorInstanceManager(mostRecentOptions));
311
+ const [instanceManager] = (0, _react.useState)(()=>new EditorInstanceManager(mostRecentOptions));
326
312
  const editor = (0, _index.useSyncExternalStore)(instanceManager.subscribe, instanceManager.getEditor, instanceManager.getServerSnapshot);
327
- (0, _react1.useDebugValue)(editor);
313
+ (0, _react.useDebugValue)(editor);
328
314
  // This effect will handle creating/updating the editor instance
329
315
  // eslint-disable-next-line react-hooks/exhaustive-deps
330
- (0, _react1.useEffect)(instanceManager.onRender(deps));
331
- // The default behavior is to re-render on each transaction
332
- // This is legacy behavior that will be removed in future versions
333
- (0, _react.useEditorState)({
334
- editor,
335
- selector: (param)=>{
336
- let { transactionNumber } = param;
337
- if (options.shouldRerenderOnTransaction === false || options.shouldRerenderOnTransaction === undefined) {
338
- // This will prevent the editor from re-rendering on each transaction
339
- return null;
340
- }
341
- // This will avoid re-rendering on the first transaction when `immediatelyRender` is set to `true`
342
- if (options.immediatelyRender && transactionNumber === 0) {
343
- return 0;
344
- }
345
- return transactionNumber + 1;
346
- }
347
- });
316
+ (0, _react.useEffect)(instanceManager.onRender(deps));
348
317
  return editor;
349
318
  }
@@ -8,7 +8,6 @@ Object.defineProperty(exports, "useTiptapEditor", {
8
8
  return useTiptapEditor;
9
9
  }
10
10
  });
11
- const _StaticEditorView = require("../../StaticEditorView.js");
12
11
  const _ReactProseMirror = require("../extensions/ReactProseMirror.js");
13
12
  const _ReactProseMirrorCommands = require("../extensions/ReactProseMirrorCommands.js");
14
13
  const _useEditor = require("./useEditor.js");
@@ -28,34 +27,9 @@ function useTiptapEditor(options, deps) {
28
27
  };
29
28
  extensions.push(_ReactProseMirrorCommands.ReactProseMirrorCommands);
30
29
  }
31
- const editor = (0, _useEditor.useEditor)({
30
+ return (0, _useEditor.useEditor)({
32
31
  ...options,
33
32
  extensions,
34
33
  element: null
35
34
  }, deps);
36
- // @ts-expect-error private property
37
- editor.editorView ??= new _StaticEditorView.StaticEditorView({
38
- // @ts-expect-error private property
39
- state: editor.editorState,
40
- ...editor.options.editorProps,
41
- attributes: {
42
- role: "textbox",
43
- ...editor.options.editorProps.attributes
44
- }
45
- });
46
- // @ts-expect-error private property
47
- const stateHasPlugins = !!editor.editorState.plugins.length;
48
- const stateNeedsReconfigure = !stateHasPlugins && !editor.isDestroyed;
49
- if (stateNeedsReconfigure) {
50
- const managerPlugins = editor.extensionManager.plugins;
51
- if (managerPlugins.length) {
52
- // @ts-expect-error private property
53
- editor.editorState = editor.editorState.reconfigure({
54
- plugins: editor.extensionManager.plugins
55
- });
56
- // @ts-expect-error private property
57
- editor.editorView.updateState(editor.editorState);
58
- }
59
- }
60
- return editor;
61
35
  }
@@ -37,7 +37,8 @@ let didWarnValueDefaultValue = false;
37
37
  options.plugins
38
38
  ]);
39
39
  const dispatchTransaction = useCallback(function dispatchTransaction(tr) {
40
- if (flushSyncRef.current) {
40
+ const async = tr.getMeta("async");
41
+ if (flushSyncRef.current && !async) {
41
42
  flushSync(()=>{
42
43
  if (!options.state) {
43
44
  setState((s)=>s.apply(tr));
@@ -1,4 +1,4 @@
1
- import { useContext, useRef, useSyncExternalStore } from "react";
1
+ import { useContext, useSyncExternalStore } from "react";
2
2
  import { EditorStateStoreContext } from "../contexts/EditorStateStoreContext.js";
3
3
  /**
4
4
  * Select a piece of the EditorState, a la Redux’s
@@ -9,8 +9,6 @@ import { EditorStateStoreContext } from "../contexts/EditorStateStoreContext.js"
9
9
  * changes.
10
10
  */ export function useEditorStateSelector(selector) {
11
11
  const store = useContext(EditorStateStoreContext);
12
- const selectorRef = useRef(selector);
13
- selectorRef.current = selector;
14
- const getSnapshot = ()=>selectorRef.current(store.getState());
12
+ const getSnapshot = ()=>selector(store.getState());
15
13
  return useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
16
14
  }
@@ -1,12 +1,26 @@
1
1
  // Forked from Tiptap's useEditor hook, with modifications to support
2
2
  // server-side rendering
3
3
  import { Editor } from "@tiptap/core";
4
- import { useEditorState } from "@tiptap/react";
5
4
  import { useDebugValue, useEffect, useRef, useState } from "react";
6
5
  import { useSyncExternalStore } from "use-sync-external-store/shim/index.js";
7
- const isDev = process.env.NODE_ENV !== "production";
8
- const isSSR = typeof window === "undefined";
9
- const isNext = isSSR || Boolean(typeof window !== "undefined" && window.next);
6
+ import { StaticEditorView } from "../../StaticEditorView.js";
7
+ let ReactEditor = class ReactEditor extends Editor {
8
+ constructor(options){
9
+ super(options);
10
+ // @ts-expect-error Internal property
11
+ this.editorView = new StaticEditorView({
12
+ // @ts-expect-error Internal property
13
+ state: this.editorState.reconfigure({
14
+ plugins: this.extensionManager.plugins
15
+ }),
16
+ ...this.options.editorProps,
17
+ attributes: {
18
+ role: "textbox",
19
+ ...this.options.editorProps.attributes
20
+ }
21
+ });
22
+ }
23
+ };
10
24
  /**
11
25
  * This class handles the creation, destruction, and re-creation of the editor instance.
12
26
  */ let EditorInstanceManager = class EditorInstanceManager {
@@ -35,7 +49,7 @@ const isNext = isSSR || Boolean(typeof window !== "undefined" && window.next);
35
49
  constructor(options){
36
50
  this.options = options;
37
51
  this.subscriptions = new Set();
38
- this.setEditor(this.getInitialEditor());
52
+ this.setEditor(this.createEditor());
39
53
  this.scheduleDestroy();
40
54
  this.getEditor = this.getEditor.bind(this);
41
55
  this.getServerSnapshot = this.getServerSnapshot.bind(this);
@@ -51,34 +65,6 @@ const isNext = isSSR || Boolean(typeof window !== "undefined" && window.next);
51
65
  // Notify all subscribers that the editor instance has been created
52
66
  this.subscriptions.forEach((cb)=>cb());
53
67
  }
54
- getInitialEditor() {
55
- if (this.options.current.immediatelyRender === undefined) {
56
- if (isSSR || isNext) {
57
- if (isDev) {
58
- /**
59
- * Throw an error in development, to make sure the developer is aware that tiptap cannot be SSR'd
60
- * and that they need to set `immediatelyRender` to `false` to avoid hydration mismatches.
61
- */ throw new Error("Tiptap Error: SSR has been detected, please set `immediatelyRender` explicitly to `false` to avoid hydration mismatches.");
62
- }
63
- // Best faith effort in production, run the code in the legacy mode to avoid hydration mismatches and errors in production
64
- return null;
65
- }
66
- // Default to immediately rendering when client-side rendering
67
- return this.createEditor();
68
- }
69
- // ----- FORKED CHANGES -----
70
- // if (this.options.current.immediatelyRender && isSSR && isDev) {
71
- // // Warn in development, to make sure the developer is aware that tiptap cannot be SSR'd, set `immediatelyRender` to `false` to avoid hydration mismatches.
72
- // throw new Error(
73
- // "Tiptap Error: SSR has been detected, and `immediatelyRender` has been set to `true` this is an unsupported configuration that may result in errors, explicitly set `immediatelyRender` to `false` to avoid hydration mismatches."
74
- // );
75
- // }
76
- // ----- END FORKED CHANGES -----
77
- if (this.options.current.immediatelyRender) {
78
- return this.createEditor();
79
- }
80
- return null;
81
- }
82
68
  /**
83
69
  * Create a new editor instance. And attach event listeners.
84
70
  */ createEditor() {
@@ -159,7 +145,7 @@ const isNext = isSSR || Boolean(typeof window !== "undefined" && window.next);
159
145
  return _this.options.current.onDelete?.(...args);
160
146
  }
161
147
  };
162
- const editor = new Editor(optionsToApply);
148
+ const editor = new ReactEditor(optionsToApply);
163
149
  // no need to keep track of the event listeners, they will be removed when the editor is destroyed
164
150
  return editor;
165
151
  }
@@ -318,22 +304,5 @@ export function useEditor() {
318
304
  // This effect will handle creating/updating the editor instance
319
305
  // eslint-disable-next-line react-hooks/exhaustive-deps
320
306
  useEffect(instanceManager.onRender(deps));
321
- // The default behavior is to re-render on each transaction
322
- // This is legacy behavior that will be removed in future versions
323
- useEditorState({
324
- editor,
325
- selector: (param)=>{
326
- let { transactionNumber } = param;
327
- if (options.shouldRerenderOnTransaction === false || options.shouldRerenderOnTransaction === undefined) {
328
- // This will prevent the editor from re-rendering on each transaction
329
- return null;
330
- }
331
- // This will avoid re-rendering on the first transaction when `immediatelyRender` is set to `true`
332
- if (options.immediatelyRender && transactionNumber === 0) {
333
- return 0;
334
- }
335
- return transactionNumber + 1;
336
- }
337
- });
338
307
  return editor;
339
308
  }
@@ -1,4 +1,3 @@
1
- import { StaticEditorView } from "../../StaticEditorView.js";
2
1
  import { ReactProseMirror } from "../extensions/ReactProseMirror.js";
3
2
  import { ReactProseMirrorCommands } from "../extensions/ReactProseMirrorCommands.js";
4
3
  import { useEditor } from "./useEditor.js";
@@ -24,34 +23,9 @@ import { useEditor } from "./useEditor.js";
24
23
  };
25
24
  extensions.push(ReactProseMirrorCommands);
26
25
  }
27
- const editor = useEditor({
26
+ return useEditor({
28
27
  ...options,
29
28
  extensions,
30
29
  element: null
31
30
  }, deps);
32
- // @ts-expect-error private property
33
- editor.editorView ??= new StaticEditorView({
34
- // @ts-expect-error private property
35
- state: editor.editorState,
36
- ...editor.options.editorProps,
37
- attributes: {
38
- role: "textbox",
39
- ...editor.options.editorProps.attributes
40
- }
41
- });
42
- // @ts-expect-error private property
43
- const stateHasPlugins = !!editor.editorState.plugins.length;
44
- const stateNeedsReconfigure = !stateHasPlugins && !editor.isDestroyed;
45
- if (stateNeedsReconfigure) {
46
- const managerPlugins = editor.extensionManager.plugins;
47
- if (managerPlugins.length) {
48
- // @ts-expect-error private property
49
- editor.editorState = editor.editorState.reconfigure({
50
- plugins: editor.extensionManager.plugins
51
- });
52
- // @ts-expect-error private property
53
- editor.editorView.updateState(editor.editorState);
54
- }
55
- }
56
- return editor;
57
31
  }