@handlewithcare/react-prosemirror 2.5.2 → 2.6.0-tiptap.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 +2 -3
- package/dist/cjs/ReactEditorView.js +12 -0
- package/dist/cjs/components/CustomNodeView.js +11 -23
- package/dist/cjs/components/ReactNodeView.js +16 -18
- package/dist/cjs/components/__tests__/ProseMirror.composition.test.js +398 -0
- package/dist/cjs/components/__tests__/ProseMirror.domchange.test.js +270 -0
- package/dist/cjs/components/__tests__/ProseMirror.draw-decoration.test.js +1010 -0
- package/dist/cjs/components/__tests__/ProseMirror.draw.test.js +337 -0
- package/dist/cjs/components/__tests__/ProseMirror.node-view.test.js +315 -0
- package/dist/cjs/components/__tests__/ProseMirror.selection.test.js +444 -0
- package/dist/cjs/components/__tests__/ProseMirror.test.js +382 -0
- package/dist/cjs/contexts/__tests__/DeferredLayoutEffects.test.js +141 -0
- package/dist/cjs/hooks/__tests__/useEditorViewLayoutEffect.test.js +108 -0
- package/dist/cjs/hooks/useClientOnly.js +19 -0
- package/dist/cjs/plugins/__tests__/reactKeys.test.js +81 -0
- package/dist/cjs/selection/SelectionDOMObserver.js +171 -0
- package/dist/cjs/selection/hasFocusAndSelection.js +35 -0
- package/dist/cjs/selection/selectionFromDOM.js +77 -0
- package/dist/cjs/selection/selectionToDOM.js +226 -0
- package/dist/cjs/ssr.js +85 -0
- package/dist/cjs/tiptap/TiptapEditorContent.js +93 -0
- package/dist/cjs/tiptap/TiptapEditorView.js +84 -0
- package/dist/cjs/tiptap/hooks/useTiptapEditorEffect.js +27 -0
- package/dist/cjs/tiptap/hooks/useTiptapEditorEventCallback.js +26 -0
- package/dist/cjs/tiptap/index.js +32 -0
- package/dist/cjs/tiptap/tiptapNodeView.js +181 -0
- package/dist/esm/ReactEditorView.js +12 -0
- package/dist/esm/components/CustomNodeView.js +12 -24
- package/dist/esm/components/ReactNodeView.js +16 -18
- package/dist/esm/components/__tests__/ProseMirror.composition.test.js +395 -0
- package/dist/esm/components/__tests__/ProseMirror.domchange.test.js +266 -0
- package/dist/esm/components/__tests__/ProseMirror.draw-decoration.test.js +967 -0
- package/dist/esm/components/__tests__/ProseMirror.draw.test.js +294 -0
- package/dist/esm/components/__tests__/ProseMirror.node-view.test.js +272 -0
- package/dist/esm/components/__tests__/ProseMirror.selection.test.js +440 -0
- package/dist/esm/components/__tests__/ProseMirror.test.js +339 -0
- package/dist/esm/contexts/__tests__/DeferredLayoutEffects.test.js +98 -0
- package/dist/esm/hooks/__tests__/useEditorViewLayoutEffect.test.js +99 -0
- package/dist/esm/hooks/useClientOnly.js +9 -0
- package/dist/esm/hooks/useEditorEffect.js +4 -0
- package/dist/esm/hooks/useEditorEventCallback.js +3 -5
- package/dist/esm/plugins/__tests__/reactKeys.test.js +77 -0
- package/dist/esm/selection/SelectionDOMObserver.js +161 -0
- package/dist/esm/selection/hasFocusAndSelection.js +17 -0
- package/dist/esm/selection/selectionFromDOM.js +59 -0
- package/dist/esm/selection/selectionToDOM.js +196 -0
- package/dist/esm/ssr.js +82 -0
- package/dist/esm/tiptap/TiptapEditorContent.js +42 -0
- package/dist/esm/tiptap/TiptapEditorView.js +35 -0
- package/dist/esm/tiptap/hooks/useTiptapEditorEffect.js +34 -0
- package/dist/esm/tiptap/hooks/useTiptapEditorEventCallback.js +26 -0
- package/dist/esm/tiptap/index.js +5 -0
- package/dist/esm/tiptap/tiptapNodeView.js +149 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/ReactEditorView.d.ts +1 -0
- package/dist/types/constants.d.ts +1 -1
- package/dist/types/hooks/__tests__/useEditorViewLayoutEffect.test.d.ts +1 -0
- package/dist/types/hooks/useClientOnly.d.ts +1 -0
- package/dist/types/hooks/useEditorEffect.d.ts +4 -0
- package/dist/types/hooks/useEditorEventCallback.d.ts +3 -5
- package/dist/types/props.d.ts +26 -26
- package/dist/types/selection/SelectionDOMObserver.d.ts +33 -0
- package/dist/types/selection/hasFocusAndSelection.d.ts +3 -0
- package/dist/types/selection/selectionFromDOM.d.ts +4 -0
- package/dist/types/selection/selectionToDOM.d.ts +9 -0
- package/dist/types/ssr.d.ts +19 -0
- package/dist/types/tiptap/TiptapEditorContent.d.ts +7 -0
- package/dist/types/tiptap/TiptapEditorView.d.ts +13 -0
- package/dist/types/tiptap/hooks/useTiptapEditorEffect.d.ts +21 -0
- package/dist/types/tiptap/hooks/useTiptapEditorEventCallback.d.ts +13 -0
- package/dist/types/tiptap/index.d.ts +5 -0
- package/dist/types/tiptap/tiptapNodeView.d.ts +48 -0
- package/package.json +8 -1
package/README.md
CHANGED
|
@@ -693,15 +693,14 @@ export function SelectionWidget() {
|
|
|
693
693
|
### `NodeViewComponentProps`
|
|
694
694
|
|
|
695
695
|
```tsx
|
|
696
|
-
|
|
696
|
+
interface NodeViewComponentProps extends AllHTMLAttributes<HTMLElement> = {
|
|
697
697
|
nodeProps: {
|
|
698
698
|
decorations: readonly Decoration[];
|
|
699
699
|
innerDecorations: DecorationSource;
|
|
700
700
|
node: Node;
|
|
701
|
-
children?: ReactNode | ReactNode[];
|
|
702
701
|
getPos: () => number;
|
|
703
702
|
};
|
|
704
|
-
}
|
|
703
|
+
};
|
|
705
704
|
```
|
|
706
705
|
|
|
707
706
|
The props that will be passed to all node view components. These props map
|
|
@@ -132,6 +132,18 @@ let ReactEditorView = class ReactEditorView extends _prosemirrorview.EditorView
|
|
|
132
132
|
}
|
|
133
133
|
return undefined;
|
|
134
134
|
}
|
|
135
|
+
destroy() {
|
|
136
|
+
// Prevent the base class from destroying the React-managed nodes.
|
|
137
|
+
// Restore them below after invoking the base class method.
|
|
138
|
+
const reactContent = [
|
|
139
|
+
...this.dom.childNodes
|
|
140
|
+
];
|
|
141
|
+
try {
|
|
142
|
+
super.destroy();
|
|
143
|
+
} finally{
|
|
144
|
+
this.dom.replaceChildren(...reactContent);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
135
147
|
/**
|
|
136
148
|
* Commit effects by appling the pending props and state.
|
|
137
149
|
*
|
|
@@ -59,7 +59,6 @@ const CustomNodeView = /*#__PURE__*/ (0, _react.memo)(function CustomNodeView(pa
|
|
|
59
59
|
let { constructor, node, getPos, innerDeco, outerDeco } = param;
|
|
60
60
|
const ref = (0, _react.useRef)(null);
|
|
61
61
|
const innerRef = (0, _react.useRef)(null);
|
|
62
|
-
const [selected, setSelected] = (0, _react.useState)(false);
|
|
63
62
|
const nodeProps = (0, _react.useMemo)(()=>({
|
|
64
63
|
node,
|
|
65
64
|
getPos,
|
|
@@ -89,7 +88,6 @@ const CustomNodeView = /*#__PURE__*/ (0, _react.memo)(function CustomNodeView(pa
|
|
|
89
88
|
for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){
|
|
90
89
|
args[_key] = arguments[_key];
|
|
91
90
|
}
|
|
92
|
-
setSelected(false);
|
|
93
91
|
const nodeView = createNodeView(...args);
|
|
94
92
|
const contentDOM = nodeView.contentDOM;
|
|
95
93
|
const nodeDOM = nodeView.dom;
|
|
@@ -99,6 +97,9 @@ const CustomNodeView = /*#__PURE__*/ (0, _react.memo)(function CustomNodeView(pa
|
|
|
99
97
|
if (!nodeDOM.hasAttribute("contenteditable")) {
|
|
100
98
|
nodeDOM.contentEditable = "false";
|
|
101
99
|
}
|
|
100
|
+
if (node.type.spec.draggable) {
|
|
101
|
+
nodeDOM.draggable = true;
|
|
102
|
+
}
|
|
102
103
|
}
|
|
103
104
|
return {
|
|
104
105
|
...nodeView,
|
|
@@ -108,22 +109,16 @@ const CustomNodeView = /*#__PURE__*/ (0, _react.memo)(function CustomNodeView(pa
|
|
|
108
109
|
}
|
|
109
110
|
wrapperDOM.removeChild(nodeDOM);
|
|
110
111
|
},
|
|
111
|
-
selectNode: nodeView.selectNode?.bind(nodeView)
|
|
112
|
-
|
|
113
|
-
nodeDOM.classList.add("ProseMirror-selectednode");
|
|
114
|
-
}
|
|
115
|
-
setSelected(true);
|
|
116
|
-
}),
|
|
117
|
-
deselectNode: nodeView.deselectNode?.bind(nodeView) ?? (()=>{
|
|
118
|
-
if (nodeDOM instanceof HTMLElement) {
|
|
119
|
-
nodeDOM.classList.remove("ProseMirror-selectednode");
|
|
120
|
-
}
|
|
121
|
-
setSelected(false);
|
|
122
|
-
}),
|
|
112
|
+
selectNode: nodeView.selectNode?.bind(nodeView),
|
|
113
|
+
deselectNode: nodeView.deselectNode?.bind(nodeView),
|
|
123
114
|
stopEvent: nodeView.stopEvent?.bind(nodeView),
|
|
124
115
|
ignoreMutation: nodeView.ignoreMutation?.bind(nodeView)
|
|
125
116
|
};
|
|
126
117
|
}, nodeProps);
|
|
118
|
+
const Component = node.isInline ? "span" : "div";
|
|
119
|
+
const props = {
|
|
120
|
+
ref: innerRef
|
|
121
|
+
};
|
|
127
122
|
const children = !node.isLeaf && contentDOM ? /*#__PURE__*/ (0, _reactdom.createPortal)(/*#__PURE__*/ _react.default.createElement(_ChildDescriptorsContext.ChildDescriptorsContext.Provider, {
|
|
128
123
|
value: childContextValue
|
|
129
124
|
}, /*#__PURE__*/ _react.default.createElement(_ChildNodeViews.ChildNodeViews, {
|
|
@@ -131,14 +126,7 @@ const CustomNodeView = /*#__PURE__*/ (0, _react.memo)(function CustomNodeView(pa
|
|
|
131
126
|
node: node,
|
|
132
127
|
innerDecorations: innerDeco
|
|
133
128
|
})), contentDOM) : null;
|
|
134
|
-
|
|
135
|
-
ref: innerRef
|
|
136
|
-
}, children);
|
|
137
|
-
const props = {
|
|
138
|
-
...selected || node.type.spec.draggable ? {
|
|
139
|
-
draggable: true
|
|
140
|
-
} : null,
|
|
129
|
+
return /*#__PURE__*/ (0, _react.cloneElement)(outerDeco.reduce(_ChildNodeViews.wrapInDeco, /*#__PURE__*/ _react.default.createElement(Component, props, children)), {
|
|
141
130
|
ref
|
|
142
|
-
};
|
|
143
|
-
return /*#__PURE__*/ (0, _react.cloneElement)(outerDeco.reduce(_ChildNodeViews.wrapInDeco, innerElement), props);
|
|
131
|
+
});
|
|
144
132
|
});
|
|
@@ -58,7 +58,7 @@ function _interop_require_wildcard(obj, nodeInterop) {
|
|
|
58
58
|
}
|
|
59
59
|
const ReactNodeView = /*#__PURE__*/ (0, _react.memo)(function ReactNodeView(param) {
|
|
60
60
|
let { component: Component, outerDeco, getPos, node, innerDeco } = param;
|
|
61
|
-
const [
|
|
61
|
+
const [hasCustomSelectNode, setHasCustomSelectNode] = (0, _react.useState)(false);
|
|
62
62
|
const [selected, setSelected] = (0, _react.useState)(false);
|
|
63
63
|
const ref = (0, _react.useRef)(null);
|
|
64
64
|
const innerRef = (0, _react.useRef)(null);
|
|
@@ -69,11 +69,11 @@ const ReactNodeView = /*#__PURE__*/ (0, _react.memo)(function ReactNodeView(para
|
|
|
69
69
|
const setSelectNode = (0, _react.useCallback)((selectHandler, deselectHandler)=>{
|
|
70
70
|
selectNodeRef.current = selectHandler;
|
|
71
71
|
deselectNodeRef.current = deselectHandler;
|
|
72
|
-
|
|
72
|
+
setHasCustomSelectNode(true);
|
|
73
73
|
return ()=>{
|
|
74
74
|
selectNodeRef.current = null;
|
|
75
75
|
deselectNodeRef.current = null;
|
|
76
|
-
|
|
76
|
+
setHasCustomSelectNode(false);
|
|
77
77
|
};
|
|
78
78
|
}, []);
|
|
79
79
|
const setStopEvent = (0, _react.useCallback)((handler)=>{
|
|
@@ -140,30 +140,28 @@ const ReactNodeView = /*#__PURE__*/ (0, _react.memo)(function ReactNodeView(para
|
|
|
140
140
|
}
|
|
141
141
|
};
|
|
142
142
|
}, nodeProps);
|
|
143
|
-
const
|
|
144
|
-
getPos: getPos,
|
|
145
|
-
node: node,
|
|
146
|
-
innerDecorations: innerDeco
|
|
147
|
-
}) : null;
|
|
148
|
-
const innerProps = {
|
|
143
|
+
const props = {
|
|
149
144
|
nodeProps,
|
|
150
145
|
...!contentDOM && !nodeProps.node.isText && nodeDOM?.nodeName !== "BR" ? {
|
|
151
146
|
contentEditable: false,
|
|
152
147
|
suppressContentEditableWarning: true
|
|
153
148
|
} : null,
|
|
154
|
-
|
|
149
|
+
...!hasCustomSelectNode && selected ? {
|
|
155
150
|
className: "ProseMirror-selectednode"
|
|
156
151
|
} : null,
|
|
157
|
-
|
|
158
|
-
};
|
|
159
|
-
const innerElement = /*#__PURE__*/ _react.default.createElement(Component, innerProps, children);
|
|
160
|
-
const props = {
|
|
161
|
-
...controlSelected && selected || node.type.spec.draggable ? {
|
|
152
|
+
...!hasCustomSelectNode && selected || node.type.spec.draggable ? {
|
|
162
153
|
draggable: true
|
|
163
154
|
} : null,
|
|
164
|
-
ref
|
|
155
|
+
ref: innerRef
|
|
165
156
|
};
|
|
166
|
-
const
|
|
157
|
+
const children = !node.isLeaf ? /*#__PURE__*/ _react.default.createElement(_ChildNodeViews.ChildNodeViews, {
|
|
158
|
+
getPos: getPos,
|
|
159
|
+
node: node,
|
|
160
|
+
innerDecorations: innerDeco
|
|
161
|
+
}) : null;
|
|
162
|
+
const element = /*#__PURE__*/ (0, _react.cloneElement)(outerDeco.reduce(_ChildNodeViews.wrapInDeco, /*#__PURE__*/ _react.default.createElement(Component, props, children)), {
|
|
163
|
+
ref
|
|
164
|
+
});
|
|
167
165
|
return /*#__PURE__*/ _react.default.createElement(_SelectNodeContext.SelectNodeContext.Provider, {
|
|
168
166
|
value: setSelectNode
|
|
169
167
|
}, /*#__PURE__*/ _react.default.createElement(_StopEventContext.StopEventContext.Provider, {
|
|
@@ -172,5 +170,5 @@ const ReactNodeView = /*#__PURE__*/ (0, _react.memo)(function ReactNodeView(para
|
|
|
172
170
|
value: setIgnoreMutation
|
|
173
171
|
}, /*#__PURE__*/ _react.default.createElement(_ChildDescriptorsContext.ChildDescriptorsContext.Provider, {
|
|
174
172
|
value: childContextValue
|
|
175
|
-
},
|
|
173
|
+
}, element))));
|
|
176
174
|
});
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-non-null-assertion */ "use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
const _prosemirrorTestBuilder = require("prosemirror-test-builder");
|
|
6
|
+
const _prosemirrorView = require("prosemirror-view");
|
|
7
|
+
const _editorViewTestHelpersJs = require("../../testing/editorViewTestHelpers.js");
|
|
8
|
+
function endComposition(view, forceUpdate) {
|
|
9
|
+
(0, _prosemirrorView["__endComposition"])(view, forceUpdate);
|
|
10
|
+
}
|
|
11
|
+
function event(pm, type) {
|
|
12
|
+
pm.dom.dispatchEvent(new CompositionEvent(type));
|
|
13
|
+
}
|
|
14
|
+
function edit(node) {
|
|
15
|
+
let text = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "", from = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : node.nodeValue.length, to = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : from;
|
|
16
|
+
const val = node.nodeValue;
|
|
17
|
+
node.nodeValue = val.slice(0, from) + text + val.slice(to);
|
|
18
|
+
document.getSelection().collapse(node, from + text.length);
|
|
19
|
+
return node;
|
|
20
|
+
}
|
|
21
|
+
function hasCompositionNode(_pm) {
|
|
22
|
+
let { focusNode } = document.getSelection();
|
|
23
|
+
while(focusNode && !focusNode.pmViewDesc)focusNode = focusNode.parentNode;
|
|
24
|
+
return focusNode && focusNode.pmViewDesc.constructor.name == "CompositionViewDesc";
|
|
25
|
+
}
|
|
26
|
+
function compose(pm, start, update) {
|
|
27
|
+
let options = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : {};
|
|
28
|
+
event(pm, "compositionstart");
|
|
29
|
+
expect(pm.composing).toBeTruthy();
|
|
30
|
+
let node;
|
|
31
|
+
const sel = document.getSelection();
|
|
32
|
+
for(let i = -1; i < update.length; i++){
|
|
33
|
+
if (i < 0) node = start();
|
|
34
|
+
else update[i](node);
|
|
35
|
+
const { focusNode , focusOffset } = sel;
|
|
36
|
+
// @ts-expect-error Internal property
|
|
37
|
+
pm.domObserver.flush();
|
|
38
|
+
if (options.cancel && i == update.length - 1) {
|
|
39
|
+
expect(hasCompositionNode(pm)).toBeFalsy();
|
|
40
|
+
} else {
|
|
41
|
+
expect(node.parentNode && pm.dom.contains(node.parentNode)).toBeTruthy();
|
|
42
|
+
expect(sel.focusNode === focusNode).toBeTruthy();
|
|
43
|
+
expect(sel.focusOffset === focusOffset).toBeTruthy();
|
|
44
|
+
if (options.node) expect(hasCompositionNode(pm)).toBeTruthy();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
event(pm, "compositionend");
|
|
48
|
+
if (options.end) {
|
|
49
|
+
options.end(node);
|
|
50
|
+
// @ts-expect-error Internal property
|
|
51
|
+
pm.domObserver.flush();
|
|
52
|
+
}
|
|
53
|
+
endComposition(pm);
|
|
54
|
+
expect(pm.composing).toBeFalsy();
|
|
55
|
+
expect(hasCompositionNode(pm)).toBeFalsy();
|
|
56
|
+
}
|
|
57
|
+
// function wordDeco(state: EditorState) {
|
|
58
|
+
// const re = /\w+/g,
|
|
59
|
+
// deco: Decoration[] = [];
|
|
60
|
+
// state.doc.descendants((node, pos) => {
|
|
61
|
+
// if (node.isText)
|
|
62
|
+
// for (let m; (m = re.exec(node.text!)); )
|
|
63
|
+
// deco.push(
|
|
64
|
+
// Decoration.inline(pos + m.index, pos + m.index + m[0].length, {
|
|
65
|
+
// class: "word",
|
|
66
|
+
// })
|
|
67
|
+
// );
|
|
68
|
+
// });
|
|
69
|
+
// return DecorationSet.create(state.doc, deco);
|
|
70
|
+
// }
|
|
71
|
+
// const wordHighlighter = new Plugin({
|
|
72
|
+
// props: { decorations: wordDeco },
|
|
73
|
+
// });
|
|
74
|
+
// const Widget = forwardRef(function Widget(
|
|
75
|
+
// { widget, pos, ...props }: WidgetViewComponentProps,
|
|
76
|
+
// ref: Ref<HTMLElement>
|
|
77
|
+
// ) {
|
|
78
|
+
// return (
|
|
79
|
+
// <var ref={ref} {...props}>
|
|
80
|
+
// ×
|
|
81
|
+
// </var>
|
|
82
|
+
// );
|
|
83
|
+
// });
|
|
84
|
+
// function widgets(positions: number[], sides: number[]) {
|
|
85
|
+
// return new Plugin({
|
|
86
|
+
// state: {
|
|
87
|
+
// init(state) {
|
|
88
|
+
// const deco = positions.map((p, i) =>
|
|
89
|
+
// widget(p, Widget, { side: sides[i] })
|
|
90
|
+
// );
|
|
91
|
+
// return DecorationSet.create(state.doc!, deco);
|
|
92
|
+
// },
|
|
93
|
+
// apply(tr, deco) {
|
|
94
|
+
// return deco.map(tr.mapping, tr.doc);
|
|
95
|
+
// },
|
|
96
|
+
// },
|
|
97
|
+
// props: {
|
|
98
|
+
// decorations(this: Plugin, state) {
|
|
99
|
+
// return this.getState(state);
|
|
100
|
+
// },
|
|
101
|
+
// },
|
|
102
|
+
// });
|
|
103
|
+
// }
|
|
104
|
+
// These unfortunately aren't working at the moment, though
|
|
105
|
+
// composition seems to be working generally.
|
|
106
|
+
describe.skip("EditorView composition", ()=>{
|
|
107
|
+
it("supports composition in an empty block", ()=>{
|
|
108
|
+
const { view: pm } = (0, _editorViewTestHelpersJs.tempEditor)({
|
|
109
|
+
doc: (0, _prosemirrorTestBuilder.doc)((0, _prosemirrorTestBuilder.p)("<a>"))
|
|
110
|
+
});
|
|
111
|
+
compose(pm, ()=>edit(pm.dom.firstChild.appendChild(document.createTextNode("a"))), [
|
|
112
|
+
(n)=>edit(n, "b"),
|
|
113
|
+
(n)=>edit(n, "c")
|
|
114
|
+
], {
|
|
115
|
+
node: true
|
|
116
|
+
});
|
|
117
|
+
expect(pm.state.doc).toEqualNode((0, _prosemirrorTestBuilder.doc)((0, _prosemirrorTestBuilder.p)("abc")));
|
|
118
|
+
});
|
|
119
|
+
it("supports composition at end of block", ()=>{
|
|
120
|
+
const { view: pm } = (0, _editorViewTestHelpersJs.tempEditor)({
|
|
121
|
+
doc: (0, _prosemirrorTestBuilder.doc)((0, _prosemirrorTestBuilder.p)("foo"))
|
|
122
|
+
});
|
|
123
|
+
compose(pm, ()=>edit((0, _editorViewTestHelpersJs.findTextNode)(pm.dom, "foo")), [
|
|
124
|
+
(n)=>edit(n, "!"),
|
|
125
|
+
(n)=>edit(n, "?")
|
|
126
|
+
]);
|
|
127
|
+
expect(pm.state.doc).toEqualNode((0, _prosemirrorTestBuilder.doc)((0, _prosemirrorTestBuilder.p)("foo!?")));
|
|
128
|
+
});
|
|
129
|
+
it("supports composition at end of block in a new node", ()=>{
|
|
130
|
+
const { view: pm } = (0, _editorViewTestHelpersJs.tempEditor)({
|
|
131
|
+
doc: (0, _prosemirrorTestBuilder.doc)((0, _prosemirrorTestBuilder.p)("foo"))
|
|
132
|
+
});
|
|
133
|
+
compose(pm, ()=>edit(pm.dom.firstChild.appendChild(document.createTextNode("!"))), [
|
|
134
|
+
(n)=>edit(n, "?")
|
|
135
|
+
], // $$FORK: We don't use composition view descriptors except for in initially empty nodes
|
|
136
|
+
{
|
|
137
|
+
node: false
|
|
138
|
+
});
|
|
139
|
+
expect(pm.state.doc).toEqualNode((0, _prosemirrorTestBuilder.doc)((0, _prosemirrorTestBuilder.p)("foo!?")));
|
|
140
|
+
});
|
|
141
|
+
it("supports composition at start of block in a new node", ()=>{
|
|
142
|
+
const { view: pm } = (0, _editorViewTestHelpersJs.tempEditor)({
|
|
143
|
+
doc: (0, _prosemirrorTestBuilder.doc)((0, _prosemirrorTestBuilder.p)("foo"))
|
|
144
|
+
});
|
|
145
|
+
compose(pm, ()=>{
|
|
146
|
+
const p = pm.dom.firstChild;
|
|
147
|
+
return edit(p.insertBefore(document.createTextNode("!"), p.firstChild));
|
|
148
|
+
}, [
|
|
149
|
+
(n)=>edit(n, "?")
|
|
150
|
+
], // $$FORK: We don't use composition view descriptors except for in initially empty nodes
|
|
151
|
+
{
|
|
152
|
+
node: false
|
|
153
|
+
});
|
|
154
|
+
expect(pm.state.doc).toEqualNode((0, _prosemirrorTestBuilder.doc)((0, _prosemirrorTestBuilder.p)("!?foo")));
|
|
155
|
+
});
|
|
156
|
+
it("supports composition inside existing text", ()=>{
|
|
157
|
+
const { view: pm } = (0, _editorViewTestHelpersJs.tempEditor)({
|
|
158
|
+
doc: (0, _prosemirrorTestBuilder.doc)((0, _prosemirrorTestBuilder.p)("foo"))
|
|
159
|
+
});
|
|
160
|
+
compose(pm, ()=>edit((0, _editorViewTestHelpersJs.findTextNode)(pm.dom, "foo")), [
|
|
161
|
+
(n)=>edit(n, "x", 1),
|
|
162
|
+
(n)=>edit(n, "y", 2),
|
|
163
|
+
(n)=>edit(n, "z", 3)
|
|
164
|
+
]);
|
|
165
|
+
expect(pm.state.doc).toEqualNode((0, _prosemirrorTestBuilder.doc)((0, _prosemirrorTestBuilder.p)("fxyzoo")));
|
|
166
|
+
});
|
|
167
|
+
// TODO: Offset out of bound
|
|
168
|
+
// eslint-disable-next-line jest/no-disabled-tests
|
|
169
|
+
it.skip("can deal with Android-style newline-after-composition", ()=>{
|
|
170
|
+
const { view: pm } = (0, _editorViewTestHelpersJs.tempEditor)({
|
|
171
|
+
doc: (0, _prosemirrorTestBuilder.doc)((0, _prosemirrorTestBuilder.p)("abcdef"))
|
|
172
|
+
});
|
|
173
|
+
compose(pm, ()=>edit((0, _editorViewTestHelpersJs.findTextNode)(pm.dom, "abcdef")), [
|
|
174
|
+
(n)=>edit(n, "x", 3),
|
|
175
|
+
(n)=>edit(n, "y", 4)
|
|
176
|
+
], {
|
|
177
|
+
end: (n)=>{
|
|
178
|
+
const line = pm.dom.appendChild(document.createElement("div"));
|
|
179
|
+
line.textContent = "def";
|
|
180
|
+
n.nodeValue = "abcxy";
|
|
181
|
+
document.getSelection().collapse(line, 0);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
expect(pm.state.doc).toEqualNode((0, _prosemirrorTestBuilder.doc)((0, _prosemirrorTestBuilder.p)("abcxy"), (0, _prosemirrorTestBuilder.p)("def")));
|
|
185
|
+
});
|
|
186
|
+
it("handles replacement of existing words", ()=>{
|
|
187
|
+
const { view: pm } = (0, _editorViewTestHelpersJs.tempEditor)({
|
|
188
|
+
doc: (0, _prosemirrorTestBuilder.doc)((0, _prosemirrorTestBuilder.p)("one two three"))
|
|
189
|
+
});
|
|
190
|
+
compose(pm, ()=>edit((0, _editorViewTestHelpersJs.findTextNode)(pm.dom, "one two three"), "five", 4, 7), [
|
|
191
|
+
(n)=>edit(n, "seven", 4, 8),
|
|
192
|
+
(n)=>edit(n, "zero", 4, 9)
|
|
193
|
+
]);
|
|
194
|
+
expect(pm.state.doc).toEqualNode((0, _prosemirrorTestBuilder.doc)((0, _prosemirrorTestBuilder.p)("one zero three")));
|
|
195
|
+
});
|
|
196
|
+
it("handles composition inside marks", ()=>{
|
|
197
|
+
const { view: pm } = (0, _editorViewTestHelpersJs.tempEditor)({
|
|
198
|
+
doc: (0, _prosemirrorTestBuilder.doc)((0, _prosemirrorTestBuilder.p)("one ", (0, _prosemirrorTestBuilder.em)("two")))
|
|
199
|
+
});
|
|
200
|
+
compose(pm, ()=>edit((0, _editorViewTestHelpersJs.findTextNode)(pm.dom, "two"), "o"), [
|
|
201
|
+
(n)=>edit(n, "o"),
|
|
202
|
+
(n)=>edit(n, "w")
|
|
203
|
+
]);
|
|
204
|
+
expect(pm.state.doc).toEqualNode((0, _prosemirrorTestBuilder.doc)((0, _prosemirrorTestBuilder.p)("one ", (0, _prosemirrorTestBuilder.em)("twooow"))));
|
|
205
|
+
});
|
|
206
|
+
it.skip("handles composition in a mark that has multiple children", ()=>{
|
|
207
|
+
const { view: pm } = (0, _editorViewTestHelpersJs.tempEditor)({
|
|
208
|
+
doc: (0, _prosemirrorTestBuilder.doc)((0, _prosemirrorTestBuilder.p)("one ", (0, _prosemirrorTestBuilder.em)("two", (0, _prosemirrorTestBuilder.strong)(" three"))))
|
|
209
|
+
});
|
|
210
|
+
compose(pm, ()=>edit((0, _editorViewTestHelpersJs.findTextNode)(pm.dom, "two"), "o"), [
|
|
211
|
+
(n)=>edit(n, "o"),
|
|
212
|
+
(n)=>edit(n, "w")
|
|
213
|
+
]);
|
|
214
|
+
expect(pm.state.doc).toEqualNode((0, _prosemirrorTestBuilder.doc)((0, _prosemirrorTestBuilder.p)("one ", (0, _prosemirrorTestBuilder.em)("twooow", (0, _prosemirrorTestBuilder.strong)(" three")))));
|
|
215
|
+
});
|
|
216
|
+
// it("supports composition in a cursor wrapper", () => {
|
|
217
|
+
// const { view: pm } = tempEditor({ doc: doc(p("<a>")) });
|
|
218
|
+
// pm.dispatch(pm.state.tr.addStoredMark(schema.marks.em.create()));
|
|
219
|
+
// compose(
|
|
220
|
+
// pm,
|
|
221
|
+
// () =>
|
|
222
|
+
// edit(pm.dom.firstChild!.appendChild(document.createTextNode("")), "a"),
|
|
223
|
+
// [(n) => edit(n, "b"), (n) => edit(n, "c")],
|
|
224
|
+
// { node: true }
|
|
225
|
+
// );
|
|
226
|
+
// ist(pm.state.doc, doc(p(em("abc"))), eq);
|
|
227
|
+
// });
|
|
228
|
+
// it("handles composition in a multi-child mark with a cursor wrapper", () => {
|
|
229
|
+
// const { view: pm } = requireFocus(
|
|
230
|
+
// tempEditor({ doc: doc(p("one ", em("two<a>", strong(" three")))) })
|
|
231
|
+
// );
|
|
232
|
+
// pm.dispatch(pm.state.tr.addStoredMark(schema.marks.code.create()));
|
|
233
|
+
// const emNode = pm.dom.querySelector("em")!;
|
|
234
|
+
// compose(
|
|
235
|
+
// pm,
|
|
236
|
+
// () =>
|
|
237
|
+
// edit(
|
|
238
|
+
// emNode.insertBefore(
|
|
239
|
+
// document.createTextNode(""),
|
|
240
|
+
// emNode.querySelector("strong")
|
|
241
|
+
// ),
|
|
242
|
+
// "o"
|
|
243
|
+
// ),
|
|
244
|
+
// [(n) => edit(n, "o"), (n) => edit(n, "w")],
|
|
245
|
+
// { node: true }
|
|
246
|
+
// );
|
|
247
|
+
// ist(
|
|
248
|
+
// pm.state.doc,
|
|
249
|
+
// doc(p("one ", em("two", code("oow"), strong(" three")))),
|
|
250
|
+
// eq
|
|
251
|
+
// );
|
|
252
|
+
// });
|
|
253
|
+
// it("doesn't get interrupted by changes in decorations", () => {
|
|
254
|
+
// const { view: pm } = requireFocus(
|
|
255
|
+
// tempEditor({ doc: doc(p("foo ...")), plugins: [wordHighlighter] })
|
|
256
|
+
// );
|
|
257
|
+
// compose(pm, () => edit(findTextNode(pm.dom, " ...")!), [
|
|
258
|
+
// (n) => edit(n, "hi", 1, 4),
|
|
259
|
+
// ]);
|
|
260
|
+
// ist(pm.state.doc, doc(p("foo hi")), eq);
|
|
261
|
+
// });
|
|
262
|
+
// it("works inside highlighted text", () => {
|
|
263
|
+
// const { view: pm } = requireFocus(
|
|
264
|
+
// tempEditor({ doc: doc(p("one two")), plugins: [wordHighlighter] })
|
|
265
|
+
// );
|
|
266
|
+
// compose(pm, () => edit(findTextNode(pm.dom, "one")!, "x"), [
|
|
267
|
+
// (n) => edit(n, "y"),
|
|
268
|
+
// (n) => edit(n, "."),
|
|
269
|
+
// ]);
|
|
270
|
+
// ist(pm.state.doc, doc(p("onexy. two")), eq);
|
|
271
|
+
// });
|
|
272
|
+
// it("can handle compositions spanning multiple nodes", () => {
|
|
273
|
+
// const { view: pm } = requireFocus(
|
|
274
|
+
// tempEditor({ doc: doc(p("one two")), plugins: [wordHighlighter] })
|
|
275
|
+
// );
|
|
276
|
+
// compose(
|
|
277
|
+
// pm,
|
|
278
|
+
// () => edit(findTextNode(pm.dom, "two")!, "a"),
|
|
279
|
+
// [(n) => edit(n, "b"), (n) => edit(n, "c")],
|
|
280
|
+
// {
|
|
281
|
+
// end: (n: Text) => {
|
|
282
|
+
// n.parentNode!.previousSibling!.remove();
|
|
283
|
+
// n.parentNode!.previousSibling!.remove();
|
|
284
|
+
// return edit(n, "xyzone ", 0);
|
|
285
|
+
// },
|
|
286
|
+
// }
|
|
287
|
+
// );
|
|
288
|
+
// ist(pm.state.doc, doc(p("xyzone twoabc")), eq);
|
|
289
|
+
// });
|
|
290
|
+
// it("doesn't overwrite widgets next to the composition", () => {
|
|
291
|
+
// const { view: pm } = requireFocus(
|
|
292
|
+
// tempEditor({ doc: doc(p("")), plugins: [widgets([1, 1], [-1, 1])] })
|
|
293
|
+
// );
|
|
294
|
+
// compose(
|
|
295
|
+
// pm,
|
|
296
|
+
// () => {
|
|
297
|
+
// const p = pm.dom.firstChild!;
|
|
298
|
+
// return edit(p.insertBefore(document.createTextNode("a"), p.lastChild));
|
|
299
|
+
// },
|
|
300
|
+
// [(n) => edit(n, "b", 0, 1)],
|
|
301
|
+
// {
|
|
302
|
+
// end: () => {
|
|
303
|
+
// ist(pm.dom.querySelectorAll("var").length, 2);
|
|
304
|
+
// },
|
|
305
|
+
// }
|
|
306
|
+
// );
|
|
307
|
+
// ist(pm.state.doc, doc(p("b")), eq);
|
|
308
|
+
// });
|
|
309
|
+
// it("cancels composition when a change fully overlaps with it", () => {
|
|
310
|
+
// const { view: pm } = requireFocus(
|
|
311
|
+
// tempEditor({ doc: doc(p("one"), p("two"), p("three")) })
|
|
312
|
+
// );
|
|
313
|
+
// compose(
|
|
314
|
+
// pm,
|
|
315
|
+
// () => edit(findTextNode(pm.dom, "two")!, "x"),
|
|
316
|
+
// [() => pm.dispatch(pm.state.tr.insertText("---", 3, 13))],
|
|
317
|
+
// { cancel: true }
|
|
318
|
+
// );
|
|
319
|
+
// ist(pm.state.doc, doc(p("on---hree")), eq);
|
|
320
|
+
// });
|
|
321
|
+
// it("cancels composition when a change partially overlaps with it", () => {
|
|
322
|
+
// const { view: pm } = requireFocus(
|
|
323
|
+
// tempEditor({ doc: doc(p("one"), p("two"), p("three")) })
|
|
324
|
+
// );
|
|
325
|
+
// compose(
|
|
326
|
+
// pm,
|
|
327
|
+
// () => edit(findTextNode(pm.dom, "two")!, "x", 0),
|
|
328
|
+
// [() => pm.dispatch(pm.state.tr.insertText("---", 7, 15))],
|
|
329
|
+
// { cancel: true }
|
|
330
|
+
// );
|
|
331
|
+
// ist(pm.state.doc, doc(p("one"), p("x---ee")), eq);
|
|
332
|
+
// });
|
|
333
|
+
// it("cancels composition when a change happens inside of it", () => {
|
|
334
|
+
// const { view: pm } = requireFocus(
|
|
335
|
+
// tempEditor({ doc: doc(p("one"), p("two"), p("three")) })
|
|
336
|
+
// );
|
|
337
|
+
// compose(
|
|
338
|
+
// pm,
|
|
339
|
+
// () => edit(findTextNode(pm.dom, "two")!, "x", 0),
|
|
340
|
+
// [() => pm.dispatch(pm.state.tr.insertText("!", 7, 8))],
|
|
341
|
+
// { cancel: true }
|
|
342
|
+
// );
|
|
343
|
+
// ist(pm.state.doc, doc(p("one"), p("x!wo"), p("three")), eq);
|
|
344
|
+
// });
|
|
345
|
+
// it("doesn't cancel composition when a change happens elsewhere", () => {
|
|
346
|
+
// const { view: pm } = requireFocus(
|
|
347
|
+
// tempEditor({ doc: doc(p("one"), p("two"), p("three")) })
|
|
348
|
+
// );
|
|
349
|
+
// compose(pm, () => edit(findTextNode(pm.dom, "two")!, "x", 0), [
|
|
350
|
+
// (n) => edit(n, "y", 1),
|
|
351
|
+
// () => pm.dispatch(pm.state.tr.insertText("!", 2, 3)),
|
|
352
|
+
// (n) => edit(n, "z", 2),
|
|
353
|
+
// ]);
|
|
354
|
+
// ist(pm.state.doc, doc(p("o!e"), p("xyztwo"), p("three")), eq);
|
|
355
|
+
// });
|
|
356
|
+
// it("handles compositions rapidly following each other", () => {
|
|
357
|
+
// const { view: pm } = tempEditor({ doc: doc(p("one"), p("two")) });
|
|
358
|
+
// event(pm, "compositionstart");
|
|
359
|
+
// const one = findTextNode(pm.dom, "one")!;
|
|
360
|
+
// edit(one, "!");
|
|
361
|
+
// pm.domObserver.flush();
|
|
362
|
+
// event(pm, "compositionend");
|
|
363
|
+
// one.nodeValue = "one!!";
|
|
364
|
+
// const L2 = pm.dom.lastChild;
|
|
365
|
+
// event(pm, "compositionstart");
|
|
366
|
+
// const two = findTextNode(pm.dom, "two")!;
|
|
367
|
+
// ist(pm.dom.lastChild, L2);
|
|
368
|
+
// edit(two, ".");
|
|
369
|
+
// pm.domObserver.flush();
|
|
370
|
+
// ist(document.getSelection()!.focusNode, two);
|
|
371
|
+
// ist(document.getSelection()!.focusOffset, 4);
|
|
372
|
+
// ist(pm.composing);
|
|
373
|
+
// event(pm, "compositionend");
|
|
374
|
+
// pm.domObserver.flush();
|
|
375
|
+
// ist(pm.state.doc, doc(p("one!!"), p("two.")), eq);
|
|
376
|
+
// });
|
|
377
|
+
// function crossParagraph(first = false) {
|
|
378
|
+
// const { view: pm } = requireFocus(
|
|
379
|
+
// tempEditor({ doc: doc(p("one <a>two"), p("three"), p("four<b> five")) })
|
|
380
|
+
// );
|
|
381
|
+
// compose(
|
|
382
|
+
// pm,
|
|
383
|
+
// () => {
|
|
384
|
+
// for (let i = 0; i < 2; i++)
|
|
385
|
+
// pm.dom.removeChild(first ? pm.dom.lastChild! : pm.dom.firstChild!);
|
|
386
|
+
// const target = pm.dom.firstChild!.firstChild as Text;
|
|
387
|
+
// target.nodeValue = "one A five";
|
|
388
|
+
// document.getSelection()!.collapse(target, 4);
|
|
389
|
+
// return target;
|
|
390
|
+
// },
|
|
391
|
+
// [(n) => edit(n, "B", 4, 5), (n) => edit(n, "C", 4, 5)]
|
|
392
|
+
// );
|
|
393
|
+
// ist(pm.state.doc, doc(p("one C five")), eq);
|
|
394
|
+
// }
|
|
395
|
+
// it("can handle cross-paragraph compositions", () => crossParagraph(true));
|
|
396
|
+
// it("can handle cross-paragraph compositions (keeping the last paragraph)", () =>
|
|
397
|
+
// crossParagraph(false));
|
|
398
|
+
});
|