@handlewithcare/react-prosemirror 3.1.0-tiptap.52 → 3.1.0
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 +3 -0
- package/dist/cjs/ReactEditorView.js +18 -1
- package/dist/cjs/components/ChildNodeViews.js +4 -4
- package/dist/cjs/components/CursorWrapper.js +9 -11
- package/dist/cjs/components/ProseMirror.js +13 -3
- package/dist/cjs/components/TextNodeView.js +54 -50
- package/dist/cjs/components/WidgetView.js +3 -1
- package/dist/cjs/components/nodes/NodeView.js +40 -4
- package/dist/cjs/contexts/CompositionContext.js +14 -0
- package/dist/cjs/decorations/viewDecorations.js +1 -6
- package/dist/cjs/hooks/useEditor.js +2 -10
- package/dist/cjs/hooks/useMarkViewDescription.js +1 -4
- package/dist/cjs/hooks/useNodeViewDescription.js +1 -22
- package/dist/cjs/plugins/beforeInputPlugin.js +162 -50
- package/dist/cjs/plugins/reactKeys.js +34 -15
- package/dist/cjs/tiptap/tiptapNodeView.js +10 -9
- package/dist/cjs/viewdesc.js +55 -4
- package/dist/esm/ReactEditorView.js +18 -1
- package/dist/esm/components/ChildNodeViews.js +5 -5
- package/dist/esm/components/CursorWrapper.js +9 -11
- package/dist/esm/components/ProseMirror.js +13 -3
- package/dist/esm/components/TextNodeView.js +56 -52
- package/dist/esm/components/WidgetView.js +3 -1
- package/dist/esm/components/nodes/NodeView.js +38 -5
- package/dist/esm/contexts/CompositionContext.js +4 -0
- package/dist/esm/decorations/viewDecorations.js +1 -6
- package/dist/esm/hooks/useEditor.js +2 -10
- package/dist/esm/hooks/useIsEditorStatic.js +4 -1
- package/dist/esm/hooks/useMarkViewDescription.js +1 -4
- package/dist/esm/hooks/useNodeViewDescription.js +2 -23
- package/dist/esm/plugins/beforeInputPlugin.js +162 -50
- package/dist/esm/plugins/reactKeys.js +34 -15
- package/dist/esm/tiptap/ReactProseMirrorNodeView.js +1 -1
- package/dist/esm/tiptap/TiptapEditorContent.js +8 -1
- package/dist/esm/tiptap/hooks/useIsInReactProseMirror.js +5 -1
- package/dist/esm/tiptap/tiptapNodeView.js +13 -14
- package/dist/esm/viewdesc.js +54 -4
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/ReactEditorView.d.ts +10 -1
- package/dist/types/components/ChildNodeViews.d.ts +2 -2
- package/dist/types/components/CursorWrapper.d.ts +1 -1
- package/dist/types/components/TextNodeView.d.ts +7 -4
- package/dist/types/components/__tests__/ProseMirror.composition.test.d.ts +17 -1
- package/dist/types/components/marks/DefaultMarkView.d.ts +1 -1
- package/dist/types/components/marks/MarkView.d.ts +1 -1
- package/dist/types/components/marks/MarkViewConstructorView.d.ts +1 -1
- package/dist/types/components/marks/ReactMarkView.d.ts +1 -1
- package/dist/types/components/nodes/DefaultNodeView.d.ts +1 -1
- package/dist/types/components/nodes/NodeView.d.ts +3 -1
- package/dist/types/components/nodes/NodeViewConstructorView.d.ts +1 -1
- package/dist/types/components/nodes/ReactNodeView.d.ts +1 -1
- package/dist/types/contexts/ChildDescriptionsContext.d.ts +3 -2
- package/dist/types/contexts/CompositionContext.d.ts +4 -0
- package/dist/types/decorations/viewDecorations.d.ts +2 -2
- package/dist/types/hooks/useEditor.d.ts +3 -4
- package/dist/types/hooks/useIsEditorStatic.d.ts +4 -0
- package/dist/types/hooks/useReactKeys.d.ts +2 -5
- package/dist/types/plugins/beforeInputPlugin.d.ts +1 -2
- package/dist/types/plugins/reactKeys.d.ts +10 -9
- package/dist/types/props.d.ts +225 -225
- package/dist/types/tiptap/ReactProseMirrorNodeView.d.ts +1 -1
- package/dist/types/tiptap/TiptapEditorContent.d.ts +10 -1
- package/dist/types/tiptap/hooks/useIsInReactProseMirror.d.ts +5 -0
- package/dist/types/tiptap/tiptapNodeView.d.ts +5 -6
- package/dist/types/viewdesc.d.ts +5 -3
- package/package.json +22 -6
- package/dist/cjs/plugins/componentEventListeners.js +0 -28
- package/dist/cjs/plugins/componentEventListenersPlugin.js +0 -35
- package/dist/cjs/tiptap/utils/ssrJSDOMPatch.js +0 -59
- package/dist/esm/plugins/componentEventListeners.js +0 -18
- package/dist/esm/plugins/componentEventListenersPlugin.js +0 -25
- package/dist/esm/tiptap/utils/ssrJSDOMPatch.js +0 -56
- package/dist/types/plugins/componentEventListeners.d.ts +0 -3
- package/dist/types/plugins/componentEventListenersPlugin.d.ts +0 -4
- package/dist/types/tiptap/utils/ssrJSDOMPatch.d.ts +0 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Plugin, PluginKey } from "prosemirror-state";
|
|
2
|
+
import { DecorationSet } from "prosemirror-view";
|
|
3
|
+
import { widget } from "../decorations/ReactWidgetType.js";
|
|
2
4
|
export function createNodeKey() {
|
|
3
5
|
const key = Math.floor(Math.random() * 0xffffffffffff).toString(16);
|
|
4
6
|
return key;
|
|
@@ -11,14 +13,15 @@ export const reactKeysPluginKey = new PluginKey("@handlewithcare/react-prosemirr
|
|
|
11
13
|
* key for a given node can be accessed by that node's
|
|
12
14
|
* current position in the document, and vice versa.
|
|
13
15
|
*/ export function reactKeys() {
|
|
14
|
-
let composing = false;
|
|
15
16
|
return new Plugin({
|
|
16
17
|
key: reactKeysPluginKey,
|
|
17
18
|
state: {
|
|
18
19
|
init (_, state) {
|
|
19
20
|
const next = {
|
|
20
21
|
posToKey: new Map(),
|
|
21
|
-
keyToPos: new Map()
|
|
22
|
+
keyToPos: new Map(),
|
|
23
|
+
cursorWrapper: null,
|
|
24
|
+
freezeFrom: null
|
|
22
25
|
};
|
|
23
26
|
state.doc.descendants((_, pos)=>{
|
|
24
27
|
const key = createNodeKey();
|
|
@@ -35,15 +38,32 @@ export const reactKeysPluginKey = new PluginKey("@handlewithcare/react-prosemirr
|
|
|
35
38
|
* through the transaction to identify its current position,
|
|
36
39
|
* and assign its key to that new position, dropping it if the
|
|
37
40
|
* node was deleted.
|
|
38
|
-
*/ apply (tr, value,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
41
|
+
*/ apply (tr, value, oldState, newState) {
|
|
42
|
+
const meta = tr.getMeta(reactKeysPluginKey);
|
|
43
|
+
const overrides = meta && "overrides" in meta ? meta.overrides : {};
|
|
44
|
+
const cursorWrapper = meta && "cursorWrapper" in meta ? meta.cursorWrapper : undefined;
|
|
45
|
+
const freezeFrom = meta && "freezeFrom" in meta ? meta.freezeFrom : undefined;
|
|
43
46
|
const next = {
|
|
44
47
|
posToKey: new Map(),
|
|
45
|
-
keyToPos: new Map()
|
|
48
|
+
keyToPos: new Map(),
|
|
49
|
+
cursorWrapper: cursorWrapper === undefined ? value.cursorWrapper ? widget(tr.mapping.map(value.cursorWrapper.from, -1), value.cursorWrapper.type.Component, value.cursorWrapper.spec) : null : cursorWrapper,
|
|
50
|
+
freezeFrom: freezeFrom === undefined ? value.freezeFrom !== null ? tr.mapping.map(value.freezeFrom, -1) : null : freezeFrom
|
|
46
51
|
};
|
|
52
|
+
if (value.freezeFrom !== null && next.freezeFrom !== null && tr.getMeta("composition") == null) {
|
|
53
|
+
const oldBlock = oldState.doc.nodeAt(value.freezeFrom);
|
|
54
|
+
const newBlock = newState.doc.nodeAt(next.freezeFrom);
|
|
55
|
+
if (newBlock && !oldBlock?.eq(newBlock)) {
|
|
56
|
+
next.freezeFrom = null;
|
|
57
|
+
next.cursorWrapper = null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (!tr.docChanged) {
|
|
61
|
+
return {
|
|
62
|
+
...value,
|
|
63
|
+
cursorWrapper: next.cursorWrapper,
|
|
64
|
+
freezeFrom: next.freezeFrom
|
|
65
|
+
};
|
|
66
|
+
}
|
|
47
67
|
const posToKeyEntries = Array.from(value.posToKey.entries()).sort((param, param1)=>{
|
|
48
68
|
let [a] = param, [b] = param1;
|
|
49
69
|
return a - b;
|
|
@@ -69,13 +89,12 @@ export const reactKeysPluginKey = new PluginKey("@handlewithcare/react-prosemirr
|
|
|
69
89
|
}
|
|
70
90
|
},
|
|
71
91
|
props: {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
92
|
+
decorations (state) {
|
|
93
|
+
const deco = reactKeysPluginKey.getState(state)?.cursorWrapper;
|
|
94
|
+
if (!deco) return DecorationSet.empty;
|
|
95
|
+
return DecorationSet.create(state.doc, [
|
|
96
|
+
deco
|
|
97
|
+
]);
|
|
79
98
|
}
|
|
80
99
|
}
|
|
81
100
|
});
|
|
@@ -2,7 +2,7 @@ import { NodeView } from "@tiptap/core";
|
|
|
2
2
|
/**
|
|
3
3
|
* Subclass of Tiptap's NodeView to be used in tiptapNodeView.
|
|
4
4
|
*
|
|
5
|
-
* Allows us to pass in an existing dom and
|
|
5
|
+
* Allows us to pass in an existing dom and contentDOM from React ProseMirror's
|
|
6
6
|
* ViewDesc, so that we can call Tiptap's default stopEvent and ignoreMutation
|
|
7
7
|
* methods
|
|
8
8
|
*/ export class ReactProseMirrorNodeView extends NodeView {
|
|
@@ -49,7 +49,14 @@ function getInstance() {
|
|
|
49
49
|
}
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Renders the actual editable ProseMirror document.
|
|
54
|
+
*
|
|
55
|
+
* This **must** be passed as a child to the `TiptapEditorView`
|
|
56
|
+
* component. It may be wrapped in other components, and other
|
|
57
|
+
* childern may be passed before or after. It must be passed the
|
|
58
|
+
* same `editor` as is passed to the `TiptapEditorView`.
|
|
59
|
+
*/ export function TiptapEditorContent(param) {
|
|
53
60
|
let { editor: editorProp, ...props } = param;
|
|
54
61
|
const editor = editorProp;
|
|
55
62
|
const { onEditorInitialize, onEditorDeinitialize } = useContext(TiptapEditorContext);
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { useContext } from "react";
|
|
2
2
|
import { EditorContext } from "../../contexts/EditorContext.js";
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Returns true if the hook is called in a
|
|
5
|
+
* component that's a descendant of the
|
|
6
|
+
* ProseMirror component
|
|
7
|
+
*/ export function useIsInReactProseMirror() {
|
|
4
8
|
return useContext(EditorContext) !== null;
|
|
5
9
|
}
|
|
@@ -23,11 +23,9 @@ import { useTiptapEditorEventCallback } from "./hooks/useTiptapEditorEventCallba
|
|
|
23
23
|
* codeBlock: nodeView({
|
|
24
24
|
* component: function CodeBlock(nodeViewProps) {
|
|
25
25
|
* return (
|
|
26
|
-
* <
|
|
27
|
-
* <
|
|
28
|
-
*
|
|
29
|
-
* </pre>
|
|
30
|
-
* </AnnotatableNodeViewWrapper>
|
|
26
|
+
* <pre>
|
|
27
|
+
* <NodeViewContent as="code" />
|
|
28
|
+
* </pre>
|
|
31
29
|
* )
|
|
32
30
|
* },
|
|
33
31
|
* extension: CodeBlockExtension,
|
|
@@ -84,15 +82,6 @@ import { useTiptapEditorEventCallback } from "./hooks/useTiptapEditorEventCallba
|
|
|
84
82
|
return result;
|
|
85
83
|
});
|
|
86
84
|
useIgnoreMutation(function(_, mutation) {
|
|
87
|
-
if (ignoreMutation) {
|
|
88
|
-
return ignoreMutation.call({
|
|
89
|
-
name: extension.name,
|
|
90
|
-
editor,
|
|
91
|
-
type: node.type
|
|
92
|
-
}, {
|
|
93
|
-
mutation
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
85
|
if (!editor || !(this.dom instanceof HTMLElement)) return false;
|
|
97
86
|
const nodeView = new ReactProseMirrorNodeView(WrappedComponent, {
|
|
98
87
|
extension,
|
|
@@ -104,6 +93,16 @@ import { useTiptapEditorEventCallback } from "./hooks/useTiptapEditorEventCallba
|
|
|
104
93
|
node,
|
|
105
94
|
view: editor.view
|
|
106
95
|
}, this.dom, this.contentDOM);
|
|
96
|
+
if (ignoreMutation) {
|
|
97
|
+
return ignoreMutation.call({
|
|
98
|
+
name: extension.name,
|
|
99
|
+
editor,
|
|
100
|
+
type: node.type
|
|
101
|
+
}, {
|
|
102
|
+
mutation,
|
|
103
|
+
defaultIgnoreMutation: nodeView.ignoreMutation.bind(nodeView)
|
|
104
|
+
});
|
|
105
|
+
}
|
|
107
106
|
return nodeView.ignoreMutation(mutation) ?? false;
|
|
108
107
|
});
|
|
109
108
|
const { extraClassName, htmlProps } = useMemo(()=>{
|
package/dist/esm/viewdesc.js
CHANGED
|
@@ -16,7 +16,17 @@ import { domIndex, isEquivalentPosition } from "./dom.js";
|
|
|
16
16
|
export function sortViewDescs(a, b) {
|
|
17
17
|
if (a instanceof TrailingHackViewDesc) return 1;
|
|
18
18
|
if (b instanceof TrailingHackViewDesc) return -1;
|
|
19
|
-
|
|
19
|
+
const posDiff = a.getPos() - b.getPos();
|
|
20
|
+
if (posDiff !== 0) return posDiff;
|
|
21
|
+
// When two descs share the same PM position (e.g. a zero-width widget
|
|
22
|
+
// and a text node that starts at the same position), fall back to DOM
|
|
23
|
+
// order so that the viewdesc children match the actual DOM layout.
|
|
24
|
+
// Without this, position computations like `posBeforeChild` can return
|
|
25
|
+
// the wrong PM position for the widget's container.
|
|
26
|
+
const cmp = a.dom.compareDocumentPosition(b.dom);
|
|
27
|
+
if (cmp & 4 /* DOCUMENT_POSITION_FOLLOWING */ ) return -1;
|
|
28
|
+
if (cmp & 2 /* DOCUMENT_POSITION_PRECEDING */ ) return 1;
|
|
29
|
+
return 0;
|
|
20
30
|
}
|
|
21
31
|
const NOT_DIRTY = 0, CHILD_DIRTY = 1, CONTENT_DIRTY = 2, NODE_DIRTY = 3;
|
|
22
32
|
// Superclass for the various kinds of descriptions. Defines their
|
|
@@ -235,7 +245,9 @@ export class ViewDesc {
|
|
|
235
245
|
prev = i ? this.children[i - 1] : null;
|
|
236
246
|
if (!prev || prev.dom.parentNode == this.contentDOM) break;
|
|
237
247
|
}
|
|
238
|
-
if (prev && side && enter && !prev.border && !prev.domAtom)
|
|
248
|
+
if (prev && side && enter && !prev.border && !prev.domAtom) {
|
|
249
|
+
return prev.domFromPos(prev.size, side);
|
|
250
|
+
}
|
|
239
251
|
return {
|
|
240
252
|
node: this.contentDOM,
|
|
241
253
|
offset: prev ? domIndex(prev.dom) + 1 : 0
|
|
@@ -370,7 +382,9 @@ export class ViewDesc {
|
|
|
370
382
|
const after = selRange.focusNode.childNodes[selRange.focusOffset];
|
|
371
383
|
if (after && after.contentEditable == "false") force = true;
|
|
372
384
|
}
|
|
373
|
-
if (!(force || brKludge && browser.safari) && isEquivalentPosition(anchorDOM.node, anchorDOM.offset, selRange.anchorNode, selRange.anchorOffset) && isEquivalentPosition(headDOM.node, headDOM.offset, selRange.focusNode, selRange.focusOffset))
|
|
385
|
+
if (view.cursorWrapped || !(force || brKludge && browser.safari) && isEquivalentPosition(anchorDOM.node, anchorDOM.offset, selRange.anchorNode, selRange.anchorOffset) && isEquivalentPosition(headDOM.node, headDOM.offset, selRange.focusNode, selRange.focusOffset)) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
374
388
|
// Selection.extend can be used to create an 'inverted' selection
|
|
375
389
|
// (one where the focus is before the anchor), but not all
|
|
376
390
|
// browsers support it yet.
|
|
@@ -645,7 +659,10 @@ export class TextViewDesc extends NodeViewDesc {
|
|
|
645
659
|
skip: skip || true
|
|
646
660
|
};
|
|
647
661
|
}
|
|
648
|
-
update(
|
|
662
|
+
update(node, outerDeco, _innerDeco, _view) {
|
|
663
|
+
if (this.dirty == NODE_DIRTY || this.dirty != NOT_DIRTY && !this.inParent() || !node.sameMarkup(this.node)) return false;
|
|
664
|
+
this.updateOuterDeco(outerDeco);
|
|
665
|
+
this.node = node;
|
|
649
666
|
this.dirty = NOT_DIRTY;
|
|
650
667
|
return true;
|
|
651
668
|
}
|
|
@@ -674,6 +691,9 @@ export class TextViewDesc extends NodeViewDesc {
|
|
|
674
691
|
isText(text) {
|
|
675
692
|
return this.node.text == text;
|
|
676
693
|
}
|
|
694
|
+
ignoreMutation(mutation) {
|
|
695
|
+
return mutation.type !== "characterData" && mutation.type !== "selection";
|
|
696
|
+
}
|
|
677
697
|
}
|
|
678
698
|
// A dummy desc used to tag trailing BR or IMG nodes created to work
|
|
679
699
|
// around contentEditable terribleness.
|
|
@@ -763,3 +783,33 @@ export function sameOuterDeco(a, b) {
|
|
|
763
783
|
for(let i = 0; i < a.length; i++)if (!a[i].type.eq(b[i].type)) return false;
|
|
764
784
|
return true;
|
|
765
785
|
}
|
|
786
|
+
// Find a piece of text in an inline fragment, overlapping from-to.
|
|
787
|
+
// Ported from prosemirror-view's findTextInFragment.
|
|
788
|
+
export function findTextInFragment(frag, text, from, to) {
|
|
789
|
+
for(let i = 0, pos = 0; i < frag.childCount && pos <= to;){
|
|
790
|
+
const child = frag.child(i++);
|
|
791
|
+
const childStart = pos;
|
|
792
|
+
pos += child.nodeSize;
|
|
793
|
+
if (!child.isText) continue;
|
|
794
|
+
let str = child.text;
|
|
795
|
+
while(i < frag.childCount){
|
|
796
|
+
const next = frag.child(i++);
|
|
797
|
+
pos += next.nodeSize;
|
|
798
|
+
if (!next.isText) break;
|
|
799
|
+
str += next.text;
|
|
800
|
+
}
|
|
801
|
+
if (pos >= from) {
|
|
802
|
+
if (pos >= to && str.slice(to - text.length - childStart, to - childStart) === text) {
|
|
803
|
+
return to - text.length;
|
|
804
|
+
}
|
|
805
|
+
const found = childStart < to ? str.lastIndexOf(text, to - childStart - 1) : -1;
|
|
806
|
+
if (found >= 0 && found + text.length + childStart >= from) {
|
|
807
|
+
return childStart + found;
|
|
808
|
+
}
|
|
809
|
+
if (from === to && str.length >= to + text.length - childStart && str.slice(to - childStart, to - childStart + text.length) === text) {
|
|
810
|
+
return to;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
return -1;
|
|
815
|
+
}
|