@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.
- package/README.md +5 -71
- package/dist/cjs/StaticEditorView.js +0 -3
- package/dist/cjs/dom.js +8 -3
- package/dist/cjs/plugins/beforeInputPlugin.js +1 -1
- package/dist/cjs/viewdesc.js +6 -0
- package/dist/esm/StaticEditorView.js +0 -3
- package/dist/esm/dom.js +8 -3
- package/dist/esm/hooks/useEditorEffect.js +0 -4
- package/dist/esm/hooks/useEditorEventCallback.js +5 -3
- package/dist/esm/plugins/beforeInputPlugin.js +1 -1
- package/dist/esm/viewdesc.js +6 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/AbstractEditorView.d.ts +0 -1
- package/dist/types/StaticEditorView.d.ts +0 -1
- package/dist/types/hooks/useEditorEffect.d.ts +0 -4
- package/dist/types/hooks/useEditorEventCallback.d.ts +5 -3
- package/dist/types/viewdesc.d.ts +2 -0
- package/package.json +8 -11
- package/dist/cjs/tiptap/TiptapEditor.js +0 -34
- package/dist/cjs/tiptap/TiptapEditorContent.js +0 -136
- package/dist/cjs/tiptap/TiptapEditorView.js +0 -99
- package/dist/cjs/tiptap/extensions/ReactProseMirror.js +0 -20
- package/dist/cjs/tiptap/hooks/useTiptapEditor.js +0 -29
- package/dist/cjs/tiptap/hooks/useTiptapEditorEffect.js +0 -32
- package/dist/cjs/tiptap/hooks/useTiptapEditorEventCallback.js +0 -35
- package/dist/cjs/tiptap/index.js +0 -44
- package/dist/cjs/tiptap/tiptapNodeView.js +0 -188
- package/dist/esm/tiptap/TiptapEditor.js +0 -24
- package/dist/esm/tiptap/TiptapEditorContent.js +0 -85
- package/dist/esm/tiptap/TiptapEditorView.js +0 -50
- package/dist/esm/tiptap/extensions/ReactProseMirror.js +0 -10
- package/dist/esm/tiptap/hooks/useTiptapEditor.js +0 -19
- package/dist/esm/tiptap/hooks/useTiptapEditorEffect.js +0 -39
- package/dist/esm/tiptap/hooks/useTiptapEditorEventCallback.js +0 -35
- package/dist/esm/tiptap/index.js +0 -8
- package/dist/esm/tiptap/tiptapNodeView.js +0 -156
- package/dist/types/tiptap/TiptapEditor.d.ts +0 -6
- package/dist/types/tiptap/TiptapEditorContent.d.ts +0 -19
- package/dist/types/tiptap/TiptapEditorView.d.ts +0 -13
- package/dist/types/tiptap/extensions/ReactProseMirror.d.ts +0 -2
- package/dist/types/tiptap/hooks/useTiptapEditor.d.ts +0 -4
- package/dist/types/tiptap/hooks/useTiptapEditorEffect.d.ts +0 -21
- package/dist/types/tiptap/hooks/useTiptapEditorEventCallback.d.ts +0 -13
- package/dist/types/tiptap/index.d.ts +0 -8
- package/dist/types/tiptap/tiptapNodeView.d.ts +0 -48
package/README.md
CHANGED
|
@@ -9,17 +9,6 @@
|
|
|
9
9
|
|
|
10
10
|
[](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
|
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
|
-
|
|
77
|
-
if (
|
|
78
|
-
|
|
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;
|
package/dist/cjs/viewdesc.js
CHANGED
|
@@ -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
|
}
|
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
|
-
|
|
38
|
-
if (
|
|
39
|
-
|
|
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
|
|
19
|
-
*
|
|
20
|
-
*
|
|
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;
|
package/dist/esm/viewdesc.js
CHANGED
|
@@ -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
|
}
|