@handlewithcare/react-prosemirror 2.7.0-tiptap.9 → 2.7.1

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 (45) hide show
  1. package/README.md +5 -71
  2. package/dist/cjs/StaticEditorView.js +0 -3
  3. package/dist/cjs/dom.js +8 -3
  4. package/dist/cjs/plugins/beforeInputPlugin.js +1 -1
  5. package/dist/cjs/viewdesc.js +6 -0
  6. package/dist/esm/StaticEditorView.js +0 -3
  7. package/dist/esm/dom.js +8 -3
  8. package/dist/esm/hooks/useEditorEffect.js +0 -4
  9. package/dist/esm/hooks/useEditorEventCallback.js +5 -3
  10. package/dist/esm/plugins/beforeInputPlugin.js +1 -1
  11. package/dist/esm/viewdesc.js +6 -0
  12. package/dist/tsconfig.tsbuildinfo +1 -1
  13. package/dist/types/AbstractEditorView.d.ts +0 -1
  14. package/dist/types/StaticEditorView.d.ts +0 -1
  15. package/dist/types/hooks/useEditorEffect.d.ts +0 -4
  16. package/dist/types/hooks/useEditorEventCallback.d.ts +5 -3
  17. package/dist/types/viewdesc.d.ts +2 -0
  18. package/package.json +8 -11
  19. package/dist/cjs/tiptap/TiptapEditor.js +0 -34
  20. package/dist/cjs/tiptap/TiptapEditorContent.js +0 -136
  21. package/dist/cjs/tiptap/TiptapEditorView.js +0 -99
  22. package/dist/cjs/tiptap/extensions/ReactProseMirror.js +0 -20
  23. package/dist/cjs/tiptap/hooks/useTiptapEditor.js +0 -29
  24. package/dist/cjs/tiptap/hooks/useTiptapEditorEffect.js +0 -32
  25. package/dist/cjs/tiptap/hooks/useTiptapEditorEventCallback.js +0 -35
  26. package/dist/cjs/tiptap/index.js +0 -44
  27. package/dist/cjs/tiptap/tiptapNodeView.js +0 -188
  28. package/dist/esm/tiptap/TiptapEditor.js +0 -24
  29. package/dist/esm/tiptap/TiptapEditorContent.js +0 -85
  30. package/dist/esm/tiptap/TiptapEditorView.js +0 -50
  31. package/dist/esm/tiptap/extensions/ReactProseMirror.js +0 -10
  32. package/dist/esm/tiptap/hooks/useTiptapEditor.js +0 -19
  33. package/dist/esm/tiptap/hooks/useTiptapEditorEffect.js +0 -39
  34. package/dist/esm/tiptap/hooks/useTiptapEditorEventCallback.js +0 -35
  35. package/dist/esm/tiptap/index.js +0 -8
  36. package/dist/esm/tiptap/tiptapNodeView.js +0 -156
  37. package/dist/types/tiptap/TiptapEditor.d.ts +0 -6
  38. package/dist/types/tiptap/TiptapEditorContent.d.ts +0 -19
  39. package/dist/types/tiptap/TiptapEditorView.d.ts +0 -13
  40. package/dist/types/tiptap/extensions/ReactProseMirror.d.ts +0 -2
  41. package/dist/types/tiptap/hooks/useTiptapEditor.d.ts +0 -4
  42. package/dist/types/tiptap/hooks/useTiptapEditorEffect.d.ts +0 -21
  43. package/dist/types/tiptap/hooks/useTiptapEditorEventCallback.d.ts +0 -13
  44. package/dist/types/tiptap/index.d.ts +0 -8
  45. package/dist/types/tiptap/tiptapNodeView.d.ts +0 -48
package/README.md CHANGED
@@ -9,17 +9,6 @@
9
9
 
