@handlewithcare/react-prosemirror 3.1.0-tiptap.42 → 3.1.0-tiptap.44
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 +26 -0
- package/dist/cjs/ReactEditorView.js +74 -70
- package/dist/cjs/StaticEditorView.js +21 -18
- package/dist/cjs/browser.js +3 -1
- package/dist/cjs/commands/reorderSiblings.js +13 -9
- package/dist/cjs/components/ChildNodeViews.js +74 -69
- package/dist/cjs/components/CursorWrapper.js +20 -34
- package/dist/cjs/components/LayoutGroup.js +16 -12
- package/dist/cjs/components/NativeWidgetView.js +19 -15
- package/dist/cjs/components/OutputSpec.js +13 -9
- package/dist/cjs/components/ProseMirror.js +27 -37
- package/dist/cjs/components/ProseMirrorDoc.js +18 -25
- package/dist/cjs/components/SeparatorHackView.js +20 -17
- package/dist/cjs/components/TextNodeView.js +20 -22
- package/dist/cjs/components/TrailingHackView.js +17 -13
- package/dist/cjs/components/WidgetView.js +18 -14
- package/dist/cjs/components/marks/DefaultMarkView.js +15 -27
- package/dist/cjs/components/marks/MarkView.js +23 -32
- package/dist/cjs/components/marks/MarkViewConstructorView.js +22 -38
- package/dist/cjs/components/marks/ReactMarkView.js +16 -27
- package/dist/cjs/components/nodes/DefaultNodeView.js +15 -27
- package/dist/cjs/components/nodes/DocNodeView.js +16 -12
- package/dist/cjs/components/nodes/NodeView.js +26 -33
- package/dist/cjs/components/nodes/NodeViewConstructorView.js +29 -45
- package/dist/cjs/components/nodes/ReactNodeView.js +27 -39
- package/dist/cjs/constants.js +10 -6
- package/dist/cjs/contexts/ChildDescriptionsContext.js +3 -1
- package/dist/cjs/contexts/EditorContext.js +3 -1
- package/dist/cjs/contexts/EditorStateContext.js +3 -1
- package/dist/cjs/contexts/IgnoreMutationContext.js +3 -1
- package/dist/cjs/contexts/LayoutGroupContext.js +3 -1
- package/dist/cjs/contexts/NodeViewContext.js +3 -1
- package/dist/cjs/contexts/SelectNodeContext.js +3 -1
- package/dist/cjs/contexts/StopEventContext.js +3 -1
- package/dist/cjs/decorations/ReactWidgetType.js +19 -13
- package/dist/cjs/decorations/computeDocDeco.js +5 -3
- package/dist/cjs/decorations/iterDeco.js +19 -17
- package/dist/cjs/decorations/viewDecorations.js +15 -12
- package/dist/cjs/dom.js +34 -13
- package/dist/cjs/findDOMNode.js +9 -5
- package/dist/cjs/hooks/useClientLayoutEffect.js +3 -1
- package/dist/cjs/hooks/useComponentEventListeners.js +7 -6
- package/dist/cjs/hooks/useEditor.js +29 -32
- package/dist/cjs/hooks/useEditorEffect.js +9 -7
- package/dist/cjs/hooks/useEditorEventCallback.js +9 -7
- package/dist/cjs/hooks/useEditorEventListener.js +8 -6
- package/dist/cjs/hooks/useEditorState.js +5 -3
- package/dist/cjs/hooks/useEffectEvent.js +3 -1
- package/dist/cjs/hooks/useForceUpdate.js +3 -1
- package/dist/cjs/hooks/useIgnoreMutation.js +9 -7
- package/dist/cjs/hooks/useIsEditorStatic.js +5 -5
- package/dist/cjs/hooks/useIsNodeSelected.js +5 -3
- package/dist/cjs/hooks/useLayoutGroupEffect.js +7 -5
- package/dist/cjs/hooks/useMarkViewDescription.js +31 -25
- package/dist/cjs/hooks/useNodePos.js +7 -5
- package/dist/cjs/hooks/useNodeViewDescription.js +32 -28
- package/dist/cjs/hooks/useReactKeys.js +7 -5
- package/dist/cjs/hooks/useSelectNode.js +10 -8
- package/dist/cjs/hooks/useStopEvent.js +9 -7
- package/dist/cjs/index.js +66 -34
- package/dist/cjs/plugins/beforeInputPlugin.js +51 -24
- package/dist/cjs/plugins/componentEventListeners.js +8 -6
- package/dist/cjs/plugins/componentEventListenersPlugin.js +8 -6
- package/dist/cjs/plugins/reactKeys.js +15 -10
- package/dist/cjs/props.js +23 -19
- package/dist/cjs/refs.js +3 -1
- package/dist/cjs/testing/editorViewTestHelpers.js +40 -47
- package/dist/cjs/testing/setupProseMirrorView.js +7 -4
- package/dist/cjs/tiptap/ReactProseMirrorNodeView.js +10 -6
- package/dist/cjs/tiptap/TiptapEditor.js +15 -14
- package/dist/cjs/tiptap/TiptapEditorContent.js +18 -28
- package/dist/cjs/tiptap/TiptapEditorView.js +23 -34
- package/dist/cjs/tiptap/contexts/TiptapEditorContext.js +3 -1
- package/dist/cjs/tiptap/extensions/ReactProseMirror.js +8 -6
- package/dist/cjs/tiptap/extensions/ReactProseMirrorCommands.js +5 -3
- package/dist/cjs/tiptap/extensions/commands/updateAttributes.js +4 -2
- package/dist/cjs/tiptap/hooks/useIsInReactProseMirror.js +5 -3
- package/dist/cjs/tiptap/hooks/useTiptapEditor.js +12 -14
- package/dist/cjs/tiptap/hooks/useTiptapEditorEffect.js +12 -10
- package/dist/cjs/tiptap/hooks/useTiptapEditorEventCallback.js +6 -4
- package/dist/cjs/tiptap/index.js +36 -18
- package/dist/cjs/tiptap/tiptapNodeView.js +48 -62
- package/dist/cjs/viewdesc.js +119 -89
- package/dist/esm/ReactEditorView.js +68 -66
- package/dist/esm/StaticEditorView.js +18 -17
- package/dist/esm/commands/reorderSiblings.js +5 -5
- package/dist/esm/components/ChildNodeViews.js +36 -37
- package/dist/esm/components/CursorWrapper.js +10 -28
- package/dist/esm/components/LayoutGroup.js +1 -1
- package/dist/esm/components/NativeWidgetView.js +2 -2
- package/dist/esm/components/OutputSpec.js +1 -1
- package/dist/esm/components/ProseMirror.js +4 -18
- package/dist/esm/components/ProseMirrorDoc.js +6 -19
- package/dist/esm/components/SeparatorHackView.js +3 -4
- package/dist/esm/components/TextNodeView.js +6 -10
- package/dist/esm/components/TrailingHackView.js +2 -2
- package/dist/esm/components/WidgetView.js +3 -3
- package/dist/esm/components/marks/DefaultMarkView.js +6 -22
- package/dist/esm/components/marks/MarkView.js +11 -24
- package/dist/esm/components/marks/MarkViewConstructorView.js +7 -27
- package/dist/esm/components/marks/ReactMarkView.js +3 -18
- package/dist/esm/components/nodes/DefaultNodeView.js +6 -22
- package/dist/esm/components/nodes/DocNodeView.js +2 -2
- package/dist/esm/components/nodes/NodeView.js +11 -24
- package/dist/esm/components/nodes/NodeViewConstructorView.js +11 -31
- package/dist/esm/components/nodes/ReactNodeView.js +6 -22
- package/dist/esm/decorations/ReactWidgetType.js +10 -8
- package/dist/esm/decorations/iterDeco.js +13 -13
- package/dist/esm/decorations/viewDecorations.js +7 -6
- package/dist/esm/dom.js +1 -2
- package/dist/esm/findDOMNode.js +1 -1
- package/dist/esm/hooks/useComponentEventListeners.js +2 -3
- package/dist/esm/hooks/useEditor.js +6 -11
- package/dist/esm/hooks/useEditorEffect.js +1 -1
- package/dist/esm/hooks/useEditorEventCallback.js +1 -1
- package/dist/esm/hooks/useEditorEventListener.js +1 -1
- package/dist/esm/hooks/useIsEditorStatic.js +1 -3
- package/dist/esm/hooks/useMarkViewDescription.js +14 -10
- package/dist/esm/hooks/useNodeViewDescription.js +10 -8
- package/dist/esm/hooks/useReactKeys.js +1 -1
- package/dist/esm/plugins/beforeInputPlugin.js +44 -19
- package/dist/esm/plugins/reactKeys.js +3 -4
- package/dist/esm/props.js +15 -15
- package/dist/esm/testing/editorViewTestHelpers.js +20 -31
- package/dist/esm/testing/setupProseMirrorView.js +1 -2
- package/dist/esm/tiptap/ReactProseMirrorNodeView.js +7 -5
- package/dist/esm/tiptap/TiptapEditor.js +9 -10
- package/dist/esm/tiptap/TiptapEditorContent.js +4 -18
- package/dist/esm/tiptap/TiptapEditorView.js +8 -23
- package/dist/esm/tiptap/extensions/ReactProseMirror.js +1 -1
- package/dist/esm/tiptap/extensions/commands/updateAttributes.js +1 -1
- package/dist/esm/tiptap/hooks/useTiptapEditor.js +4 -8
- package/dist/esm/tiptap/hooks/useTiptapEditorEffect.js +4 -4
- package/dist/esm/tiptap/hooks/useTiptapEditorEventCallback.js +1 -1
- package/dist/esm/tiptap/tiptapNodeView.js +20 -38
- package/dist/esm/viewdesc.js +74 -66
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/constants.d.ts +1 -1
- package/dist/types/contexts/EditorContext.d.ts +1 -2
- package/dist/types/hooks/useEditor.d.ts +1 -1
- package/dist/types/props.d.ts +8 -8
- package/dist/types/testing/editorViewTestHelpers.d.ts +1 -0
- package/package.json +1 -2
|
@@ -43,41 +43,41 @@ onWidget, onNode) {
|
|
|
43
43
|
if (widget) {
|
|
44
44
|
if (widgets) {
|
|
45
45
|
widgets.sort(compareSide);
|
|
46
|
-
for(let
|
|
47
|
-
!(widgets[
|
|
46
|
+
for(let i = 0; i < widgets.length; i++)onWidget(widgets[i], // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
+
!(widgets[i].type instanceof ReactWidgetType), offset, parentIndex + i, !!restNode);
|
|
48
48
|
} else {
|
|
49
49
|
onWidget(widget, // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
50
50
|
!(widget.type instanceof ReactWidgetType), offset, parentIndex, !!restNode);
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
-
let
|
|
54
|
+
let child, index;
|
|
55
55
|
if (restNode) {
|
|
56
56
|
index = -1;
|
|
57
|
-
|
|
57
|
+
child = restNode;
|
|
58
58
|
restNode = null;
|
|
59
59
|
} else if (parentIndex < parent.childCount) {
|
|
60
60
|
index = parentIndex;
|
|
61
|
-
|
|
61
|
+
child = parent.child(parentIndex++);
|
|
62
62
|
} else {
|
|
63
63
|
break;
|
|
64
64
|
}
|
|
65
|
-
for(let
|
|
65
|
+
for(let i = 0; i < active.length; i++)if (active[i].to <= offset) active.splice(i--, 1);
|
|
66
66
|
while(decoIndex < locals.length && locals[decoIndex].from <= offset && locals[decoIndex].to > offset)active.push(locals[decoIndex++]);
|
|
67
|
-
let end = offset +
|
|
68
|
-
if (
|
|
67
|
+
let end = offset + child.nodeSize;
|
|
68
|
+
if (child.isText) {
|
|
69
69
|
let cutAt = end;
|
|
70
70
|
if (decoIndex < locals.length && locals[decoIndex].from < cutAt) cutAt = locals[decoIndex].from;
|
|
71
|
-
for(let
|
|
71
|
+
for(let i = 0; i < active.length; i++)if (active[i].to < cutAt) cutAt = active[i].to;
|
|
72
72
|
if (cutAt < end) {
|
|
73
|
-
restNode =
|
|
74
|
-
|
|
73
|
+
restNode = child.cut(cutAt - offset);
|
|
74
|
+
child = child.cut(0, cutAt - offset);
|
|
75
75
|
end = cutAt;
|
|
76
76
|
index = -1;
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
-
const outerDeco =
|
|
80
|
-
onNode(
|
|
79
|
+
const outerDeco = child.isInline && !child.isLeaf ? active.filter((d)=>!d.inline) : active.slice();
|
|
80
|
+
onNode(child, outerDeco, deco.forChild(offset, child), offset, index);
|
|
81
81
|
offset = end;
|
|
82
82
|
}
|
|
83
83
|
}
|
|
@@ -6,6 +6,10 @@ const empty = DecorationSet.empty;
|
|
|
6
6
|
// treat multiple DecorationSet objects as if it were a single object
|
|
7
7
|
// with (a subset of) the same interface.
|
|
8
8
|
let DecorationGroup = class DecorationGroup {
|
|
9
|
+
members;
|
|
10
|
+
constructor(members){
|
|
11
|
+
this.members = members;
|
|
12
|
+
}
|
|
9
13
|
map(mapping, doc) {
|
|
10
14
|
const mappedDecos = this.members.map((member)=>member.map(mapping, doc, noSpec));
|
|
11
15
|
return DecorationGroup.from(mappedDecos);
|
|
@@ -61,9 +65,6 @@ let DecorationGroup = class DecorationGroup {
|
|
|
61
65
|
for(let i = 0; i < this.members.length; i++)// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
62
66
|
this.members[i].forEachSet(f);
|
|
63
67
|
}
|
|
64
|
-
constructor(members){
|
|
65
|
-
this.members = members;
|
|
66
|
-
}
|
|
67
68
|
};
|
|
68
69
|
// Used to sort decorations so that ones with a low start position
|
|
69
70
|
// come first, and within a set with the same start position, those
|
|
@@ -151,9 +152,9 @@ const ViewDecorationsCache = new WeakMap();
|
|
|
151
152
|
areSetsEqual = false;
|
|
152
153
|
}
|
|
153
154
|
if (!areSetsEqual) {
|
|
154
|
-
const
|
|
155
|
-
ViewDecorationsCache.set(view,
|
|
156
|
-
return
|
|
155
|
+
const result = DecorationGroup.from(found);
|
|
156
|
+
ViewDecorationsCache.set(view, result);
|
|
157
|
+
return result;
|
|
157
158
|
}
|
|
158
159
|
return previous;
|
|
159
160
|
}
|
package/dist/esm/dom.js
CHANGED
|
@@ -36,8 +36,7 @@ function scanFor(node, off, targetNode, targetOff, dir) {
|
|
|
36
36
|
} else if (node.nodeType == 1) {
|
|
37
37
|
const child = node.childNodes[off + (dir < 0 ? -1 : 0)];
|
|
38
38
|
if (child.nodeType == 1 && child.contentEditable == "false") {
|
|
39
|
-
|
|
40
|
-
if ((ref = child.pmViewDesc) === null || ref === void 0 ? void 0 : ref.ignoreForSelection) off += dir;
|
|
39
|
+
if (child.pmViewDesc?.ignoreForSelection) off += dir;
|
|
41
40
|
else return false;
|
|
42
41
|
} else {
|
|
43
42
|
node = child;
|
package/dist/esm/findDOMNode.js
CHANGED
|
@@ -16,7 +16,7 @@ function findHostInstance(component) {
|
|
|
16
16
|
throw new Error("Unable to find node on an unmounted component.");
|
|
17
17
|
} else {
|
|
18
18
|
const keys = Object.keys(component).join(",");
|
|
19
|
-
throw new Error(
|
|
19
|
+
throw new Error(`Argument appears to not be a ReactComponent. Keys: ${keys}`);
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
const hostFiber = findCurrentHostFiber(fiber);
|
|
@@ -28,8 +28,7 @@ import { componentEventListeners } from "../plugins/componentEventListeners.js";
|
|
|
28
28
|
*/ export function useComponentEventListeners() {
|
|
29
29
|
const [registry, setRegistry] = useState(new Map());
|
|
30
30
|
const registerEventListener = useCallback((eventType, handler)=>{
|
|
31
|
-
|
|
32
|
-
const handlers = (ref = registry.get(eventType)) !== null && ref !== void 0 ? ref : [];
|
|
31
|
+
const handlers = registry.get(eventType) ?? [];
|
|
33
32
|
handlers.unshift(handler);
|
|
34
33
|
if (!registry.has(eventType)) {
|
|
35
34
|
registry.set(eventType, handlers);
|
|
@@ -40,7 +39,7 @@ import { componentEventListeners } from "../plugins/componentEventListeners.js";
|
|
|
40
39
|
]);
|
|
41
40
|
const unregisterEventListener = useCallback((eventType, handler)=>{
|
|
42
41
|
const handlers = registry.get(eventType);
|
|
43
|
-
handlers
|
|
42
|
+
handlers?.splice(handlers.indexOf(handler), 1);
|
|
44
43
|
}, [
|
|
45
44
|
registry
|
|
46
45
|
]);
|
|
@@ -27,20 +27,17 @@ let didWarnValueDefaultValue = false;
|
|
|
27
27
|
const flushSyncRef = useRef(true);
|
|
28
28
|
const [cursorWrapper, _setCursorWrapper] = useState(null);
|
|
29
29
|
const forceUpdate = useForceUpdate();
|
|
30
|
-
|
|
31
|
-
const defaultState = (_defaultState = options.defaultState) !== null && _defaultState !== void 0 ? _defaultState : EMPTY_STATE;
|
|
30
|
+
const defaultState = options.defaultState ?? EMPTY_STATE;
|
|
32
31
|
const [_state, setState] = useState(defaultState);
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
const { componentEventListenersPlugin , registerEventListener , unregisterEventListener } = useComponentEventListeners();
|
|
32
|
+
const state = options.state ?? _state;
|
|
33
|
+
const { componentEventListenersPlugin, registerEventListener, unregisterEventListener } = useComponentEventListeners();
|
|
36
34
|
const setCursorWrapper = useCallback((deco)=>{
|
|
37
35
|
flushSync(()=>{
|
|
38
36
|
_setCursorWrapper(deco);
|
|
39
37
|
});
|
|
40
38
|
}, []);
|
|
41
|
-
var _plugins;
|
|
42
39
|
const plugins = useMemo(()=>[
|
|
43
|
-
...
|
|
40
|
+
...options.plugins ?? [],
|
|
44
41
|
componentEventListenersPlugin,
|
|
45
42
|
beforeInputPlugin(setCursorWrapper)
|
|
46
43
|
], [
|
|
@@ -112,16 +109,13 @@ let didWarnValueDefaultValue = false;
|
|
|
112
109
|
}
|
|
113
110
|
});
|
|
114
111
|
view.update(directEditorProps);
|
|
115
|
-
var _static;
|
|
116
112
|
const editor = useMemo(()=>({
|
|
117
113
|
view,
|
|
118
|
-
cursorWrapper,
|
|
119
114
|
flushSyncRef,
|
|
120
115
|
registerEventListener,
|
|
121
116
|
unregisterEventListener,
|
|
122
|
-
isStatic:
|
|
117
|
+
isStatic: options.static ?? false
|
|
123
118
|
}), [
|
|
124
|
-
cursorWrapper,
|
|
125
119
|
options.static,
|
|
126
120
|
registerEventListener,
|
|
127
121
|
unregisterEventListener,
|
|
@@ -129,6 +123,7 @@ let didWarnValueDefaultValue = false;
|
|
|
129
123
|
]);
|
|
130
124
|
return {
|
|
131
125
|
editor,
|
|
126
|
+
cursorWrapper,
|
|
132
127
|
state
|
|
133
128
|
};
|
|
134
129
|
}
|
|
@@ -20,7 +20,7 @@ import { useLayoutGroupEffect } from "./useLayoutGroupEffect.js";
|
|
|
20
20
|
* as a child of the TiptapEditorView component, including
|
|
21
21
|
* React node view components.
|
|
22
22
|
*/ export function useEditorEffect(effect, dependencies) {
|
|
23
|
-
const { view
|
|
23
|
+
const { view, flushSyncRef } = useContext(EditorContext);
|
|
24
24
|
// The rules of hooks want `effect` to be included in the
|
|
25
25
|
// dependency list, but dependency issues for `effect` will
|
|
26
26
|
// be caught by the linter at the call-site for
|
|
@@ -20,7 +20,7 @@ function assertIsReactEditorView(view) {
|
|
|
20
20
|
* React node view components.
|
|
21
21
|
*/ export function useEditorEventCallback(callback) {
|
|
22
22
|
const ref = useRef(callback);
|
|
23
|
-
const { view
|
|
23
|
+
const { view } = useContext(EditorContext);
|
|
24
24
|
useEditorEffect(()=>{
|
|
25
25
|
ref.current = callback;
|
|
26
26
|
}, [
|
|
@@ -6,7 +6,7 @@ import { useEditorEffect } from "./useEditorEffect.js";
|
|
|
6
6
|
* [the ProseMirror docs](https://prosemirror.net/docs/ref/#view.EditorProps.handleDOMEvents)
|
|
7
7
|
* for more details.
|
|
8
8
|
*/ export function useEditorEventListener(eventType, handler) {
|
|
9
|
-
const { registerEventListener
|
|
9
|
+
const { registerEventListener, unregisterEventListener } = useContext(EditorContext);
|
|
10
10
|
const ref = useRef(handler);
|
|
11
11
|
useEditorEffect(()=>{
|
|
12
12
|
ref.current = handler;
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { useContext } from "react";
|
|
2
2
|
import { EditorContext } from "../contexts/EditorContext.js";
|
|
3
3
|
export function useIsEditorStatic() {
|
|
4
|
-
|
|
5
|
-
var ref1;
|
|
6
|
-
return (ref1 = (ref = useContext(EditorContext)) === null || ref === void 0 ? void 0 : ref.isStatic) !== null && ref1 !== void 0 ? ref1 : false;
|
|
4
|
+
return useContext(EditorContext)?.isStatic ?? false;
|
|
7
5
|
}
|
|
@@ -6,9 +6,8 @@ import { ReactMarkViewDesc, sortViewDescs } from "../viewdesc.js";
|
|
|
6
6
|
import { useClientLayoutEffect } from "./useClientLayoutEffect.js";
|
|
7
7
|
import { useEffectEvent } from "./useEffectEvent.js";
|
|
8
8
|
export function useMarkViewDescription(getDOM, getContentDOM, constructor, props) {
|
|
9
|
-
|
|
10
|
-
const {
|
|
11
|
-
const { parentRef , siblingsRef } = useContext(ChildDescriptionsContext);
|
|
9
|
+
const { view } = useContext(EditorContext);
|
|
10
|
+
const { parentRef, siblingsRef } = useContext(ChildDescriptionsContext);
|
|
12
11
|
const contentDOMRef = useRef(null);
|
|
13
12
|
const viewDescRef = useRef();
|
|
14
13
|
const childrenRef = useRef([]);
|
|
@@ -20,7 +19,7 @@ export function useMarkViewDescription(getDOM, getContentDOM, constructor, props
|
|
|
20
19
|
if (!dom) {
|
|
21
20
|
return;
|
|
22
21
|
}
|
|
23
|
-
const { mark
|
|
22
|
+
const { mark, inline, getPos } = props;
|
|
24
23
|
const markView = constructor(mark, view, inline);
|
|
25
24
|
if (!markView) {
|
|
26
25
|
return;
|
|
@@ -28,7 +27,14 @@ export function useMarkViewDescription(getDOM, getContentDOM, constructor, props
|
|
|
28
27
|
const parent = parentRef.current;
|
|
29
28
|
const children = childrenRef.current;
|
|
30
29
|
const contentDOM = getContentDOM(markView);
|
|
31
|
-
const viewDesc = new ReactMarkViewDesc(parent, children, getPos, mark, dom, contentDOM
|
|
30
|
+
const viewDesc = new ReactMarkViewDesc(parent, children, getPos, mark, dom, contentDOM ?? markView.dom, markView);
|
|
31
|
+
// When create() runs after a destroy() (either here in a layout
|
|
32
|
+
// effect or in refUpdated), the inherited children still reference
|
|
33
|
+
// the just-destroyed desc. Re-parent them onto this fresh desc
|
|
34
|
+
// before any other code can observe the stale pointer.
|
|
35
|
+
for (const child of children){
|
|
36
|
+
child.parent = viewDesc;
|
|
37
|
+
}
|
|
32
38
|
contentDOMRef.current = contentDOM;
|
|
33
39
|
return viewDesc;
|
|
34
40
|
});
|
|
@@ -45,11 +51,10 @@ export function useMarkViewDescription(getDOM, getContentDOM, constructor, props
|
|
|
45
51
|
return false;
|
|
46
52
|
}
|
|
47
53
|
const contentDOM = getContentDOM(viewDesc);
|
|
48
|
-
|
|
49
|
-
if (contentDOM !== ((_contentDOM = viewDesc.contentDOM) !== null && _contentDOM !== void 0 ? _contentDOM : dom)) {
|
|
54
|
+
if (contentDOM !== (viewDesc.contentDOM ?? dom)) {
|
|
50
55
|
return false;
|
|
51
56
|
}
|
|
52
|
-
const { mark
|
|
57
|
+
const { mark } = props;
|
|
53
58
|
return viewDesc.matchesMark(mark);
|
|
54
59
|
});
|
|
55
60
|
const destroy = useEffectEvent(()=>{
|
|
@@ -112,10 +117,9 @@ export function useMarkViewDescription(getDOM, getContentDOM, constructor, props
|
|
|
112
117
|
childrenRef,
|
|
113
118
|
viewDescRef
|
|
114
119
|
]);
|
|
115
|
-
var _current;
|
|
116
120
|
return {
|
|
117
121
|
childContextValue,
|
|
118
|
-
contentDOM:
|
|
122
|
+
contentDOM: contentDOMRef.current ?? viewDescRef.current?.dom,
|
|
119
123
|
refUpdated
|
|
120
124
|
};
|
|
121
125
|
}
|
|
@@ -6,8 +6,8 @@ import { CompositionViewDesc, ReactNodeViewDesc, sortViewDescs } from "../viewde
|
|
|
6
6
|
import { useClientLayoutEffect } from "./useClientLayoutEffect.js";
|
|
7
7
|
import { useEffectEvent } from "./useEffectEvent.js";
|
|
8
8
|
export function useNodeViewDescription(getDOM, getContentDOM, constructor, props) {
|
|
9
|
-
const { view
|
|
10
|
-
const { parentRef
|
|
9
|
+
const { view } = useContext(EditorContext);
|
|
10
|
+
const { parentRef, siblingsRef } = useContext(ChildDescriptionsContext);
|
|
11
11
|
const contentDOMRef = useRef(null);
|
|
12
12
|
const viewDescRef = useRef();
|
|
13
13
|
const childrenRef = useRef([]);
|
|
@@ -19,7 +19,7 @@ export function useNodeViewDescription(getDOM, getContentDOM, constructor, props
|
|
|
19
19
|
if (!dom) {
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
|
-
const { node
|
|
22
|
+
const { node, getPos, decorations, innerDecorations } = props;
|
|
23
23
|
const nodeView = constructor(node, view, getPos, decorations, innerDecorations);
|
|
24
24
|
if (!nodeView) {
|
|
25
25
|
return;
|
|
@@ -29,6 +29,9 @@ export function useNodeViewDescription(getDOM, getContentDOM, constructor, props
|
|
|
29
29
|
const contentDOM = getContentDOM(nodeView);
|
|
30
30
|
const nodeDOM = nodeView.dom;
|
|
31
31
|
const viewDesc = new ReactNodeViewDesc(parent, children, getPos, node, decorations, innerDecorations, dom, contentDOM, nodeDOM, nodeView);
|
|
32
|
+
for (const child of children){
|
|
33
|
+
child.parent = viewDesc;
|
|
34
|
+
}
|
|
32
35
|
const siblings = siblingsRef.current;
|
|
33
36
|
if (!siblings.includes(viewDesc)) {
|
|
34
37
|
siblings.push(viewDesc);
|
|
@@ -56,7 +59,7 @@ export function useNodeViewDescription(getDOM, getContentDOM, constructor, props
|
|
|
56
59
|
if (!dom.contains(viewDesc.nodeDOM)) {
|
|
57
60
|
return false;
|
|
58
61
|
}
|
|
59
|
-
const { node
|
|
62
|
+
const { node, decorations, innerDecorations } = props;
|
|
60
63
|
return viewDesc.matchesNode(node, decorations, innerDecorations) || viewDesc.update(node, decorations, innerDecorations, view);
|
|
61
64
|
});
|
|
62
65
|
const destroy = useEffectEvent(()=>{
|
|
@@ -131,14 +134,13 @@ export function useNodeViewDescription(getDOM, getContentDOM, constructor, props
|
|
|
131
134
|
// Because TextNodeViews can't locate the DOM nodes
|
|
132
135
|
// for compositions, we need to override them here
|
|
133
136
|
if (child instanceof CompositionViewDesc) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if (!compositionTopDOM) throw new Error("Started a composition but couldn't find the text node it belongs to.");
|
|
137
|
+
const compositionTopDOM = viewDesc?.contentDOM?.firstChild;
|
|
138
|
+
if (!compositionTopDOM) throw new Error(`Started a composition but couldn't find the text node it belongs to.`);
|
|
137
139
|
let textDOM = compositionTopDOM;
|
|
138
140
|
while(textDOM.firstChild){
|
|
139
141
|
textDOM = textDOM.firstChild;
|
|
140
142
|
}
|
|
141
|
-
if (!textDOM || !(textDOM instanceof Text)) throw new Error(
|
|
143
|
+
if (!textDOM || !(textDOM instanceof Text)) throw new Error(`Started a composition but couldn't find the text node it belongs to.`);
|
|
142
144
|
child.dom = compositionTopDOM;
|
|
143
145
|
child.textDOM = textDOM;
|
|
144
146
|
child.text = textDOM.data;
|
|
@@ -2,6 +2,6 @@ import { useContext } from "react";
|
|
|
2
2
|
import { EditorContext } from "../contexts/EditorContext.js";
|
|
3
3
|
import { reactKeysPluginKey } from "../plugins/reactKeys.js";
|
|
4
4
|
export function useReactKeys() {
|
|
5
|
-
const { view
|
|
5
|
+
const { view } = useContext(EditorContext);
|
|
6
6
|
return reactKeysPluginKey.getState(view.state);
|
|
7
7
|
}
|
|
@@ -1,22 +1,49 @@
|
|
|
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) {
|
|
5
6
|
let options = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {};
|
|
6
7
|
if (eventData === null) return false;
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
var _to;
|
|
10
|
-
const to = (_to = options.to) !== null && _to !== void 0 ? _to : view.state.selection.to;
|
|
8
|
+
const from = options.from ?? view.state.selection.from;
|
|
9
|
+
const to = options.to ?? view.state.selection.to;
|
|
11
10
|
if (view.someProp("handleTextInput", (f)=>f(view, from, to, eventData, ()=>view.state.tr.insertText(eventData, from, to)))) {
|
|
12
11
|
return true;
|
|
13
12
|
}
|
|
14
|
-
const { tr
|
|
13
|
+
const { tr } = view.state;
|
|
15
14
|
if (options.marks) tr.ensureMarks(options.marks);
|
|
16
15
|
tr.insertText(eventData, from, to);
|
|
17
16
|
view.dispatch(tr);
|
|
18
17
|
return true;
|
|
19
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
|
+
}
|
|
20
47
|
export function beforeInputPlugin(setCursorWrapper) {
|
|
21
48
|
let compositionMarks = null;
|
|
22
49
|
let precompositionSnapshot = null;
|
|
@@ -24,11 +51,11 @@ export function beforeInputPlugin(setCursorWrapper) {
|
|
|
24
51
|
props: {
|
|
25
52
|
handleDOMEvents: {
|
|
26
53
|
compositionstart (view) {
|
|
27
|
-
|
|
28
|
-
view.dispatch(state.tr.deleteSelection());
|
|
54
|
+
compositionMarks = view.state.storedMarks ?? view.state.selection.$from.marks();
|
|
55
|
+
view.dispatch(view.state.tr.deleteSelection());
|
|
56
|
+
handleGapCursorComposition(view);
|
|
57
|
+
const { state } = view;
|
|
29
58
|
const $pos = state.selection.$from;
|
|
30
|
-
var _storedMarks;
|
|
31
|
-
compositionMarks = (_storedMarks = state.storedMarks) !== null && _storedMarks !== void 0 ? _storedMarks : $pos.marks();
|
|
32
59
|
if (compositionMarks) {
|
|
33
60
|
setCursorWrapper(widget(state.selection.from, CursorWrapper, {
|
|
34
61
|
key: "cursor-wrapper",
|
|
@@ -38,7 +65,7 @@ export function beforeInputPlugin(setCursorWrapper) {
|
|
|
38
65
|
// Snapshot the siblings of the node that contains the
|
|
39
66
|
// current cursor. We'll restore this later, so that React
|
|
40
67
|
// doesn't panic about unknown DOM nodes.
|
|
41
|
-
const { node: parent
|
|
68
|
+
const { node: parent } = view.domAtPos($pos.pos);
|
|
42
69
|
precompositionSnapshot = [];
|
|
43
70
|
for (const node of parent.childNodes){
|
|
44
71
|
precompositionSnapshot.push(node);
|
|
@@ -53,8 +80,8 @@ export function beforeInputPlugin(setCursorWrapper) {
|
|
|
53
80
|
compositionend (view, event) {
|
|
54
81
|
// @ts-expect-error Internal property - input
|
|
55
82
|
view.input.composing = false;
|
|
56
|
-
const { state
|
|
57
|
-
const { node: parent
|
|
83
|
+
const { state } = view;
|
|
84
|
+
const { node: parent } = view.domAtPos(state.selection.from);
|
|
58
85
|
if (precompositionSnapshot) {
|
|
59
86
|
// Restore the snapshot of the parent node's children
|
|
60
87
|
// from before the composition started. This gives us a
|
|
@@ -106,15 +133,13 @@ export function beforeInputPlugin(setCursorWrapper) {
|
|
|
106
133
|
keyCode: 13,
|
|
107
134
|
shiftKey: event.inputType === "insertLineBreak"
|
|
108
135
|
});
|
|
109
|
-
var ref;
|
|
110
136
|
// Use someProp to directly call ProseMirror handlers
|
|
111
|
-
return
|
|
137
|
+
return view.someProp("handleKeyDown", (f)=>f(view, keyEvent)) ?? false;
|
|
112
138
|
}
|
|
113
139
|
case "insertReplacementText":
|
|
114
140
|
{
|
|
115
|
-
var ref1, ref2;
|
|
116
141
|
const ranges = event.getTargetRanges();
|
|
117
|
-
|
|
142
|
+
event.dataTransfer?.items[0]?.getAsString((data)=>{
|
|
118
143
|
for (const range of ranges){
|
|
119
144
|
const from = view.posAtDOM(range.startContainer, range.startOffset, 1);
|
|
120
145
|
const to = view.posAtDOM(range.endContainer, range.endOffset, 1);
|
|
@@ -142,11 +167,11 @@ export function beforeInputPlugin(setCursorWrapper) {
|
|
|
142
167
|
case "deleteContent":
|
|
143
168
|
{
|
|
144
169
|
const targetRanges = event.getTargetRanges();
|
|
145
|
-
const { tr
|
|
170
|
+
const { tr } = view.state;
|
|
146
171
|
for (const range of targetRanges){
|
|
147
172
|
const start = view.posAtDOM(range.startContainer, range.startOffset);
|
|
148
173
|
const end = view.posAtDOM(range.endContainer, range.endOffset);
|
|
149
|
-
const { doc
|
|
174
|
+
const { doc } = view.state;
|
|
150
175
|
const storedMarks = doc.resolve(start).marksAcross(doc.resolve(end));
|
|
151
176
|
tr.delete(start, end).setStoredMarks(storedMarks);
|
|
152
177
|
}
|
|
@@ -36,11 +36,10 @@ export const reactKeysPluginKey = new PluginKey("@handlewithcare/react-prosemirr
|
|
|
36
36
|
* and assign its key to that new position, dropping it if the
|
|
37
37
|
* node was deleted.
|
|
38
38
|
*/ apply (tr, value, _, newState) {
|
|
39
|
-
var ref;
|
|
40
39
|
if (!tr.docChanged || composing) {
|
|
41
40
|
return value;
|
|
42
41
|
}
|
|
43
|
-
const overrides =
|
|
42
|
+
const overrides = tr.getMeta(reactKeysPluginKey)?.overrides;
|
|
44
43
|
const next = {
|
|
45
44
|
posToKey: new Map(),
|
|
46
45
|
keyToPos: new Map()
|
|
@@ -50,8 +49,8 @@ export const reactKeysPluginKey = new PluginKey("@handlewithcare/react-prosemirr
|
|
|
50
49
|
return a - b;
|
|
51
50
|
});
|
|
52
51
|
for (const [pos, key] of posToKeyEntries){
|
|
53
|
-
const override = overrides
|
|
54
|
-
const { pos: newPos
|
|
52
|
+
const override = overrides?.[pos];
|
|
53
|
+
const { pos: newPos, deleted } = override === undefined ? tr.mapping.mapResult(pos) : {
|
|
55
54
|
pos: override,
|
|
56
55
|
deleted: false
|
|
57
56
|
};
|
package/dist/esm/props.js
CHANGED
|
@@ -25,7 +25,7 @@ function mergeStyleProps(a, b) {
|
|
|
25
25
|
if (!("STYLE" in b) || typeof b.STYLE !== "string") {
|
|
26
26
|
return a.STYLE;
|
|
27
27
|
}
|
|
28
|
-
return
|
|
28
|
+
return `${a.STYLE.match(/;\s*$/) ? a.STYLE : `${a.STYLE};`} ${b.STYLE}`;
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
31
|
* Merges two sets of React props. Class names
|
|
@@ -178,32 +178,32 @@ function mergeStyleProps(a, b) {
|
|
|
178
178
|
}
|
|
179
179
|
case "maxlength":
|
|
180
180
|
{
|
|
181
|
-
const
|
|
182
|
-
if (!Number.isNaN(
|
|
181
|
+
const numValue = parseInt(attrValue, 10);
|
|
182
|
+
if (!Number.isNaN(numValue)) {
|
|
183
183
|
props.maxLength = attrValue;
|
|
184
184
|
}
|
|
185
185
|
break;
|
|
186
186
|
}
|
|
187
187
|
case "minlength":
|
|
188
188
|
{
|
|
189
|
-
const
|
|
190
|
-
if (!Number.isNaN(
|
|
189
|
+
const numValue = parseInt(attrValue, 10);
|
|
190
|
+
if (!Number.isNaN(numValue)) {
|
|
191
191
|
props.minLength = attrValue;
|
|
192
192
|
}
|
|
193
193
|
break;
|
|
194
194
|
}
|
|
195
195
|
case "max":
|
|
196
196
|
{
|
|
197
|
-
const
|
|
198
|
-
if (!Number.isNaN(
|
|
197
|
+
const numValue = parseInt(attrValue, 10);
|
|
198
|
+
if (!Number.isNaN(numValue)) {
|
|
199
199
|
props.max = attrValue;
|
|
200
200
|
}
|
|
201
201
|
break;
|
|
202
202
|
}
|
|
203
203
|
case "min":
|
|
204
204
|
{
|
|
205
|
-
const
|
|
206
|
-
if (!Number.isNaN(
|
|
205
|
+
const numValue = parseInt(attrValue, 10);
|
|
206
|
+
if (!Number.isNaN(numValue)) {
|
|
207
207
|
props.min = attrValue;
|
|
208
208
|
}
|
|
209
209
|
break;
|
|
@@ -225,8 +225,8 @@ function mergeStyleProps(a, b) {
|
|
|
225
225
|
}
|
|
226
226
|
case "size":
|
|
227
227
|
{
|
|
228
|
-
const
|
|
229
|
-
if (!Number.isNaN(
|
|
228
|
+
const numValue = parseInt(attrValue, 10);
|
|
229
|
+
if (!Number.isNaN(numValue)) {
|
|
230
230
|
props.size = attrValue;
|
|
231
231
|
}
|
|
232
232
|
break;
|
|
@@ -237,8 +237,8 @@ function mergeStyleProps(a, b) {
|
|
|
237
237
|
props.step = attrValue;
|
|
238
238
|
break;
|
|
239
239
|
}
|
|
240
|
-
const
|
|
241
|
-
if (!Number.isNaN(
|
|
240
|
+
const numValue = parseInt(attrValue, 10);
|
|
241
|
+
if (!Number.isNaN(numValue) && numValue > 0) {
|
|
242
242
|
props.step = attrValue;
|
|
243
243
|
}
|
|
244
244
|
break;
|
|
@@ -250,8 +250,8 @@ function mergeStyleProps(a, b) {
|
|
|
250
250
|
}
|
|
251
251
|
case "rows":
|
|
252
252
|
{
|
|
253
|
-
const
|
|
254
|
-
if (!Number.isNaN(
|
|
253
|
+
const numValue = parseInt(attrValue, 10);
|
|
254
|
+
if (!Number.isNaN(numValue)) {
|
|
255
255
|
props.rows = attrValue;
|
|
256
256
|
}
|
|
257
257
|
break;
|