@handlewithcare/react-prosemirror 2.4.12 → 2.5.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/dist/cjs/AbstractEditorView.js +4 -0
- package/dist/cjs/ReactEditorView.js +156 -0
- package/dist/cjs/StaticEditorView.js +86 -0
- package/dist/cjs/components/ChildNodeViews.js +59 -30
- package/dist/cjs/components/CustomNodeView.js +9 -25
- package/dist/cjs/components/DocNodeView.js +6 -15
- package/dist/cjs/components/MarkView.js +1 -2
- package/dist/cjs/components/NativeWidgetView.js +2 -3
- package/dist/cjs/components/NodeView.js +1 -1
- package/dist/cjs/components/ProseMirror.js +11 -14
- package/dist/cjs/components/ReactNodeView.js +3 -4
- package/dist/cjs/components/SeparatorHackView.js +1 -2
- package/dist/cjs/components/TextNodeView.js +4 -5
- package/dist/cjs/components/TrailingHackView.js +1 -2
- package/dist/cjs/components/WidgetView.js +2 -4
- package/dist/cjs/constants.js +33 -0
- package/dist/cjs/hooks/useEditor.js +32 -228
- package/dist/cjs/hooks/useEditorEffect.js +2 -2
- package/dist/cjs/hooks/useEditorEventCallback.js +8 -5
- package/dist/cjs/hooks/useNodeViewDescriptor.js +10 -10
- package/dist/cjs/hooks/useReactKeys.js +1 -1
- package/dist/cjs/testing/editorViewTestHelpers.js +0 -2
- package/dist/cjs/viewdesc.js +10 -9
- package/dist/esm/AbstractEditorView.js +1 -0
- package/dist/esm/ReactEditorView.js +156 -0
- package/dist/esm/StaticEditorView.js +76 -0
- package/dist/esm/components/ChildNodeViews.js +60 -32
- package/dist/esm/components/CustomNodeView.js +9 -25
- package/dist/esm/components/DocNodeView.js +6 -15
- package/dist/esm/components/MarkView.js +1 -2
- package/dist/esm/components/NativeWidgetView.js +2 -3
- package/dist/esm/components/NodeView.js +1 -1
- package/dist/esm/components/ProseMirror.js +11 -14
- package/dist/esm/components/ReactNodeView.js +3 -4
- package/dist/esm/components/SeparatorHackView.js +1 -2
- package/dist/esm/components/TextNodeView.js +4 -5
- package/dist/esm/components/TrailingHackView.js +1 -2
- package/dist/esm/components/WidgetView.js +2 -4
- package/dist/esm/constants.js +15 -0
- package/dist/esm/hooks/useEditor.js +28 -217
- package/dist/esm/hooks/useEditorEffect.js +2 -2
- package/dist/esm/hooks/useEditorEventCallback.js +8 -5
- package/dist/esm/hooks/useNodeViewDescriptor.js +10 -10
- package/dist/esm/hooks/useReactKeys.js +1 -1
- package/dist/esm/testing/editorViewTestHelpers.js +0 -2
- package/dist/esm/viewdesc.js +3 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/AbstractEditorView.d.ts +27 -0
- package/dist/types/ReactEditorView.d.ts +79 -0
- package/dist/types/StaticEditorView.d.ts +24 -0
- package/dist/types/components/ChildNodeViews.d.ts +2 -2
- package/dist/types/components/CustomNodeView.d.ts +2 -2
- package/dist/types/components/DocNodeView.d.ts +2 -5
- package/dist/types/components/MarkView.d.ts +2 -2
- package/dist/types/components/NativeWidgetView.d.ts +2 -2
- package/dist/types/components/NodeView.d.ts +2 -2
- package/dist/types/components/ReactNodeView.d.ts +2 -2
- package/dist/types/components/SeparatorHackView.d.ts +2 -2
- package/dist/types/components/TextNodeView.d.ts +4 -3
- package/dist/types/components/TrailingHackView.d.ts +2 -2
- package/dist/types/components/WidgetView.d.ts +2 -2
- package/dist/types/constants.d.ts +4 -0
- package/dist/types/contexts/EditorContext.d.ts +6 -4
- package/dist/types/decorations/computeDocDeco.d.ts +3 -2
- package/dist/types/decorations/viewDecorations.d.ts +3 -2
- package/dist/types/hooks/useEditor.d.ts +5 -46
- package/dist/types/hooks/useNodeViewDescriptor.d.ts +1 -1
- package/dist/types/hooks/useReactKeys.d.ts +1 -1
- package/dist/types/props.d.ts +3 -3
- package/dist/types/viewdesc.d.ts +6 -5
- package/package.json +6 -2
- package/dist/cjs/components/Editor.js +0 -28
- package/dist/cjs/components/NodeViews.js +0 -73
- package/dist/cjs/components/__tests__/LayoutGroup.test.js +0 -141
- package/dist/cjs/components/__tests__/ProseMirror.test.js +0 -255
- package/dist/cjs/contexts/NodeViewsContext.js +0 -10
- package/dist/cjs/hooks/__tests__/useEditorViewLayoutEffect.test.js +0 -107
- package/dist/cjs/hooks/__tests__/useNodeViews.test.js +0 -159
- package/dist/cjs/hooks/useEditorView.js +0 -100
- package/dist/cjs/hooks/useNodePos.js +0 -69
- package/dist/cjs/hooks/useNodeViews.js +0 -100
- package/dist/cjs/nodeViews/createReactNodeViewConstructor.js +0 -244
- package/dist/cjs/nodeViews/phrasingContentTags.js +0 -57
- package/dist/cjs/plugins/__tests__/react.test.js +0 -139
- package/dist/cjs/plugins/react.js +0 -71
- package/dist/cjs/selection/SelectionDOMObserver.js +0 -171
- package/dist/cjs/selection/hasFocusAndSelection.js +0 -35
- package/dist/cjs/selection/selectionFromDOM.js +0 -77
- package/dist/cjs/selection/selectionToDOM.js +0 -226
- package/dist/cjs/ssr.js +0 -85
- package/dist/esm/components/Editor.js +0 -15
- package/dist/esm/components/NodeViews.js +0 -26
- package/dist/esm/components/__tests__/LayoutGroup.test.js +0 -98
- package/dist/esm/components/__tests__/ProseMirror.test.js +0 -207
- package/dist/esm/contexts/NodeViewsContext.js +0 -9
- package/dist/esm/hooks/__tests__/useEditorViewLayoutEffect.test.js +0 -98
- package/dist/esm/hooks/__tests__/useNodeViews.test.js +0 -116
- package/dist/esm/hooks/useEditorView.js +0 -99
- package/dist/esm/hooks/useNodePos.js +0 -16
- package/dist/esm/hooks/useNodeViews.js +0 -53
- package/dist/esm/nodeViews/createReactNodeViewConstructor.js +0 -214
- package/dist/esm/nodeViews/phrasingContentTags.js +0 -49
- package/dist/esm/plugins/__tests__/react.test.js +0 -135
- package/dist/esm/plugins/react.js +0 -64
- package/dist/esm/selection/SelectionDOMObserver.js +0 -161
- package/dist/esm/selection/hasFocusAndSelection.js +0 -17
- package/dist/esm/selection/selectionFromDOM.js +0 -59
- package/dist/esm/selection/selectionToDOM.js +0 -196
- package/dist/esm/ssr.js +0 -82
- package/dist/types/components/Editor.d.ts +0 -7
- package/dist/types/components/NodeViews.d.ts +0 -6
- package/dist/types/components/__tests__/LayoutGroup.test.d.ts +0 -1
- package/dist/types/contexts/NodeViewsContext.d.ts +0 -19
- package/dist/types/hooks/__tests__/useEditorViewLayoutEffect.test.d.ts +0 -1
- package/dist/types/hooks/__tests__/useNodeViews.test.d.ts +0 -1
- package/dist/types/hooks/useEditorView.d.ts +0 -23
- package/dist/types/hooks/useNodePos.d.ts +0 -9
- package/dist/types/hooks/useNodeViews.d.ts +0 -5
- package/dist/types/nodeViews/createReactNodeViewConstructor.d.ts +0 -48
- package/dist/types/nodeViews/phrasingContentTags.d.ts +0 -1
- package/dist/types/plugins/__tests__/react.test.d.ts +0 -1
- package/dist/types/plugins/react.d.ts +0 -21
- package/dist/types/selection/SelectionDOMObserver.d.ts +0 -33
- package/dist/types/selection/hasFocusAndSelection.d.ts +0 -3
- package/dist/types/selection/selectionFromDOM.d.ts +0 -4
- package/dist/types/selection/selectionToDOM.d.ts +0 -9
- package/dist/types/ssr.d.ts +0 -19
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { Schema } from "prosemirror-model";
|
|
2
|
-
import { EditorState } from "prosemirror-state";
|
|
3
|
-
import { findWrapping } from "prosemirror-transform";
|
|
4
|
-
import { react, reactPluginKey } from "../react.js";
|
|
5
|
-
const schema = new Schema({
|
|
6
|
-
nodes: {
|
|
7
|
-
doc: {
|
|
8
|
-
content: "block+"
|
|
9
|
-
},
|
|
10
|
-
paragraph: {
|
|
11
|
-
group: "block",
|
|
12
|
-
content: "inline*"
|
|
13
|
-
},
|
|
14
|
-
list: {
|
|
15
|
-
group: "block",
|
|
16
|
-
content: "list_item+"
|
|
17
|
-
},
|
|
18
|
-
list_item: {
|
|
19
|
-
content: "paragraph+"
|
|
20
|
-
},
|
|
21
|
-
text: {
|
|
22
|
-
group: "inline"
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
describe("reactNodeViewPlugin", ()=>{
|
|
27
|
-
it("should create a unique key for each node", ()=>{
|
|
28
|
-
const editorState = EditorState.create({
|
|
29
|
-
doc: schema.topNodeType.create(null, [
|
|
30
|
-
schema.nodes.paragraph.create(),
|
|
31
|
-
schema.nodes.paragraph.create(),
|
|
32
|
-
schema.nodes.paragraph.create()
|
|
33
|
-
]),
|
|
34
|
-
plugins: [
|
|
35
|
-
react()
|
|
36
|
-
]
|
|
37
|
-
});
|
|
38
|
-
const pluginState = reactPluginKey.getState(editorState);
|
|
39
|
-
expect(pluginState.posToKey.size).toBe(3);
|
|
40
|
-
});
|
|
41
|
-
it("should maintain key stability when possible", ()=>{
|
|
42
|
-
const initialEditorState = EditorState.create({
|
|
43
|
-
doc: schema.topNodeType.create(null, [
|
|
44
|
-
schema.nodes.paragraph.create(),
|
|
45
|
-
schema.nodes.paragraph.create(),
|
|
46
|
-
schema.nodes.paragraph.create()
|
|
47
|
-
]),
|
|
48
|
-
plugins: [
|
|
49
|
-
react()
|
|
50
|
-
]
|
|
51
|
-
});
|
|
52
|
-
const initialPluginState = reactPluginKey.getState(initialEditorState);
|
|
53
|
-
const nextEditorState = initialEditorState.apply(initialEditorState.tr.insertText("Hello, world!", 1));
|
|
54
|
-
const nextPluginState = reactPluginKey.getState(nextEditorState);
|
|
55
|
-
expect(Array.from(nextPluginState.keyToPos.keys())).toEqual(Array.from(initialPluginState.keyToPos.keys()));
|
|
56
|
-
});
|
|
57
|
-
it("should create unique keys for new nodes", ()=>{
|
|
58
|
-
const initialEditorState = EditorState.create({
|
|
59
|
-
doc: schema.topNodeType.create(null, [
|
|
60
|
-
schema.nodes.paragraph.create(),
|
|
61
|
-
schema.nodes.paragraph.create(),
|
|
62
|
-
schema.nodes.paragraph.create()
|
|
63
|
-
]),
|
|
64
|
-
plugins: [
|
|
65
|
-
react()
|
|
66
|
-
]
|
|
67
|
-
});
|
|
68
|
-
const initialPluginState = reactPluginKey.getState(initialEditorState);
|
|
69
|
-
const nextEditorState = initialEditorState.apply(initialEditorState.tr.insert(0, schema.nodes.list.createAndFill()));
|
|
70
|
-
const nextPluginState = reactPluginKey.getState(nextEditorState);
|
|
71
|
-
// Adds new keys for new nodes
|
|
72
|
-
expect(nextPluginState.keyToPos.size).toBe(6);
|
|
73
|
-
// Maintains keys for previous nodes that are still there
|
|
74
|
-
Array.from(initialPluginState.keyToPos.keys()).forEach((key)=>{
|
|
75
|
-
expect(Array.from(nextPluginState.keyToPos.keys())).toContain(key);
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
it("should maintain key stability when splitting a node", ()=>{
|
|
79
|
-
const initialEditorState = EditorState.create({
|
|
80
|
-
doc: schema.topNodeType.create(null, [
|
|
81
|
-
schema.nodes.list.create(null, [
|
|
82
|
-
schema.nodes.list_item.create(null, [
|
|
83
|
-
schema.nodes.paragraph.create(null, [
|
|
84
|
-
schema.text("first")
|
|
85
|
-
])
|
|
86
|
-
])
|
|
87
|
-
])
|
|
88
|
-
]),
|
|
89
|
-
plugins: [
|
|
90
|
-
react()
|
|
91
|
-
]
|
|
92
|
-
});
|
|
93
|
-
const initialPluginState = reactPluginKey.getState(initialEditorState);
|
|
94
|
-
const nextEditorState = initialEditorState.apply(initialEditorState.tr.insert(1, schema.nodes.list_item.create(null, [
|
|
95
|
-
schema.nodes.paragraph.create(null, [
|
|
96
|
-
schema.text("second")
|
|
97
|
-
])
|
|
98
|
-
])));
|
|
99
|
-
const nextPluginState = reactPluginKey.getState(nextEditorState);
|
|
100
|
-
// The new list item was inserted before the original one,
|
|
101
|
-
// pushing it further into the document. The original list
|
|
102
|
-
// item should keep its original key, and the new list item
|
|
103
|
-
// should be assigned a new one
|
|
104
|
-
expect(nextPluginState.posToKey.get(11)).toBe(initialPluginState.posToKey.get(1));
|
|
105
|
-
expect(nextPluginState.posToKey.get(1)).not.toBe(initialPluginState.posToKey.get(1));
|
|
106
|
-
});
|
|
107
|
-
it("should maintain key stability when wrapping a node", ()=>{
|
|
108
|
-
const initialEditorState = EditorState.create({
|
|
109
|
-
doc: schema.topNodeType.create(null, [
|
|
110
|
-
schema.nodes.paragraph.create(null, [
|
|
111
|
-
schema.text("content")
|
|
112
|
-
])
|
|
113
|
-
]),
|
|
114
|
-
plugins: [
|
|
115
|
-
react()
|
|
116
|
-
]
|
|
117
|
-
});
|
|
118
|
-
const initialPluginState = reactPluginKey.getState(initialEditorState);
|
|
119
|
-
const start = 1;
|
|
120
|
-
const end = 9;
|
|
121
|
-
const tr = initialEditorState.tr.delete(start, end);
|
|
122
|
-
const $start = tr.doc.resolve(start);
|
|
123
|
-
const range = $start.blockRange();
|
|
124
|
-
const wrapping = range && findWrapping(range, schema.nodes.list, null);
|
|
125
|
-
tr.wrap(range, wrapping);
|
|
126
|
-
const nextEditorState = initialEditorState.apply(tr);
|
|
127
|
-
const nextPluginState = reactPluginKey.getState(nextEditorState);
|
|
128
|
-
// The new list and list item nodes were wrapped around the
|
|
129
|
-
// paragraph, pushing it further into the document. The paragraph
|
|
130
|
-
// should keep its original key, and the new nodes
|
|
131
|
-
// should be assigned a new one
|
|
132
|
-
expect(nextPluginState.posToKey.get(2)).toBe(initialPluginState.posToKey.get(0));
|
|
133
|
-
expect(nextPluginState.posToKey.get(0)).not.toBe(initialPluginState.posToKey.get(0));
|
|
134
|
-
});
|
|
135
|
-
});
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { Plugin, PluginKey } from "prosemirror-state";
|
|
2
|
-
/**
|
|
3
|
-
* This is a stand-in for the doc node itself, which doesn't have a
|
|
4
|
-
* unique position to map to.
|
|
5
|
-
*/ export const ROOT_NODE_KEY = Symbol("@nytimes/react-prosemirror/root-node-key");
|
|
6
|
-
export function createNodeKey() {
|
|
7
|
-
return Math.floor(Math.random() * 0xffffff).toString(16);
|
|
8
|
-
}
|
|
9
|
-
export const reactPluginKey = new PluginKey("@nytimes/react-prosemirror/react");
|
|
10
|
-
/**
|
|
11
|
-
* Tracks a unique key for each (non-text) node in the
|
|
12
|
-
* document, identified by its current position. Keys are
|
|
13
|
-
* (mostly) stable across transaction applications. The
|
|
14
|
-
* key for a given node can be accessed by that node's
|
|
15
|
-
* current position in the document, and vice versa.
|
|
16
|
-
*/ export function react() {
|
|
17
|
-
return new Plugin({
|
|
18
|
-
key: reactPluginKey,
|
|
19
|
-
state: {
|
|
20
|
-
init (_, state) {
|
|
21
|
-
const next = {
|
|
22
|
-
posToKey: new Map(),
|
|
23
|
-
keyToPos: new Map()
|
|
24
|
-
};
|
|
25
|
-
state.doc.descendants((node, pos)=>{
|
|
26
|
-
if (node.isText) return false;
|
|
27
|
-
const key = createNodeKey();
|
|
28
|
-
next.posToKey.set(pos, key);
|
|
29
|
-
next.keyToPos.set(key, pos);
|
|
30
|
-
return true;
|
|
31
|
-
});
|
|
32
|
-
return next;
|
|
33
|
-
},
|
|
34
|
-
/**
|
|
35
|
-
* Keeps node keys (mostly) stable across transactions.
|
|
36
|
-
*
|
|
37
|
-
* To accomplish this, we map each node position backwards
|
|
38
|
-
* through the transaction to identify its previous position,
|
|
39
|
-
* and thereby retrieve its previous key.
|
|
40
|
-
*/ apply (tr, value, _, newState) {
|
|
41
|
-
if (!tr.docChanged) return value;
|
|
42
|
-
const next = {
|
|
43
|
-
posToKey: new Map(),
|
|
44
|
-
keyToPos: new Map()
|
|
45
|
-
};
|
|
46
|
-
for (const [pos, key] of value.posToKey.entries()){
|
|
47
|
-
const { pos: newPos , deleted } = tr.mapping.mapResult(pos);
|
|
48
|
-
if (deleted) continue;
|
|
49
|
-
next.posToKey.set(newPos, key);
|
|
50
|
-
next.keyToPos.set(key, newPos);
|
|
51
|
-
}
|
|
52
|
-
newState.doc.descendants((node, pos)=>{
|
|
53
|
-
if (node.isText) return false;
|
|
54
|
-
if (next.posToKey.has(pos)) return true;
|
|
55
|
-
const key = createNodeKey();
|
|
56
|
-
next.posToKey.set(pos, key);
|
|
57
|
-
next.keyToPos.set(key, pos);
|
|
58
|
-
return true;
|
|
59
|
-
});
|
|
60
|
-
return next;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
}
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import { Selection } from "prosemirror-state";
|
|
2
|
-
import { browser } from "../browser.js";
|
|
3
|
-
import { parentNode, selectionCollapsed } from "../dom.js";
|
|
4
|
-
import { hasFocusAndSelection } from "./hasFocusAndSelection.js";
|
|
5
|
-
import { selectionFromDOM } from "./selectionFromDOM.js";
|
|
6
|
-
import { isEquivalentPosition, selectionToDOM } from "./selectionToDOM.js";
|
|
7
|
-
let SelectionState = class SelectionState {
|
|
8
|
-
anchorNode = null;
|
|
9
|
-
anchorOffset = 0;
|
|
10
|
-
focusNode = null;
|
|
11
|
-
focusOffset = 0;
|
|
12
|
-
set(sel) {
|
|
13
|
-
this.anchorNode = sel.anchorNode;
|
|
14
|
-
this.anchorOffset = sel.anchorOffset;
|
|
15
|
-
this.focusNode = sel.focusNode;
|
|
16
|
-
this.focusOffset = sel.focusOffset;
|
|
17
|
-
}
|
|
18
|
-
clear() {
|
|
19
|
-
this.anchorNode = this.focusNode = null;
|
|
20
|
-
}
|
|
21
|
-
eq(sel) {
|
|
22
|
-
return sel.anchorNode == this.anchorNode && sel.anchorOffset == this.anchorOffset && sel.focusNode == this.focusNode && sel.focusOffset == this.focusOffset;
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
export class SelectionDOMObserver {
|
|
26
|
-
view;
|
|
27
|
-
flushingSoon;
|
|
28
|
-
currentSelection;
|
|
29
|
-
suppressingSelectionUpdates;
|
|
30
|
-
constructor(view){
|
|
31
|
-
this.view = view;
|
|
32
|
-
this.flushingSoon = -1;
|
|
33
|
-
this.currentSelection = new SelectionState();
|
|
34
|
-
this.suppressingSelectionUpdates = false;
|
|
35
|
-
this.view = view;
|
|
36
|
-
this.onSelectionChange = this.onSelectionChange.bind(this);
|
|
37
|
-
}
|
|
38
|
-
connectSelection() {
|
|
39
|
-
this.view.dom.ownerDocument.addEventListener("selectionchange", this.onSelectionChange);
|
|
40
|
-
}
|
|
41
|
-
disconnectSelection() {
|
|
42
|
-
this.view.dom.ownerDocument.removeEventListener("selectionchange", this.onSelectionChange);
|
|
43
|
-
}
|
|
44
|
-
stop() {
|
|
45
|
-
this.disconnectSelection();
|
|
46
|
-
}
|
|
47
|
-
start() {
|
|
48
|
-
this.connectSelection();
|
|
49
|
-
}
|
|
50
|
-
suppressSelectionUpdates() {
|
|
51
|
-
this.suppressingSelectionUpdates = true;
|
|
52
|
-
setTimeout(()=>this.suppressingSelectionUpdates = false, 50);
|
|
53
|
-
}
|
|
54
|
-
setCurSelection() {
|
|
55
|
-
// @ts-expect-error Internal method
|
|
56
|
-
this.currentSelection.set(this.view.domSelectionRange());
|
|
57
|
-
}
|
|
58
|
-
ignoreSelectionChange(sel) {
|
|
59
|
-
if (!sel.focusNode) return true;
|
|
60
|
-
const ancestors = new Set();
|
|
61
|
-
let container;
|
|
62
|
-
for(let scan = sel.focusNode; scan; scan = parentNode(scan))ancestors.add(scan);
|
|
63
|
-
for(let scan = sel.anchorNode; scan; scan = parentNode(scan))if (ancestors.has(scan)) {
|
|
64
|
-
container = scan;
|
|
65
|
-
break;
|
|
66
|
-
}
|
|
67
|
-
// @ts-expect-error Internal property (docView)
|
|
68
|
-
const desc = container && this.view.docView.nearestDesc(container);
|
|
69
|
-
if (desc && desc.ignoreMutation({
|
|
70
|
-
type: "selection",
|
|
71
|
-
target: container?.nodeType == 3 ? container?.parentNode : container
|
|
72
|
-
})) {
|
|
73
|
-
this.setCurSelection();
|
|
74
|
-
return true;
|
|
75
|
-
}
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
registerMutation() {
|
|
79
|
-
// pass
|
|
80
|
-
}
|
|
81
|
-
flushSoon() {
|
|
82
|
-
if (this.flushingSoon < 0) this.flushingSoon = window.setTimeout(()=>{
|
|
83
|
-
this.flushingSoon = -1;
|
|
84
|
-
this.flush();
|
|
85
|
-
}, 20);
|
|
86
|
-
}
|
|
87
|
-
updateSelection() {
|
|
88
|
-
const { view } = this;
|
|
89
|
-
const compositionID = // @ts-expect-error Internal property (input)
|
|
90
|
-
view.input.compositionPendingChanges || // @ts-expect-error Internal property (input)
|
|
91
|
-
(view.composing ? view.input.compositionID : 0);
|
|
92
|
-
// @ts-expect-error Internal property (input)
|
|
93
|
-
view.input.compositionPendingChanges = 0;
|
|
94
|
-
const origin = // @ts-expect-error Internal property (input)
|
|
95
|
-
view.input.lastSelectionTime > Date.now() - 50 ? view.input.lastSelectionOrigin : null;
|
|
96
|
-
const newSel = selectionFromDOM(view, origin);
|
|
97
|
-
if (newSel && !view.state.selection.eq(newSel)) {
|
|
98
|
-
const tr = view.state.tr.setSelection(newSel);
|
|
99
|
-
if (origin == "pointer") tr.setMeta("pointer", true);
|
|
100
|
-
else if (origin == "key") tr.scrollIntoView();
|
|
101
|
-
if (compositionID) tr.setMeta("composition", compositionID);
|
|
102
|
-
view.dispatch(tr);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
selectionToDOM() {
|
|
106
|
-
const { view } = this;
|
|
107
|
-
selectionToDOM(view);
|
|
108
|
-
// @ts-expect-error Internal property (domSelectionRange)
|
|
109
|
-
const sel = view.domSelectionRange();
|
|
110
|
-
this.currentSelection.set(sel);
|
|
111
|
-
}
|
|
112
|
-
flush() {
|
|
113
|
-
const { view } = this;
|
|
114
|
-
// @ts-expect-error Internal property (docView)
|
|
115
|
-
if (!view.docView || this.flushingSoon > -1) return;
|
|
116
|
-
// @ts-expect-error Internal property (domSelectionRange)
|
|
117
|
-
const sel = view.domSelectionRange();
|
|
118
|
-
const newSel = !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(view) && !this.ignoreSelectionChange(sel);
|
|
119
|
-
let readSel = null;
|
|
120
|
-
// If it looks like the browser has reset the selection to the
|
|
121
|
-
// start of the document after focus, restore the selection from
|
|
122
|
-
// the state
|
|
123
|
-
if (newSel && // @ts-expect-error Internal property (input)
|
|
124
|
-
view.input.lastFocus > Date.now() - 200 && // @ts-expect-error Internal property (input)
|
|
125
|
-
Math.max(view.input.lastTouch, view.input.lastClick.time) < Date.now() - 300 && selectionCollapsed(sel) && (readSel = selectionFromDOM(view)) && readSel.eq(Selection.near(view.state.doc.resolve(0), 1))) {
|
|
126
|
-
// @ts-expect-error Internal property (input)
|
|
127
|
-
view.input.lastFocus = 0;
|
|
128
|
-
selectionToDOM(view);
|
|
129
|
-
this.currentSelection.set(sel);
|
|
130
|
-
// @ts-expect-error Internal property (scrollToSelection)
|
|
131
|
-
view.scrollToSelection();
|
|
132
|
-
} else if (newSel) {
|
|
133
|
-
this.updateSelection();
|
|
134
|
-
if (!this.currentSelection.eq(sel)) selectionToDOM(view);
|
|
135
|
-
this.currentSelection.set(sel);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
forceFlush() {
|
|
139
|
-
if (this.flushingSoon > -1) {
|
|
140
|
-
window.clearTimeout(this.flushingSoon);
|
|
141
|
-
this.flushingSoon = -1;
|
|
142
|
-
this.flush();
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
onSelectionChange() {
|
|
146
|
-
if (!hasFocusAndSelection(this.view)) return;
|
|
147
|
-
if (this.view.composing) return;
|
|
148
|
-
if (this.suppressingSelectionUpdates) return selectionToDOM(this.view);
|
|
149
|
-
// Deletions on IE11 fire their events in the wrong order, giving
|
|
150
|
-
// us a selection change event before the DOM changes are
|
|
151
|
-
// reported.
|
|
152
|
-
if (browser.ie && browser.ie_version <= 11 && !this.view.state.selection.empty) {
|
|
153
|
-
// @ts-expect-error Internal method
|
|
154
|
-
const sel = this.view.domSelectionRange();
|
|
155
|
-
// Selection.isCollapsed isn't reliable on IE
|
|
156
|
-
if (sel.focusNode && isEquivalentPosition(sel.focusNode, sel.focusOffset, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
157
|
-
sel.anchorNode, sel.anchorOffset)) return this.flushSoon();
|
|
158
|
-
}
|
|
159
|
-
this.flush();
|
|
160
|
-
}
|
|
161
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export function hasFocusAndSelection(view) {
|
|
2
|
-
if (view.editable && !view.hasFocus()) return false;
|
|
3
|
-
return hasSelection(view);
|
|
4
|
-
}
|
|
5
|
-
export function hasSelection(view) {
|
|
6
|
-
// @ts-expect-error Internal method
|
|
7
|
-
const sel = view.domSelectionRange();
|
|
8
|
-
if (!sel.anchorNode) return false;
|
|
9
|
-
try {
|
|
10
|
-
// Firefox will raise 'permission denied' errors when accessing
|
|
11
|
-
// properties of `sel.anchorNode` when it's in a generated CSS
|
|
12
|
-
// element.
|
|
13
|
-
return view.dom.contains(sel.anchorNode.nodeType == 3 ? sel.anchorNode.parentNode : sel.anchorNode) && (view.editable || view.dom.contains(sel.focusNode?.nodeType == 3 ? sel.focusNode?.parentNode : sel.focusNode));
|
|
14
|
-
} catch (_) {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { NodeSelection, TextSelection } from "prosemirror-state";
|
|
2
|
-
import { isOnEdge, selectionCollapsed } from "../dom.js";
|
|
3
|
-
export function selectionBetween(view, $anchor, $head, bias) {
|
|
4
|
-
return view.someProp("createSelectionBetween", (f)=>f(view, $anchor, $head)) || TextSelection.between($anchor, $head, bias);
|
|
5
|
-
}
|
|
6
|
-
export function selectionFromDOM(view) {
|
|
7
|
-
let origin = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : null;
|
|
8
|
-
// @ts-expect-error Internal method
|
|
9
|
-
const domSel = view.domSelectionRange(), doc = view.state.doc;
|
|
10
|
-
if (!domSel.focusNode) return null;
|
|
11
|
-
// @ts-expect-error Internal method
|
|
12
|
-
let nearestDesc = view.docView.nearestDesc(domSel.focusNode);
|
|
13
|
-
const inWidget = nearestDesc && nearestDesc.size == 0;
|
|
14
|
-
// @ts-expect-error Internal method
|
|
15
|
-
let head = view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset, 1);
|
|
16
|
-
if (head < 0) return null;
|
|
17
|
-
let $head = doc.resolve(head), anchor, selection;
|
|
18
|
-
if (selectionCollapsed(domSel)) {
|
|
19
|
-
anchor = head;
|
|
20
|
-
while(nearestDesc && !nearestDesc.node)nearestDesc = nearestDesc.parent;
|
|
21
|
-
const nearestDescNode = nearestDesc.node;
|
|
22
|
-
if (nearestDesc && nearestDescNode.isAtom && NodeSelection.isSelectable(nearestDescNode) && nearestDesc.parent && !(nearestDescNode.isInline && isOnEdge(domSel.focusNode, domSel.focusOffset, nearestDesc.dom))) {
|
|
23
|
-
const pos = nearestDesc.posBefore;
|
|
24
|
-
selection = new NodeSelection(head == pos ? $head : doc.resolve(pos));
|
|
25
|
-
}
|
|
26
|
-
} else {
|
|
27
|
-
if (// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
28
|
-
domSel instanceof view.dom.ownerDocument.defaultView.Selection && domSel.rangeCount > 1) {
|
|
29
|
-
let min = head, max = head;
|
|
30
|
-
for(let i = 0; i < domSel.rangeCount; i++){
|
|
31
|
-
const range = domSel.getRangeAt(i);
|
|
32
|
-
min = Math.min(min, // @ts-expect-error Internal method
|
|
33
|
-
view.docView.posFromDOM(range.startContainer, range.startOffset, 1));
|
|
34
|
-
max = Math.max(max, // @ts-expect-error Internal method
|
|
35
|
-
view.docView.posFromDOM(range.endContainer, range.endOffset, -1));
|
|
36
|
-
}
|
|
37
|
-
if (min < 0) return null;
|
|
38
|
-
[anchor, head] = max == view.state.selection.anchor ? [
|
|
39
|
-
max,
|
|
40
|
-
min
|
|
41
|
-
] : [
|
|
42
|
-
min,
|
|
43
|
-
max
|
|
44
|
-
];
|
|
45
|
-
$head = doc.resolve(head);
|
|
46
|
-
} else {
|
|
47
|
-
// @ts-expect-error Internal method
|
|
48
|
-
anchor = view.docView.posFromDOM(// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
49
|
-
domSel.anchorNode, domSel.anchorOffset, 1);
|
|
50
|
-
}
|
|
51
|
-
if (anchor < 0) return null;
|
|
52
|
-
}
|
|
53
|
-
const $anchor = doc.resolve(anchor);
|
|
54
|
-
if (!selection) {
|
|
55
|
-
const bias = origin == "pointer" || view.state.selection.head < $head.pos && !inWidget ? 1 : -1;
|
|
56
|
-
selection = selectionBetween(view, $anchor, $head, bias);
|
|
57
|
-
}
|
|
58
|
-
return selection;
|
|
59
|
-
}
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import { NodeSelection, TextSelection } from "prosemirror-state";
|
|
2
|
-
import { browser } from "../browser.js";
|
|
3
|
-
// Scans forward and backward through DOM positions equivalent to the
|
|
4
|
-
// given one to see if the two are in the same place (i.e. after a
|
|
5
|
-
// text node vs at the end of that text node)
|
|
6
|
-
export const isEquivalentPosition = function(node, off, targetNode, targetOff) {
|
|
7
|
-
return targetNode && (scanFor(node, off, targetNode, targetOff, -1) || scanFor(node, off, targetNode, targetOff, 1));
|
|
8
|
-
};
|
|
9
|
-
export function hasBlockDesc(dom) {
|
|
10
|
-
let desc;
|
|
11
|
-
for(let cur = dom; cur; cur = cur.parentNode)if (desc = cur.pmViewDesc) break;
|
|
12
|
-
return desc && desc.node && desc.node.isBlock && (desc.dom == dom || desc.contentDOM == dom);
|
|
13
|
-
}
|
|
14
|
-
const atomElements = /^(img|br|input|textarea|hr)$/i;
|
|
15
|
-
function scanFor(node, off, targetNode, targetOff, dir) {
|
|
16
|
-
for(;;){
|
|
17
|
-
if (node == targetNode && off == targetOff) return true;
|
|
18
|
-
if (off == (dir < 0 ? 0 : nodeSize(node))) {
|
|
19
|
-
const parent = node.parentNode;
|
|
20
|
-
if (!parent || parent.nodeType != 1 || hasBlockDesc(node) || atomElements.test(node.nodeName) || node.contentEditable == "false") return false;
|
|
21
|
-
off = domIndex(node) + (dir < 0 ? 0 : 1);
|
|
22
|
-
node = parent;
|
|
23
|
-
} else if (node.nodeType == 1) {
|
|
24
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
25
|
-
node = node.childNodes[off + (dir < 0 ? -1 : 0)];
|
|
26
|
-
if (node.contentEditable == "false") return false;
|
|
27
|
-
off = dir < 0 ? nodeSize(node) : 0;
|
|
28
|
-
} else {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
export const domIndex = function(node) {
|
|
34
|
-
let n = node;
|
|
35
|
-
for(let index = 0;; index++){
|
|
36
|
-
n = n.previousSibling;
|
|
37
|
-
if (!n) return index;
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
export function nodeSize(node) {
|
|
41
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
42
|
-
return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
|
|
43
|
-
}
|
|
44
|
-
export function syncNodeSelection(view, sel) {
|
|
45
|
-
const v = view;
|
|
46
|
-
if (sel instanceof NodeSelection) {
|
|
47
|
-
const desc = v.docView.descAt(sel.from);
|
|
48
|
-
if (desc != v.lastSelectedViewDesc) {
|
|
49
|
-
clearNodeSelection(v);
|
|
50
|
-
if (desc) desc.selectNode();
|
|
51
|
-
v.lastSelectedViewDesc = desc;
|
|
52
|
-
}
|
|
53
|
-
} else {
|
|
54
|
-
clearNodeSelection(v);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
// Clear all DOM statefulness of the last node selection.
|
|
58
|
-
function clearNodeSelection(view) {
|
|
59
|
-
const v = view;
|
|
60
|
-
if (v.lastSelectedViewDesc) {
|
|
61
|
-
if (v.lastSelectedViewDesc.parent) v.lastSelectedViewDesc.deselectNode();
|
|
62
|
-
v.lastSelectedViewDesc = undefined;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
export function hasSelection(view) {
|
|
66
|
-
const v = view;
|
|
67
|
-
const sel = v.domSelectionRange();
|
|
68
|
-
if (!sel.anchorNode) return false;
|
|
69
|
-
try {
|
|
70
|
-
// Firefox will raise 'permission denied' errors when accessing
|
|
71
|
-
// properties of `sel.anchorNode` when it's in a generated CSS
|
|
72
|
-
// element.
|
|
73
|
-
return v.dom.contains(sel.anchorNode.nodeType == 3 ? sel.anchorNode.parentNode : sel.anchorNode) && (v.editable || v.dom.contains(// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
74
|
-
sel.focusNode.nodeType == 3 ? sel.focusNode.parentNode : sel.focusNode));
|
|
75
|
-
} catch (_) {
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
function editorOwnsSelection(view) {
|
|
80
|
-
return view.editable ? view.hasFocus() : hasSelection(view) && document.activeElement && document.activeElement.contains(view.dom);
|
|
81
|
-
}
|
|
82
|
-
function selectCursorWrapper(view) {
|
|
83
|
-
const v = view;
|
|
84
|
-
const domSel = v.domSelection(), range = document.createRange();
|
|
85
|
-
if (!domSel) return;
|
|
86
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
87
|
-
const node = v.cursorWrapper.dom, img = node.nodeName == "IMG";
|
|
88
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
89
|
-
if (img) range.setStart(node.parentNode, domIndex(node) + 1);
|
|
90
|
-
else range.setStart(node, 0);
|
|
91
|
-
range.collapse(true);
|
|
92
|
-
domSel.removeAllRanges();
|
|
93
|
-
domSel.addRange(range);
|
|
94
|
-
// Kludge to kill 'control selection' in IE11 when selecting an
|
|
95
|
-
// invisible cursor wrapper, since that would result in those weird
|
|
96
|
-
// resize handles and a selection that considers the absolutely
|
|
97
|
-
// positioned wrapper, rather than the root editable node, the
|
|
98
|
-
// focused element.
|
|
99
|
-
if (!img && !v.state.selection.visible && browser.ie && browser.ie_version <= 11) {
|
|
100
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
101
|
-
node.disabled = true;
|
|
102
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
103
|
-
node.disabled = false;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
function temporarilyEditableNear(view, pos) {
|
|
107
|
-
const v = view;
|
|
108
|
-
const { node, offset } = v.docView.domFromPos(pos, 0);
|
|
109
|
-
const after = offset < node.childNodes.length ? node.childNodes[offset] : null;
|
|
110
|
-
const before = offset ? node.childNodes[offset - 1] : null;
|
|
111
|
-
if (browser.safari && after && after.contentEditable == "false") return setEditable(after);
|
|
112
|
-
if ((!after || after.contentEditable == "false") && (!before || before.contentEditable == "false")) {
|
|
113
|
-
if (after) return setEditable(after);
|
|
114
|
-
else if (before) return setEditable(before);
|
|
115
|
-
}
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
function setEditable(element) {
|
|
119
|
-
element.contentEditable = "true";
|
|
120
|
-
if (browser.safari && element.draggable) {
|
|
121
|
-
element.draggable = false;
|
|
122
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
123
|
-
element.wasDraggable = true;
|
|
124
|
-
}
|
|
125
|
-
return element;
|
|
126
|
-
}
|
|
127
|
-
function resetEditable(element) {
|
|
128
|
-
element.contentEditable = "false";
|
|
129
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
130
|
-
if (element.wasDraggable) {
|
|
131
|
-
element.draggable = true;
|
|
132
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
133
|
-
element.wasDraggable = null;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
function removeClassOnSelectionChange(view) {
|
|
137
|
-
const v = view;
|
|
138
|
-
const doc = v.dom.ownerDocument;
|
|
139
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
140
|
-
doc.removeEventListener("selectionchange", v.input.hideSelectionGuard);
|
|
141
|
-
const domSel = v.domSelectionRange();
|
|
142
|
-
const node = domSel.anchorNode, offset = domSel.anchorOffset;
|
|
143
|
-
doc.addEventListener("selectionchange", v.input.hideSelectionGuard = ()=>{
|
|
144
|
-
if (domSel.anchorNode != node || domSel.anchorOffset != offset) {
|
|
145
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
146
|
-
doc.removeEventListener("selectionchange", v.input.hideSelectionGuard);
|
|
147
|
-
setTimeout(()=>{
|
|
148
|
-
if (!editorOwnsSelection(v) || v.state.selection.visible) v.dom.classList.remove("ProseMirror-hideselection");
|
|
149
|
-
}, 20);
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
const brokenSelectBetweenUneditable = browser.safari || browser.chrome && browser.chrome_version < 63;
|
|
154
|
-
export function selectionToDOM(view) {
|
|
155
|
-
let force = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : false;
|
|
156
|
-
const v = view;
|
|
157
|
-
const sel = v.state.selection;
|
|
158
|
-
syncNodeSelection(v, sel);
|
|
159
|
-
if (!editorOwnsSelection(v)) return;
|
|
160
|
-
// The delayed drag selection causes issues with Cell Selections
|
|
161
|
-
// in Safari. And the drag selection delay is to workarond issues
|
|
162
|
-
// which only present in Chrome.
|
|
163
|
-
if (!force && v.input.mouseDown && v.input.mouseDown.allowDefault && browser.chrome) {
|
|
164
|
-
const domSel = v.domSelectionRange(), curSel = v.domObserver.currentSelection;
|
|
165
|
-
if (domSel.anchorNode && curSel.anchorNode && isEquivalentPosition(domSel.anchorNode, domSel.anchorOffset, curSel.anchorNode, curSel.anchorOffset)) {
|
|
166
|
-
v.input.mouseDown.delayedSelectionSync = true;
|
|
167
|
-
v.domObserver.setCurSelection();
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
v.domObserver.disconnectSelection();
|
|
172
|
-
if (v.cursorWrapper) {
|
|
173
|
-
selectCursorWrapper(v);
|
|
174
|
-
} else {
|
|
175
|
-
const { anchor, head } = sel;
|
|
176
|
-
let resetEditableFrom;
|
|
177
|
-
let resetEditableTo;
|
|
178
|
-
if (brokenSelectBetweenUneditable && !(sel instanceof TextSelection)) {
|
|
179
|
-
if (!sel.$from.parent.inlineContent) resetEditableFrom = temporarilyEditableNear(v, sel.from);
|
|
180
|
-
if (!sel.empty && !sel.$from.parent.inlineContent) resetEditableTo = temporarilyEditableNear(v, sel.to);
|
|
181
|
-
}
|
|
182
|
-
v.docView.setSelection(anchor, head, v, force);
|
|
183
|
-
if (brokenSelectBetweenUneditable) {
|
|
184
|
-
if (resetEditableFrom) resetEditable(resetEditableFrom);
|
|
185
|
-
if (resetEditableTo) resetEditable(resetEditableTo);
|
|
186
|
-
}
|
|
187
|
-
if (sel.visible) {
|
|
188
|
-
v.dom.classList.remove("ProseMirror-hideselection");
|
|
189
|
-
} else {
|
|
190
|
-
v.dom.classList.add("ProseMirror-hideselection");
|
|
191
|
-
if ("onselectionchange" in document) removeClassOnSelectionChange(v);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
v.domObserver.setCurSelection();
|
|
195
|
-
v.domObserver.connectSelection();
|
|
196
|
-
}
|