10
10
  [![Join the chat at https://gitter.im/nytimes/react-prosemirror](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/nytimes/react-prosemirror?utm_source=badge&utm_medium=badge&utm_content=badge)
11
11
 
12
- ## What happened to `@nytimes/react-prosemirror`?
13
-
14
- On Jan. 17, 2025, the maintainers of the `@nytimes/react-prosemirror` library
15
- decided fork the v2 release into its own project. It had been developed entirely
16
- by one developer, @smoores-dev, who had not worked for NYT in years, and was
17
- struggling to move the project at the speed he desired.
18
-
19
- This project is that fork, and all future development on the React-based
20
- ProseMirror renderer will happen in this repository, published as
21
- `@handlewithcare/react-prosemirror` on NPM.
22
-
23
12
  ## Installation
24
13
 
25
14
  _Note_: React ProseMirror releases are coupled to specific prosemirror-view
@@ -88,8 +77,6 @@ import "prosemirror-view/style/prosemirror.css";
88
77
  - [`useEditorEventCallback`](#useeditoreventcallback)
89
78
  - [`useEditorEventListener`](#useeditoreventlistener)
90
79
  - [Building node views with React](#building-node-views-with-react)
91
- - [What's new in v2?](#whats-new-in-v2)
92
- - [API changes](#api-changes)
93
80
  - [API](#api)
94
81
  - [`ProseMirror`](#prosemirror)
95
82
  - [`ProseMirrorDoc`](#prosemirrordoc)
@@ -105,6 +92,7 @@ import "prosemirror-view/style/prosemirror.css";
105
92
  - [`widget`](#widget)
106
93
  - [`reorderSiblings`](#reordersiblings)
107
94
  - [When should I use this?](#when-should-i-use-this)
95
+ - [Migrations](#migrations)
108
96
  - [Looking for someone to collaborate with?](#looking-for-someone-to-collaborate-with)
109
97
 
110
98
  <!-- tocstop -->
@@ -466,64 +454,6 @@ function ProseMirrorEditor() {
466
454
  }
467
455
  ```
468
456
 
469
- ## What's new in v2?
470
-
471
- The v2 release constitutes a significant re-write of the library.
472
-
473
- Previously, React ProseMirror relied on ProseMirror's EditorView to manage the
474
- DOM for the editor. To integrate it with React, we used React
475
- [portals](https://react.dev/reference/react-dom/createPortal) to render
476
- components into ProseMirror-managed DOM nodes, and a
477
- [useLayoutEffect](https://react.dev/reference/react/useLayoutEffect) to sync
478
- state updates with the EditorView instance.
479
-
480
- This approach provided some challenges. First, portals have to be rendered into
481
- existing, stable DOM nodes, so all React-based custom node views ended up
482
- wrapped in extra HTML elements. This made styling and producing valid DOM more
483
- challenging than it should be, and introduced corner cases in browser
484
- contenteditable implementations. Second, we induced a double render for every
485
- ProseMirror update, and during the first render, React-based custom node views
486
- will be rendered with the previous state. This is technically another form of
487
- the state tearing that this library was designed to prevent, as all _other_
488
- React components will be rendered with the new state!
489
-
490
- To overcome these challenges, the new release moves rendering responsibility
491
- entirely into React. We disabled the EditorView's DOM update cycle, and
492
- implemented the same update algorithm that prosemirror-view uses with React
493
- components. The result is a more idiomatic, React-based library, which doesn't
494
- have any of the issues of the original implementation.
495
-
496
- ### API changes
497
-
498
- - The [`ProseMirror`](#prosemirror) component API has changed slightly:
499
- - Instead of passing a `mount` prop with a ref to a child element, users must
500
- render a [`ProseMirrorDoc`](#prosemirrordoc) component as a child of the
501
- `ProseMirror` component.
502
- - The `nodeViews` prop no longer matches the ProseMirror API. Instead,
503
- `nodeViews` should be a map from node type name to React components, each of
504
- which must take [`NodeViewComponentProps`](#nodeviewcomponentprops) as
505
- props. This map should be memoized, or defined outside the React component
506
- entirely.
507
- - To pass standard ProseMirror node view constructors, use the
508
- `customNodeViews` prop
509
- - The API that React-based node views must implement has changed:
510
- - There is no longer any need to provide a ProseMirror node view constructor
511
- function. React-based node views are now just React components that accept
512
- `NodeViewComponentProps` as props.
513
- - Props related to the ProseMirror node, such as the node itself, the `getPos`
514
- function, and decorations, now live in the `nodeProps` property. All other
515
- props _must_ be spread onto the root element of the node view component,
516
- aside from `children`, which may be rendered anywhere in the component, as
517
- appropriate.
518
- - All node view components must forward their ref to the root element of the
519
- component.
520
- - To implement features that would normally live in the node view spec, there
521
- are new hooks, such as [`useStopEvent`](#usestopevent) and
522
- [`useSelectNode`](#useselectnode)
523
- - There is a new export, [`widget`](#widget), which behaves similarly to
524
- `Decoration.widget` from `prosemirror-view`, but takes a React component
525
- instead of a `toDOM` method.
526
-
527
457
  ## API
528
458
 
529
459
  ### `ProseMirror`
@@ -805,6 +735,10 @@ properly track the new positions for the reordered nodes, allowing it to reuse
805
735
  the same keys. This prevents any issues that could be caused by unexpected
806
736
  component remounts.
807
737
 
738
+ ## Migrations
739
+
740
+ - [Migrating from v1 to v2](migration-guides/v2.md)
741
+
808
742
  ## Looking for someone to collaborate with?
809
743
 
810
744
  Reach out to [Handle with Care](https://handlewithcare.dev/#get-in-touch)! We're
@@ -27,9 +27,6 @@ let StaticEditorView = class StaticEditorView {
27
27
  get state() {
28
28
  return this.props.state;
29
29
  }
30
- get isDestroyed() {
31
- return false;
32
- }
33
30
  setProps(props) {
34
31
  return this.update({
35
32
  ...this.props,
package/dist/cjs/dom.js CHANGED
@@ -73,9 +73,14 @@ function scanFor(node, off, targetNode, targetOff, dir) {
73
73
  off = domIndex(node) + (dir < 0 ? 0 : 1);
74
74
  node = parent;
75
75
  } else if (node.nodeType == 1) {
76
- node = node.childNodes[off + (dir < 0 ? -1 : 0)];
77
- if (node.contentEditable == "false") return false;
78
- off = dir < 0 ? nodeSize(node) : 0;
76
+ const child = node.childNodes[off + (dir < 0 ? -1 : 0)];
77
+ if (child.nodeType == 1 && child.contentEditable == "false") {
78
+ if (child.pmViewDesc?.ignoreForSelection) off += dir;
79
+ else return false;
80
+ } else {
81
+ node = child;
82
+ off = dir < 0 ? nodeSize(node) : 0;
83
+ }
79
84
  } else {
80
85
  return false;
81
86
  }
@@ -16,7 +16,7 @@ function insertText(view, eventData) {
16
16
  if (eventData === null) return false;
17
17
  const from = options.from ?? view.state.selection.from;
18
18
  const to = options.to ?? view.state.selection.to;
19
- if (view.someProp("handleTextInput", (f)=>f(view, from, to, eventData))) {
19
+ if (view.someProp("handleTextInput", (f)=>f(view, from, to, eventData, ()=>view.state.tr.insertText(eventData, from, to)))) {
20
20
  return true;
21
21
  }
22
22
  const { tr } = view.state;
@@ -466,6 +466,9 @@ let ViewDesc = class ViewDesc {
466
466
  get ignoreForCoords() {
467
467
  return false;
468
468
  }
469
+ get ignoreForSelection() {
470
+ return false;
471
+ }
469
472
  };
470
473
  let WidgetViewDesc = class WidgetViewDesc extends ViewDesc {
471
474
  widget;
@@ -491,6 +494,9 @@ let WidgetViewDesc = class WidgetViewDesc extends ViewDesc {
491
494
  get domAtom() {
492
495
  return true;
493
496
  }
497
+ get ignoreForSelection() {
498
+ return !!this.widget.type.spec.relaxedSide;
499
+ }
494
500
  get side() {
495
501
  return this.widget.type.side;
496
502
  }
@@ -17,9 +17,6 @@ export class StaticEditorView {
17
17
  get state() {
18
18
  return this.props.state;
19
19
  }
20
- get isDestroyed() {
21
- return false;
22
- }
23
20
  setProps(props) {
24
21
  return this.update({
25
22
  ...this.props,
package/dist/esm/dom.js CHANGED
@@ -34,9 +34,14 @@ function scanFor(node, off, targetNode, targetOff, dir) {
34
34
  off = domIndex(node) + (dir < 0 ? 0 : 1);
35
35
  node = parent;
36
36
  } else if (node.nodeType == 1) {
37
- node = node.childNodes[off + (dir < 0 ? -1 : 0)];
38
- if (node.contentEditable == "false") return false;
39
- off = dir < 0 ? nodeSize(node) : 0;
37
+ const child = node.childNodes[off + (dir < 0 ? -1 : 0)];
38
+ if (child.nodeType == 1 && child.contentEditable == "false") {
39
+ if (child.pmViewDesc?.ignoreForSelection) off += dir;
40
+ else return false;
41
+ } else {
42
+ node = child;
43
+ off = dir < 0 ? nodeSize(node) : 0;
44
+ }
40
45
  } else {
41
46
  return false;
42
47
  }
@@ -15,10 +15,6 @@ import { useLayoutGroupEffect } from "./useLayoutGroupEffect.js";
15
15
  * synchronously after all DOM mutations, but they do so
16
16
  * _after_ the EditorView has been updated, even when the
17
17
  * EditorView lives in an ancestor component.
18
- *
19
- * This hook can only be used in a component that is mounted
20
- * as a child of the TiptapEditorView component, including
21
- * React node view components.
22
18
  */ export function useEditorEffect(effect, dependencies) {
23
19
  const { view, flushSyncRef } = useContext(EditorContext);
24
20
  // The rules of hooks want `effect` to be included in the
@@ -15,9 +15,11 @@ function assertIsReactEditorView(view) {
15
15
  * The callback will be called with the EditorView instance
16
16
  * as its first argument.
17
17
  *
18
- * This hook can only be used in a component that is mounted
19
- * as a child of the TiptapEditorView component, including
20
- * React node view components.
18
+ * This hook is dependent on both the
19
+ * `EditorViewContext.Provider` and the
20
+ * `DeferredLayoutEffectProvider`. It can only be used in a
21
+ * component that is mounted as a child of both of these
22
+ * providers.
21
23
  */ export function useEditorEventCallback(callback) {
22
24
  const ref = useRef(callback);
23
25
  const { view } = useContext(EditorContext);
@@ -6,7 +6,7 @@ function insertText(view, eventData) {
6
6
  if (eventData === null) return false;
7
7
  const from = options.from ?? view.state.selection.from;
8
8
  const to = options.to ?? view.state.selection.to;
9
- if (view.someProp("handleTextInput", (f)=>f(view, from, to, eventData))) {
9
+ if (view.someProp("handleTextInput", (f)=>f(view, from, to, eventData, ()=>view.state.tr.insertText(eventData, from, to)))) {
10
10
  return true;
11
11
  }
12
12
  const { tr } = view.state;
@@ -441,6 +441,9 @@ export class ViewDesc {
441
441
  get ignoreForCoords() {
442
442
  return false;
443
443
  }
444
+ get ignoreForSelection() {
445
+ return false;
446
+ }
444
447
  }
445
448
  // A widget desc represents a widget decoration, which is a DOM node
446
449
  // drawn between the document nodes.
@@ -468,6 +471,9 @@ export class WidgetViewDesc extends ViewDesc {
468
471
  get domAtom() {
469
472
  return true;
470
473
  }
474
+ get ignoreForSelection() {
475
+ return !!this.widget.type.spec.relaxedSide;
476
+ }
471
477
  get side() {
472
478
  return this.widget.type.side;
473
479
  }