@handlewithcare/react-prosemirror 3.1.0-tiptap.43 → 3.1.0-tiptap.45
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/dist/cjs/components/ChildNodeViews.js +3 -1
- package/dist/cjs/components/CursorWrapper.js +6 -11
- package/dist/cjs/components/ProseMirror.js +2 -2
- package/dist/cjs/hooks/useEditor.js +1 -2
- package/dist/cjs/plugins/beforeInputPlugin.js +32 -2
- package/dist/cjs/testing/editorViewTestHelpers.js +1 -1
- package/dist/cjs/tiptap/tiptapNodeView.js +2 -3
- package/dist/esm/components/ChildNodeViews.js +3 -1
- package/dist/esm/components/CursorWrapper.js +7 -12
- package/dist/esm/components/ProseMirror.js +2 -2
- package/dist/esm/hooks/useEditor.js +1 -2
- package/dist/esm/plugins/beforeInputPlugin.js +33 -3
- package/dist/esm/testing/editorViewTestHelpers.js +1 -1
- package/dist/esm/tiptap/tiptapNodeView.js +2 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/contexts/EditorContext.d.ts +1 -2
- package/dist/types/hooks/useEditor.d.ts +1 -1
- package/dist/types/testing/editorViewTestHelpers.d.ts +1 -0
- package/package.json +1 -1
|
@@ -244,7 +244,9 @@ function adjustWidgetMarksBack(widgetChildren, nodeChild) {
|
|
|
244
244
|
const child = widgetChildren[i];
|
|
245
245
|
if (// Using internal Decoration property, "type"
|
|
246
246
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
247
|
-
child.widget.type.side < 0
|
|
247
|
+
child.widget.type.side < 0 || // Using internal Decoration property, "type"
|
|
248
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
249
|
+
child.widget.type.spec.marks) {
|
|
248
250
|
continue;
|
|
249
251
|
}
|
|
250
252
|
child.marks = child.marks.reduce((acc, mark)=>mark.addToSet(acc), marksToSpread);
|
|
@@ -54,7 +54,6 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
54
54
|
}
|
|
55
55
|
const CursorWrapper = /*#__PURE__*/ (0, _react.forwardRef)(function CursorWrapper(param, ref) {
|
|
56
56
|
let { widget, getPos, ...props } = param;
|
|
57
|
-
const [shouldRender, setShouldRender] = (0, _react.useState)(true);
|
|
58
57
|
const innerRef = (0, _react.useRef)(null);
|
|
59
58
|
(0, _react.useImperativeHandle)(ref, ()=>{
|
|
60
59
|
return innerRef.current;
|
|
@@ -65,27 +64,23 @@ const CursorWrapper = /*#__PURE__*/ (0, _react.forwardRef)(function CursorWrappe
|
|
|
65
64
|
view.domObserver.disconnectSelection();
|
|
66
65
|
// @ts-expect-error Internal property - domSelection
|
|
67
66
|
const domSel = view.domSelection();
|
|
68
|
-
const range = document.createRange();
|
|
69
67
|
const node = innerRef.current;
|
|
70
68
|
const img = node.nodeName == "IMG";
|
|
71
|
-
if (img
|
|
72
|
-
|
|
69
|
+
if (img) {
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
71
|
+
domSel.collapse(node.parentNode, (0, _dom.domIndex)(node) + 1);
|
|
73
72
|
} else {
|
|
74
|
-
|
|
73
|
+
domSel.collapse(node, 0);
|
|
75
74
|
}
|
|
76
|
-
range.collapse(false);
|
|
77
|
-
domSel.removeAllRanges();
|
|
78
|
-
domSel.addRange(range);
|
|
79
|
-
setShouldRender(false);
|
|
80
75
|
// @ts-expect-error Internal property - domObserver
|
|
81
76
|
view.domObserver.connectSelection();
|
|
82
77
|
}, []);
|
|
83
|
-
return
|
|
78
|
+
return /*#__PURE__*/ _react.default.createElement("img", {
|
|
84
79
|
ref: innerRef,
|
|
85
80
|
className: "ProseMirror-separator",
|
|
86
81
|
// eslint-disable-next-line react/no-unknown-property
|
|
87
82
|
"mark-placeholder": "true",
|
|
88
83
|
alt: "",
|
|
89
84
|
...props
|
|
90
|
-
})
|
|
85
|
+
});
|
|
91
86
|
});
|
|
@@ -73,7 +73,7 @@ const rootChildDescriptionsContextValue = {
|
|
|
73
73
|
function ProseMirrorInner(param) {
|
|
74
74
|
let { children, nodeViewComponents, markViewComponents, ...props } = param;
|
|
75
75
|
const [mount, setMount] = (0, _react.useState)(null);
|
|
76
|
-
const { editor, state } = (0, _useEditor.useEditor)(mount, props);
|
|
76
|
+
const { editor, cursorWrapper, state } = (0, _useEditor.useEditor)(mount, props);
|
|
77
77
|
const nodeViewConstructors = editor.view.nodeViews;
|
|
78
78
|
const nodeViewContextValue = (0, _react.useMemo)(()=>{
|
|
79
79
|
return {
|
|
@@ -90,7 +90,7 @@ function ProseMirrorInner(param) {
|
|
|
90
90
|
]);
|
|
91
91
|
const node = state.doc;
|
|
92
92
|
const decorations = (0, _computeDocDeco.computeDocDeco)(editor.view);
|
|
93
|
-
const innerDecorations = (0, _viewDecorations.viewDecorations)(editor.view,
|
|
93
|
+
const innerDecorations = (0, _viewDecorations.viewDecorations)(editor.view, cursorWrapper);
|
|
94
94
|
const docNodeViewContextValue = (0, _react.useMemo)(()=>({
|
|
95
95
|
setMount,
|
|
96
96
|
node,
|
|
@@ -113,13 +113,11 @@ function useEditor(mount, options) {
|
|
|
113
113
|
view.update(directEditorProps);
|
|
114
114
|
const editor = (0, _react.useMemo)(()=>({
|
|
115
115
|
view,
|
|
116
|
-
cursorWrapper,
|
|
117
116
|
flushSyncRef,
|
|
118
117
|
registerEventListener,
|
|
119
118
|
unregisterEventListener,
|
|
120
119
|
isStatic: options.static ?? false
|
|
121
120
|
}), [
|
|
122
|
-
cursorWrapper,
|
|
123
121
|
options.static,
|
|
124
122
|
registerEventListener,
|
|
125
123
|
unregisterEventListener,
|
|
@@ -127,6 +125,7 @@ function useEditor(mount, options) {
|
|
|
127
125
|
]);
|
|
128
126
|
return {
|
|
129
127
|
editor,
|
|
128
|
+
cursorWrapper,
|
|
130
129
|
state
|
|
131
130
|
};
|
|
132
131
|
}
|
|
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "beforeInputPlugin", {
|
|
|
8
8
|
return beforeInputPlugin;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
+
const _prosemirrormodel = require("prosemirror-model");
|
|
11
12
|
const _prosemirrorstate = require("prosemirror-state");
|
|
12
13
|
const _CursorWrapper = require("../components/CursorWrapper.js");
|
|
13
14
|
const _ReactWidgetType = require("../decorations/ReactWidgetType.js");
|
|
@@ -25,6 +26,34 @@ function insertText(view, eventData) {
|
|
|
25
26
|
view.dispatch(tr);
|
|
26
27
|
return true;
|
|
27
28
|
}
|
|
29
|
+
// Taken from https://github.com/ProseMirror/prosemirror-gapcursor/blob/master/src/index.ts#L67-L84
|
|
30
|
+
// This is a hack that, when a composition starts while a gap cursor
|
|
31
|
+
// is active, quickly creates an inline context for the composition to
|
|
32
|
+
// happen in, to avoid it being aborted by the DOM selection being
|
|
33
|
+
// moved into a valid position.
|
|
34
|
+
//
|
|
35
|
+
// We can't rely on the actual hack from prosemirror-gapcursor, because
|
|
36
|
+
// it happens too late. We snapshot the DOM during compositionstart, but
|
|
37
|
+
// the gapcursor hack runs in beforeinput (after compositionstart).
|
|
38
|
+
function handleGapCursorComposition(view) {
|
|
39
|
+
// @ts-expect-error Internal property - jsonID
|
|
40
|
+
if (!(view.state.selection.jsonID === "gapcursor")) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const { $from } = view.state.selection;
|
|
44
|
+
const insert = $from.parent.contentMatchAt($from.index())// All schemas _must_ have a text node type
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
46
|
+
.findWrapping(view.state.schema.nodes.text);
|
|
47
|
+
if (!insert) return;
|
|
48
|
+
let fragment = _prosemirrormodel.Fragment.empty;
|
|
49
|
+
for(let i = insert.length - 1; i >= 0; i--){
|
|
50
|
+
fragment = _prosemirrormodel.Fragment.from(// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
51
|
+
insert[i].createAndFill(null, fragment));
|
|
52
|
+
}
|
|
53
|
+
const tr = view.state.tr.replace($from.pos, $from.pos, new _prosemirrormodel.Slice(fragment, 0, 0));
|
|
54
|
+
tr.setSelection(_prosemirrorstate.TextSelection.near(tr.doc.resolve($from.pos + 1)));
|
|
55
|
+
view.dispatch(tr);
|
|
56
|
+
}
|
|
28
57
|
function beforeInputPlugin(setCursorWrapper) {
|
|
29
58
|
let compositionMarks = null;
|
|
30
59
|
let precompositionSnapshot = null;
|
|
@@ -32,10 +61,11 @@ function beforeInputPlugin(setCursorWrapper) {
|
|
|
32
61
|
props: {
|
|
33
62
|
handleDOMEvents: {
|
|
34
63
|
compositionstart (view) {
|
|
64
|
+
compositionMarks = view.state.storedMarks ?? view.state.selection.$from.marks();
|
|
65
|
+
view.dispatch(view.state.tr.deleteSelection());
|
|
66
|
+
handleGapCursorComposition(view);
|
|
35
67
|
const { state } = view;
|
|
36
|
-
view.dispatch(state.tr.deleteSelection());
|
|
37
68
|
const $pos = state.selection.$from;
|
|
38
|
-
compositionMarks = state.storedMarks ?? $pos.marks();
|
|
39
69
|
if (compositionMarks) {
|
|
40
70
|
setCursorWrapper((0, _ReactWidgetType.widget)(state.selection.from, _CursorWrapper.CursorWrapper, {
|
|
41
71
|
key: "cursor-wrapper",
|
|
@@ -51,7 +51,7 @@ function tempEditor(param) {
|
|
|
51
51
|
const state = _prosemirrorstate.EditorState.create({
|
|
52
52
|
doc: startDoc,
|
|
53
53
|
schema: _prosemirrortestbuilder.schema,
|
|
54
|
-
selection: selection ?? startDoc.tag?.a ? _prosemirrorstate.TextSelection.create(startDoc, startDoc.tag.a, startDoc.tag?.b) : undefined,
|
|
54
|
+
selection: selection ?? (startDoc.tag?.a ? _prosemirrorstate.TextSelection.create(startDoc, startDoc.tag.a, startDoc.tag?.b) : undefined),
|
|
55
55
|
plugins: [
|
|
56
56
|
...plugins ?? [],
|
|
57
57
|
(0, _reactKeys.reactKeys)()
|
|
@@ -163,9 +163,8 @@ function tiptapNodeView(param) {
|
|
|
163
163
|
editor.commands.command((param)=>{
|
|
164
164
|
let { tr } = param;
|
|
165
165
|
const pos = getPos();
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
...attributes
|
|
166
|
+
Object.keys(attributes).forEach((key)=>{
|
|
167
|
+
tr.setNodeAttribute(pos, key, attributes[key]);
|
|
169
168
|
});
|
|
170
169
|
return true;
|
|
171
170
|
});
|
|
@@ -185,7 +185,9 @@ function adjustWidgetMarksBack(widgetChildren, nodeChild) {
|
|
|
185
185
|
const child = widgetChildren[i];
|
|
186
186
|
if (// Using internal Decoration property, "type"
|
|
187
187
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
188
|
-
child.widget.type.side < 0
|
|
188
|
+
child.widget.type.side < 0 || // Using internal Decoration property, "type"
|
|
189
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
190
|
+
child.widget.type.spec.marks) {
|
|
189
191
|
continue;
|
|
190
192
|
}
|
|
191
193
|
child.marks = child.marks.reduce((acc, mark)=>mark.addToSet(acc), marksToSpread);
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import React, { forwardRef, useImperativeHandle, useRef
|
|
1
|
+
import React, { forwardRef, useImperativeHandle, useRef } from "react";
|
|
2
2
|
import { domIndex } from "../dom.js";
|
|
3
3
|
import { useEditorEffect } from "../hooks/useEditorEffect.js";
|
|
4
4
|
export const CursorWrapper = /*#__PURE__*/ forwardRef(function CursorWrapper(param, ref) {
|
|
5
5
|
let { widget, getPos, ...props } = param;
|
|
6
|
-
const [shouldRender, setShouldRender] = useState(true);
|
|
7
6
|
const innerRef = useRef(null);
|
|
8
7
|
useImperativeHandle(ref, ()=>{
|
|
9
8
|
return innerRef.current;
|
|
@@ -14,27 +13,23 @@ export const CursorWrapper = /*#__PURE__*/ forwardRef(function CursorWrapper(par
|
|
|
14
13
|
view.domObserver.disconnectSelection();
|
|
15
14
|
// @ts-expect-error Internal property - domSelection
|
|
16
15
|
const domSel = view.domSelection();
|
|
17
|
-
const range = document.createRange();
|
|
18
16
|
const node = innerRef.current;
|
|
19
17
|
const img = node.nodeName == "IMG";
|
|
20
|
-
if (img
|
|
21
|
-
|
|
18
|
+
if (img) {
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
20
|
+
domSel.collapse(node.parentNode, domIndex(node) + 1);
|
|
22
21
|
} else {
|
|
23
|
-
|
|
22
|
+
domSel.collapse(node, 0);
|
|
24
23
|
}
|
|
25
|
-
range.collapse(false);
|
|
26
|
-
domSel.removeAllRanges();
|
|
27
|
-
domSel.addRange(range);
|
|
28
|
-
setShouldRender(false);
|
|
29
24
|
// @ts-expect-error Internal property - domObserver
|
|
30
25
|
view.domObserver.connectSelection();
|
|
31
26
|
}, []);
|
|
32
|
-
return
|
|
27
|
+
return /*#__PURE__*/ React.createElement("img", {
|
|
33
28
|
ref: innerRef,
|
|
34
29
|
className: "ProseMirror-separator",
|
|
35
30
|
// eslint-disable-next-line react/no-unknown-property
|
|
36
31
|
"mark-placeholder": "true",
|
|
37
32
|
alt: "",
|
|
38
33
|
...props
|
|
39
|
-
})
|
|
34
|
+
});
|
|
40
35
|
});
|
|
@@ -22,7 +22,7 @@ const rootChildDescriptionsContextValue = {
|
|
|
22
22
|
function ProseMirrorInner(param) {
|
|
23
23
|
let { children, nodeViewComponents, markViewComponents, ...props } = param;
|
|
24
24
|
const [mount, setMount] = useState(null);
|
|
25
|
-
const { editor, state } = useEditor(mount, props);
|
|
25
|
+
const { editor, cursorWrapper, state } = useEditor(mount, props);
|
|
26
26
|
const nodeViewConstructors = editor.view.nodeViews;
|
|
27
27
|
const nodeViewContextValue = useMemo(()=>{
|
|
28
28
|
return {
|
|
@@ -39,7 +39,7 @@ function ProseMirrorInner(param) {
|
|
|
39
39
|
]);
|
|
40
40
|
const node = state.doc;
|
|
41
41
|
const decorations = computeDocDeco(editor.view);
|
|
42
|
-
const innerDecorations = viewDecorations(editor.view,
|
|
42
|
+
const innerDecorations = viewDecorations(editor.view, cursorWrapper);
|
|
43
43
|
const docNodeViewContextValue = useMemo(()=>({
|
|
44
44
|
setMount,
|
|
45
45
|
node,
|
|
@@ -111,13 +111,11 @@ let didWarnValueDefaultValue = false;
|
|
|
111
111
|
view.update(directEditorProps);
|
|
112
112
|
const editor = useMemo(()=>({
|
|
113
113
|
view,
|
|
114
|
-
cursorWrapper,
|
|
115
114
|
flushSyncRef,
|
|
116
115
|
registerEventListener,
|
|
117
116
|
unregisterEventListener,
|
|
118
117
|
isStatic: options.static ?? false
|
|
119
118
|
}), [
|
|
120
|
-
cursorWrapper,
|
|
121
119
|
options.static,
|
|
122
120
|
registerEventListener,
|
|
123
121
|
unregisterEventListener,
|
|
@@ -125,6 +123,7 @@ let didWarnValueDefaultValue = false;
|
|
|
125
123
|
]);
|
|
126
124
|
return {
|
|
127
125
|
editor,
|
|
126
|
+
cursorWrapper,
|
|
128
127
|
state
|
|
129
128
|
};
|
|
130
129
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Fragment, Slice } from "prosemirror-model";
|
|
2
|
+
import { Plugin, TextSelection } from "prosemirror-state";
|
|
2
3
|
import { CursorWrapper } from "../components/CursorWrapper.js";
|
|
3
4
|
import { widget } from "../decorations/ReactWidgetType.js";
|
|
4
5
|
function insertText(view, eventData) {
|
|
@@ -15,6 +16,34 @@ function insertText(view, eventData) {
|
|
|
15
16
|
view.dispatch(tr);
|
|
16
17
|
return true;
|
|
17
18
|
}
|
|
19
|
+
// Taken from https://github.com/ProseMirror/prosemirror-gapcursor/blob/master/src/index.ts#L67-L84
|
|
20
|
+
// This is a hack that, when a composition starts while a gap cursor
|
|
21
|
+
// is active, quickly creates an inline context for the composition to
|
|
22
|
+
// happen in, to avoid it being aborted by the DOM selection being
|
|
23
|
+
// moved into a valid position.
|
|
24
|
+
//
|
|
25
|
+
// We can't rely on the actual hack from prosemirror-gapcursor, because
|
|
26
|
+
// it happens too late. We snapshot the DOM during compositionstart, but
|
|
27
|
+
// the gapcursor hack runs in beforeinput (after compositionstart).
|
|
28
|
+
function handleGapCursorComposition(view) {
|
|
29
|
+
// @ts-expect-error Internal property - jsonID
|
|
30
|
+
if (!(view.state.selection.jsonID === "gapcursor")) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const { $from } = view.state.selection;
|
|
34
|
+
const insert = $from.parent.contentMatchAt($from.index())// All schemas _must_ have a text node type
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
36
|
+
.findWrapping(view.state.schema.nodes.text);
|
|
37
|
+
if (!insert) return;
|
|
38
|
+
let fragment = Fragment.empty;
|
|
39
|
+
for(let i = insert.length - 1; i >= 0; i--){
|
|
40
|
+
fragment = Fragment.from(// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
41
|
+
insert[i].createAndFill(null, fragment));
|
|
42
|
+
}
|
|
43
|
+
const tr = view.state.tr.replace($from.pos, $from.pos, new Slice(fragment, 0, 0));
|
|
44
|
+
tr.setSelection(TextSelection.near(tr.doc.resolve($from.pos + 1)));
|
|
45
|
+
view.dispatch(tr);
|
|
46
|
+
}
|
|
18
47
|
export function beforeInputPlugin(setCursorWrapper) {
|
|
19
48
|
let compositionMarks = null;
|
|
20
49
|
let precompositionSnapshot = null;
|
|
@@ -22,10 +51,11 @@ export function beforeInputPlugin(setCursorWrapper) {
|
|
|
22
51
|
props: {
|
|
23
52
|
handleDOMEvents: {
|
|
24
53
|
compositionstart (view) {
|
|
54
|
+
compositionMarks = view.state.storedMarks ?? view.state.selection.$from.marks();
|
|
55
|
+
view.dispatch(view.state.tr.deleteSelection());
|
|
56
|
+
handleGapCursorComposition(view);
|
|
25
57
|
const { state } = view;
|
|
26
|
-
view.dispatch(state.tr.deleteSelection());
|
|
27
58
|
const $pos = state.selection.$from;
|
|
28
|
-
compositionMarks = state.storedMarks ?? $pos.marks();
|
|
29
59
|
if (compositionMarks) {
|
|
30
60
|
setCursorWrapper(widget(state.selection.from, CursorWrapper, {
|
|
31
61
|
key: "cursor-wrapper",
|
|
@@ -28,7 +28,7 @@ export function tempEditor(param) {
|
|
|
28
28
|
const state = EditorState.create({
|
|
29
29
|
doc: startDoc,
|
|
30
30
|
schema,
|
|
31
|
-
selection: selection ?? startDoc.tag?.a ? TextSelection.create(startDoc, startDoc.tag.a, startDoc.tag?.b) : undefined,
|
|
31
|
+
selection: selection ?? (startDoc.tag?.a ? TextSelection.create(startDoc, startDoc.tag.a, startDoc.tag?.b) : undefined),
|
|
32
32
|
plugins: [
|
|
33
33
|
...plugins ?? [],
|
|
34
34
|
reactKeys()
|
|
@@ -131,9 +131,8 @@ import { useTiptapEditorEventCallback } from "./hooks/useTiptapEditorEventCallba
|
|
|
131
131
|
editor.commands.command((param)=>{
|
|
132
132
|
let { tr } = param;
|
|
133
133
|
const pos = getPos();
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
...attributes
|
|
134
|
+
Object.keys(attributes).forEach((key)=>{
|
|
135
|
+
tr.setNodeAttribute(pos, key, attributes[key]);
|
|
137
136
|
});
|
|
138
137
|
return true;
|
|
139
138
|
});
|