prosemirror-view 1.34.2 → 1.35.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/CHANGELOG.md +14 -0
- package/dist/index.cjs +31 -19
- package/dist/index.d.cts +19 -10
- package/dist/index.d.ts +19 -10
- package/dist/index.js +25 -13
- package/package.json +1 -1
- package/src/domobserver.ts +2 -5
- package/src/input.ts +1 -3
- package/src/selection.ts +16 -4
- package/src/viewdesc.ts +22 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## 1.35.0 (2024-10-29)
|
|
2
|
+
|
|
3
|
+
### New features
|
|
4
|
+
|
|
5
|
+
The new `NodeView.multiType` flag is now required to opt into handling different node types in a single node view. It used to be the default, but this tripped too many people up.
|
|
6
|
+
|
|
7
|
+
## 1.34.3 (2024-09-19)
|
|
8
|
+
|
|
9
|
+
### Bug fixes
|
|
10
|
+
|
|
11
|
+
Fix an issue that would, during some types of document updates, leave the document DOM in an improper form.
|
|
12
|
+
|
|
13
|
+
Handle the case where drag-selecting in Firefox creates a selection with multiple distinct ranges.
|
|
14
|
+
|
|
1
15
|
## 1.34.2 (2024-09-03)
|
|
2
16
|
|
|
3
17
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -1564,7 +1564,7 @@ var CustomNodeViewDesc = function (_NodeViewDesc2) {
|
|
|
1564
1564
|
key: "update",
|
|
1565
1565
|
value: function update(node, outerDeco, innerDeco, view) {
|
|
1566
1566
|
if (this.dirty == NODE_DIRTY) return false;
|
|
1567
|
-
if (this.spec.update) {
|
|
1567
|
+
if (this.spec.update && (this.node.type == node.type || this.spec.multiType)) {
|
|
1568
1568
|
var result = this.spec.update(node, outerDeco, innerDeco);
|
|
1569
1569
|
if (result) this.updateInner(node, outerDeco, innerDeco, view);
|
|
1570
1570
|
return result;
|
|
@@ -1840,6 +1840,7 @@ var ViewTreeUpdater = function () {
|
|
|
1840
1840
|
this.index++;
|
|
1841
1841
|
return true;
|
|
1842
1842
|
} else if (!locked && (updated = this.recreateWrapper(next, node, outerDeco, innerDeco, view, pos))) {
|
|
1843
|
+
this.destroyBetween(this.index, i);
|
|
1843
1844
|
this.top.children[this.index] = updated;
|
|
1844
1845
|
if (updated.contentDOM) {
|
|
1845
1846
|
updated.dirty = CONTENT_DIRTY;
|
|
@@ -1858,7 +1859,7 @@ var ViewTreeUpdater = function () {
|
|
|
1858
1859
|
}, {
|
|
1859
1860
|
key: "recreateWrapper",
|
|
1860
1861
|
value: function recreateWrapper(next, node, outerDeco, innerDeco, view, pos) {
|
|
1861
|
-
if (next.dirty || node.isAtom || !next.children.length || !next.node.content.eq(node.content)) return null;
|
|
1862
|
+
if (next.dirty || node.isAtom || !next.children.length || !next.node.content.eq(node.content) || !sameOuterDeco(outerDeco, next.outerDeco) || !innerDeco.eq(next.innerDeco)) return null;
|
|
1862
1863
|
var wrapper = NodeViewDesc.create(this.top, node, outerDeco, innerDeco, view, pos);
|
|
1863
1864
|
if (wrapper.contentDOM) {
|
|
1864
1865
|
wrapper.children = next.children;
|
|
@@ -2105,10 +2106,10 @@ function selectionFromDOM(view) {
|
|
|
2105
2106
|
var head = view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset, 1);
|
|
2106
2107
|
if (head < 0) return null;
|
|
2107
2108
|
var $head = doc.resolve(head),
|
|
2108
|
-
|
|
2109
|
+
anchor,
|
|
2109
2110
|
selection;
|
|
2110
2111
|
if (selectionCollapsed(domSel)) {
|
|
2111
|
-
|
|
2112
|
+
anchor = head;
|
|
2112
2113
|
while (nearestDesc && !nearestDesc.node) nearestDesc = nearestDesc.parent;
|
|
2113
2114
|
var nearestDescNode = nearestDesc.node;
|
|
2114
2115
|
if (nearestDesc && nearestDescNode.isAtom && prosemirrorState.NodeSelection.isSelectable(nearestDescNode) && nearestDesc.parent && !(nearestDescNode.isInline && isOnEdge(domSel.focusNode, domSel.focusOffset, nearestDesc.dom))) {
|
|
@@ -2116,10 +2117,26 @@ function selectionFromDOM(view) {
|
|
|
2116
2117
|
selection = new prosemirrorState.NodeSelection(head == pos ? $head : doc.resolve(pos));
|
|
2117
2118
|
}
|
|
2118
2119
|
} else {
|
|
2119
|
-
|
|
2120
|
+
if (domSel instanceof view.dom.ownerDocument.defaultView.Selection && domSel.rangeCount > 1) {
|
|
2121
|
+
var min = head,
|
|
2122
|
+
max = head;
|
|
2123
|
+
for (var i = 0; i < domSel.rangeCount; i++) {
|
|
2124
|
+
var range = domSel.getRangeAt(i);
|
|
2125
|
+
min = Math.min(min, view.docView.posFromDOM(range.startContainer, range.startOffset, 1));
|
|
2126
|
+
max = Math.max(max, view.docView.posFromDOM(range.endContainer, range.endOffset, -1));
|
|
2127
|
+
}
|
|
2128
|
+
if (min < 0) return null;
|
|
2129
|
+
var _ref3 = max == view.state.selection.anchor ? [max, min] : [min, max];
|
|
2130
|
+
var _ref4 = _slicedToArray(_ref3, 2);
|
|
2131
|
+
anchor = _ref4[0];
|
|
2132
|
+
head = _ref4[1];
|
|
2133
|
+
$head = doc.resolve(head);
|
|
2134
|
+
} else {
|
|
2135
|
+
anchor = view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset, 1);
|
|
2136
|
+
}
|
|
2120
2137
|
if (anchor < 0) return null;
|
|
2121
|
-
$anchor = doc.resolve(anchor);
|
|
2122
2138
|
}
|
|
2139
|
+
var $anchor = doc.resolve(anchor);
|
|
2123
2140
|
if (!selection) {
|
|
2124
2141
|
var bias = origin == "pointer" || view.state.selection.head < $head.pos && !inWidget ? 1 : -1;
|
|
2125
2142
|
selection = selectionBetween(view, $anchor, $head, bias);
|
|
@@ -2910,7 +2927,7 @@ editHandlers.keydown = function (view, _event) {
|
|
|
2910
2927
|
view.input.lastKeyCode = event.keyCode;
|
|
2911
2928
|
view.input.lastKeyCodeTime = Date.now();
|
|
2912
2929
|
if (android && chrome && event.keyCode == 13) return;
|
|
2913
|
-
if (
|
|
2930
|
+
if (event.keyCode != 229) view.domObserver.forceFlush();
|
|
2914
2931
|
if (ios && event.keyCode == 13 && !event.ctrlKey && !event.altKey && !event.metaKey) {
|
|
2915
2932
|
var now = Date.now();
|
|
2916
2933
|
view.input.lastIOSEnter = now;
|
|
@@ -4275,11 +4292,6 @@ var DOMObserver = function () {
|
|
|
4275
4292
|
}
|
|
4276
4293
|
return this.queue;
|
|
4277
4294
|
}
|
|
4278
|
-
}, {
|
|
4279
|
-
key: "selectionChanged",
|
|
4280
|
-
value: function selectionChanged(sel) {
|
|
4281
|
-
return !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(this.view) && !this.ignoreSelectionChange(sel);
|
|
4282
|
-
}
|
|
4283
4295
|
}, {
|
|
4284
4296
|
key: "flush",
|
|
4285
4297
|
value: function flush() {
|
|
@@ -4287,8 +4299,8 @@ var DOMObserver = function () {
|
|
|
4287
4299
|
if (!view.docView || this.flushingSoon > -1) return;
|
|
4288
4300
|
var mutations = this.pendingRecords();
|
|
4289
4301
|
if (mutations.length) this.queue = [];
|
|
4290
|
-
var sel = view.domSelectionRange()
|
|
4291
|
-
|
|
4302
|
+
var sel = view.domSelectionRange();
|
|
4303
|
+
var newSel = !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(view) && !this.ignoreSelectionChange(sel);
|
|
4292
4304
|
var from = -1,
|
|
4293
4305
|
to = -1,
|
|
4294
4306
|
typeOver = false,
|
|
@@ -4417,11 +4429,11 @@ function rangeToSelectionRange(view, range) {
|
|
|
4417
4429
|
focusOffset = range.endOffset;
|
|
4418
4430
|
var currentAnchor = view.domAtPos(view.state.selection.anchor);
|
|
4419
4431
|
if (isEquivalentPosition(currentAnchor.node, currentAnchor.offset, focusNode, focusOffset)) {
|
|
4420
|
-
var
|
|
4421
|
-
anchorNode =
|
|
4422
|
-
anchorOffset =
|
|
4423
|
-
focusNode =
|
|
4424
|
-
focusOffset =
|
|
4432
|
+
var _ref5 = [focusNode, focusOffset, anchorNode, anchorOffset];
|
|
4433
|
+
anchorNode = _ref5[0];
|
|
4434
|
+
anchorOffset = _ref5[1];
|
|
4435
|
+
focusNode = _ref5[2];
|
|
4436
|
+
focusOffset = _ref5[3];
|
|
4425
4437
|
}
|
|
4426
4438
|
return {
|
|
4427
4439
|
anchorNode: anchorNode,
|
package/dist/index.d.cts
CHANGED
|
@@ -265,19 +265,28 @@ interface NodeView {
|
|
|
265
265
|
*/
|
|
266
266
|
contentDOM?: HTMLElement | null;
|
|
267
267
|
/**
|
|
268
|
-
When given, this will be called when the view is updating
|
|
269
|
-
It will be given a node
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
268
|
+
When given, this will be called when the view is updating
|
|
269
|
+
itself. It will be given a node, an array of active decorations
|
|
270
|
+
around the node (which are automatically drawn, and the node
|
|
271
|
+
view may ignore if it isn't interested in them), and a
|
|
272
|
+
[decoration source](https://prosemirror.net/docs/ref/#view.DecorationSource) that represents any
|
|
273
|
+
decorations that apply to the content of the node (which again
|
|
274
|
+
may be ignored). It should return true if it was able to update
|
|
275
|
+
to that node, and false otherwise. If the node view has a
|
|
276
|
+
`contentDOM` property (or no `dom` property), updating its child
|
|
277
|
+
nodes will be handled by ProseMirror.
|
|
278
278
|
*/
|
|
279
279
|
update?: (node: Node, decorations: readonly Decoration[], innerDecorations: DecorationSource) => boolean;
|
|
280
280
|
/**
|
|
281
|
+
By default, `update` will only be called when a node of the same
|
|
282
|
+
node type appears in this view's position. When you set this to
|
|
283
|
+
true, it will be called for any node, making it possible to have
|
|
284
|
+
a node view that representsmultiple types of nodes. You will
|
|
285
|
+
need to check the type of the nodes you get in `update` and
|
|
286
|
+
return `false` for types you cannot handle.
|
|
287
|
+
*/
|
|
288
|
+
multiType?: boolean;
|
|
289
|
+
/**
|
|
281
290
|
Can be used to override the way the node's selected status (as a
|
|
282
291
|
node selection) is displayed.
|
|
283
292
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -265,19 +265,28 @@ interface NodeView {
|
|
|
265
265
|
*/
|
|
266
266
|
contentDOM?: HTMLElement | null;
|
|
267
267
|
/**
|
|
268
|
-
When given, this will be called when the view is updating
|
|
269
|
-
It will be given a node
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
268
|
+
When given, this will be called when the view is updating
|
|
269
|
+
itself. It will be given a node, an array of active decorations
|
|
270
|
+
around the node (which are automatically drawn, and the node
|
|
271
|
+
view may ignore if it isn't interested in them), and a
|
|
272
|
+
[decoration source](https://prosemirror.net/docs/ref/#view.DecorationSource) that represents any
|
|
273
|
+
decorations that apply to the content of the node (which again
|
|
274
|
+
may be ignored). It should return true if it was able to update
|
|
275
|
+
to that node, and false otherwise. If the node view has a
|
|
276
|
+
`contentDOM` property (or no `dom` property), updating its child
|
|
277
|
+
nodes will be handled by ProseMirror.
|
|
278
278
|
*/
|
|
279
279
|
update?: (node: Node, decorations: readonly Decoration[], innerDecorations: DecorationSource) => boolean;
|
|
280
280
|
/**
|
|
281
|
+
By default, `update` will only be called when a node of the same
|
|
282
|
+
node type appears in this view's position. When you set this to
|
|
283
|
+
true, it will be called for any node, making it possible to have
|
|
284
|
+
a node view that representsmultiple types of nodes. You will
|
|
285
|
+
need to check the type of the nodes you get in `update` and
|
|
286
|
+
return `false` for types you cannot handle.
|
|
287
|
+
*/
|
|
288
|
+
multiType?: boolean;
|
|
289
|
+
/**
|
|
281
290
|
Can be used to override the way the node's selected status (as a
|
|
282
291
|
node selection) is displayed.
|
|
283
292
|
*/
|
package/dist/index.js
CHANGED
|
@@ -1552,7 +1552,7 @@ class CustomNodeViewDesc extends NodeViewDesc {
|
|
|
1552
1552
|
update(node, outerDeco, innerDeco, view) {
|
|
1553
1553
|
if (this.dirty == NODE_DIRTY)
|
|
1554
1554
|
return false;
|
|
1555
|
-
if (this.spec.update) {
|
|
1555
|
+
if (this.spec.update && (this.node.type == node.type || this.spec.multiType)) {
|
|
1556
1556
|
let result = this.spec.update(node, outerDeco, innerDeco);
|
|
1557
1557
|
if (result)
|
|
1558
1558
|
this.updateInner(node, outerDeco, innerDeco, view);
|
|
@@ -1870,6 +1870,7 @@ class ViewTreeUpdater {
|
|
|
1870
1870
|
return true;
|
|
1871
1871
|
}
|
|
1872
1872
|
else if (!locked && (updated = this.recreateWrapper(next, node, outerDeco, innerDeco, view, pos))) {
|
|
1873
|
+
this.destroyBetween(this.index, i);
|
|
1873
1874
|
this.top.children[this.index] = updated;
|
|
1874
1875
|
if (updated.contentDOM) {
|
|
1875
1876
|
updated.dirty = CONTENT_DIRTY;
|
|
@@ -1889,7 +1890,8 @@ class ViewTreeUpdater {
|
|
|
1889
1890
|
// identical content, move over its children.
|
|
1890
1891
|
recreateWrapper(next, node, outerDeco, innerDeco, view, pos) {
|
|
1891
1892
|
if (next.dirty || node.isAtom || !next.children.length ||
|
|
1892
|
-
!next.node.content.eq(node.content)
|
|
1893
|
+
!next.node.content.eq(node.content) ||
|
|
1894
|
+
!sameOuterDeco(outerDeco, next.outerDeco) || !innerDeco.eq(next.innerDeco))
|
|
1893
1895
|
return null;
|
|
1894
1896
|
let wrapper = NodeViewDesc.create(this.top, node, outerDeco, innerDeco, view, pos);
|
|
1895
1897
|
if (wrapper.contentDOM) {
|
|
@@ -2160,9 +2162,9 @@ function selectionFromDOM(view, origin = null) {
|
|
|
2160
2162
|
let head = view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset, 1);
|
|
2161
2163
|
if (head < 0)
|
|
2162
2164
|
return null;
|
|
2163
|
-
let $head = doc.resolve(head),
|
|
2165
|
+
let $head = doc.resolve(head), anchor, selection;
|
|
2164
2166
|
if (selectionCollapsed(domSel)) {
|
|
2165
|
-
|
|
2167
|
+
anchor = head;
|
|
2166
2168
|
while (nearestDesc && !nearestDesc.node)
|
|
2167
2169
|
nearestDesc = nearestDesc.parent;
|
|
2168
2170
|
let nearestDescNode = nearestDesc.node;
|
|
@@ -2173,11 +2175,25 @@ function selectionFromDOM(view, origin = null) {
|
|
|
2173
2175
|
}
|
|
2174
2176
|
}
|
|
2175
2177
|
else {
|
|
2176
|
-
|
|
2178
|
+
if (domSel instanceof view.dom.ownerDocument.defaultView.Selection && domSel.rangeCount > 1) {
|
|
2179
|
+
let min = head, max = head;
|
|
2180
|
+
for (let i = 0; i < domSel.rangeCount; i++) {
|
|
2181
|
+
let range = domSel.getRangeAt(i);
|
|
2182
|
+
min = Math.min(min, view.docView.posFromDOM(range.startContainer, range.startOffset, 1));
|
|
2183
|
+
max = Math.max(max, view.docView.posFromDOM(range.endContainer, range.endOffset, -1));
|
|
2184
|
+
}
|
|
2185
|
+
if (min < 0)
|
|
2186
|
+
return null;
|
|
2187
|
+
[anchor, head] = max == view.state.selection.anchor ? [max, min] : [min, max];
|
|
2188
|
+
$head = doc.resolve(head);
|
|
2189
|
+
}
|
|
2190
|
+
else {
|
|
2191
|
+
anchor = view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset, 1);
|
|
2192
|
+
}
|
|
2177
2193
|
if (anchor < 0)
|
|
2178
2194
|
return null;
|
|
2179
|
-
$anchor = doc.resolve(anchor);
|
|
2180
2195
|
}
|
|
2196
|
+
let $anchor = doc.resolve(anchor);
|
|
2181
2197
|
if (!selection) {
|
|
2182
2198
|
let bias = origin == "pointer" || (view.state.selection.head < $head.pos && !inWidget) ? 1 : -1;
|
|
2183
2199
|
selection = selectionBetween(view, $anchor, $head, bias);
|
|
@@ -3095,9 +3111,7 @@ editHandlers.keydown = (view, _event) => {
|
|
|
3095
3111
|
// and handling them eagerly tends to corrupt the input.
|
|
3096
3112
|
if (android && chrome && event.keyCode == 13)
|
|
3097
3113
|
return;
|
|
3098
|
-
if (
|
|
3099
|
-
view.domObserver.flush();
|
|
3100
|
-
else if (event.keyCode != 229)
|
|
3114
|
+
if (event.keyCode != 229)
|
|
3101
3115
|
view.domObserver.forceFlush();
|
|
3102
3116
|
// On iOS, if we preventDefault enter key presses, the virtual
|
|
3103
3117
|
// keyboard gets confused. So the hack here is to set a flag that
|
|
@@ -4620,9 +4634,6 @@ class DOMObserver {
|
|
|
4620
4634
|
this.queue.push(mut);
|
|
4621
4635
|
return this.queue;
|
|
4622
4636
|
}
|
|
4623
|
-
selectionChanged(sel) {
|
|
4624
|
-
return !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(this.view) && !this.ignoreSelectionChange(sel);
|
|
4625
|
-
}
|
|
4626
4637
|
flush() {
|
|
4627
4638
|
let { view } = this;
|
|
4628
4639
|
if (!view.docView || this.flushingSoon > -1)
|
|
@@ -4630,7 +4641,8 @@ class DOMObserver {
|
|
|
4630
4641
|
let mutations = this.pendingRecords();
|
|
4631
4642
|
if (mutations.length)
|
|
4632
4643
|
this.queue = [];
|
|
4633
|
-
let sel = view.domSelectionRange()
|
|
4644
|
+
let sel = view.domSelectionRange();
|
|
4645
|
+
let newSel = !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(view) && !this.ignoreSelectionChange(sel);
|
|
4634
4646
|
let from = -1, to = -1, typeOver = false, added = [];
|
|
4635
4647
|
if (view.editable) {
|
|
4636
4648
|
for (let i = 0; i < mutations.length; i++) {
|
package/package.json
CHANGED
package/src/domobserver.ts
CHANGED
|
@@ -163,17 +163,14 @@ export class DOMObserver {
|
|
|
163
163
|
return this.queue
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
-
selectionChanged(sel: DOMSelectionRange) {
|
|
167
|
-
return !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(this.view) && !this.ignoreSelectionChange(sel)
|
|
168
|
-
}
|
|
169
|
-
|
|
170
166
|
flush() {
|
|
171
167
|
let {view} = this
|
|
172
168
|
if (!view.docView || this.flushingSoon > -1) return
|
|
173
169
|
let mutations = this.pendingRecords()
|
|
174
170
|
if (mutations.length) this.queue = []
|
|
175
171
|
|
|
176
|
-
let sel = view.domSelectionRange()
|
|
172
|
+
let sel = view.domSelectionRange()
|
|
173
|
+
let newSel = !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(view) && !this.ignoreSelectionChange(sel)
|
|
177
174
|
|
|
178
175
|
let from = -1, to = -1, typeOver = false, added: Node[] = []
|
|
179
176
|
if (view.editable) {
|
package/src/input.ts
CHANGED
|
@@ -112,9 +112,7 @@ editHandlers.keydown = (view: EditorView, _event: Event) => {
|
|
|
112
112
|
// to be part of a confused sequence of composition events fired,
|
|
113
113
|
// and handling them eagerly tends to corrupt the input.
|
|
114
114
|
if (browser.android && browser.chrome && event.keyCode == 13) return
|
|
115
|
-
|
|
116
|
-
if (view.domObserver.selectionChanged(view.domSelectionRange())) view.domObserver.flush()
|
|
117
|
-
else if (event.keyCode != 229) view.domObserver.forceFlush()
|
|
115
|
+
if (event.keyCode != 229) view.domObserver.forceFlush()
|
|
118
116
|
|
|
119
117
|
// On iOS, if we preventDefault enter key presses, the virtual
|
|
120
118
|
// keyboard gets confused. So the hack here is to set a flag that
|
package/src/selection.ts
CHANGED
|
@@ -12,9 +12,9 @@ export function selectionFromDOM(view: EditorView, origin: string | null = null)
|
|
|
12
12
|
let nearestDesc = view.docView.nearestDesc(domSel.focusNode), inWidget = nearestDesc && nearestDesc.size == 0
|
|
13
13
|
let head = view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset, 1)
|
|
14
14
|
if (head < 0) return null
|
|
15
|
-
let $head = doc.resolve(head),
|
|
15
|
+
let $head = doc.resolve(head), anchor, selection
|
|
16
16
|
if (selectionCollapsed(domSel)) {
|
|
17
|
-
|
|
17
|
+
anchor = head
|
|
18
18
|
while (nearestDesc && !nearestDesc.node) nearestDesc = nearestDesc.parent
|
|
19
19
|
let nearestDescNode = (nearestDesc as NodeViewDesc).node
|
|
20
20
|
if (nearestDesc && nearestDescNode.isAtom && NodeSelection.isSelectable(nearestDescNode) && nearestDesc.parent
|
|
@@ -23,10 +23,22 @@ export function selectionFromDOM(view: EditorView, origin: string | null = null)
|
|
|
23
23
|
selection = new NodeSelection(head == pos ? $head : doc.resolve(pos))
|
|
24
24
|
}
|
|
25
25
|
} else {
|
|
26
|
-
|
|
26
|
+
if (domSel instanceof view.dom.ownerDocument.defaultView!.Selection && domSel.rangeCount > 1) {
|
|
27
|
+
let min = head, max = head
|
|
28
|
+
for (let i = 0; i < domSel.rangeCount; i++) {
|
|
29
|
+
let range = domSel.getRangeAt(i)
|
|
30
|
+
min = Math.min(min, view.docView.posFromDOM(range.startContainer, range.startOffset, 1))
|
|
31
|
+
max = Math.max(max, view.docView.posFromDOM(range.endContainer, range.endOffset, -1))
|
|
32
|
+
}
|
|
33
|
+
if (min < 0) return null
|
|
34
|
+
;[anchor, head] = max == view.state.selection.anchor ? [max, min] : [min, max]
|
|
35
|
+
$head = doc.resolve(head)
|
|
36
|
+
} else {
|
|
37
|
+
anchor = view.docView.posFromDOM(domSel.anchorNode!, domSel.anchorOffset, 1)
|
|
38
|
+
}
|
|
27
39
|
if (anchor < 0) return null
|
|
28
|
-
$anchor = doc.resolve(anchor)
|
|
29
40
|
}
|
|
41
|
+
let $anchor = doc.resolve(anchor)
|
|
30
42
|
|
|
31
43
|
if (!selection) {
|
|
32
44
|
let bias = origin == "pointer" || (view.state.selection.head < $head.pos && !inWidget) ? 1 : -1
|
package/src/viewdesc.ts
CHANGED
|
@@ -33,18 +33,26 @@ export interface NodeView {
|
|
|
33
33
|
/// (or deciding not to render) its child nodes.
|
|
34
34
|
contentDOM?: HTMLElement | null
|
|
35
35
|
|
|
36
|
-
/// When given, this will be called when the view is updating
|
|
37
|
-
/// It will be given a node
|
|
38
|
-
///
|
|
39
|
-
///
|
|
40
|
-
///
|
|
41
|
-
///
|
|
42
|
-
///
|
|
43
|
-
///
|
|
44
|
-
///
|
|
45
|
-
///
|
|
36
|
+
/// When given, this will be called when the view is updating
|
|
37
|
+
/// itself. It will be given a node, an array of active decorations
|
|
38
|
+
/// around the node (which are automatically drawn, and the node
|
|
39
|
+
/// view may ignore if it isn't interested in them), and a
|
|
40
|
+
/// [decoration source](#view.DecorationSource) that represents any
|
|
41
|
+
/// decorations that apply to the content of the node (which again
|
|
42
|
+
/// may be ignored). It should return true if it was able to update
|
|
43
|
+
/// to that node, and false otherwise. If the node view has a
|
|
44
|
+
/// `contentDOM` property (or no `dom` property), updating its child
|
|
45
|
+
/// nodes will be handled by ProseMirror.
|
|
46
46
|
update?: (node: Node, decorations: readonly Decoration[], innerDecorations: DecorationSource) => boolean
|
|
47
47
|
|
|
48
|
+
/// By default, `update` will only be called when a node of the same
|
|
49
|
+
/// node type appears in this view's position. When you set this to
|
|
50
|
+
/// true, it will be called for any node, making it possible to have
|
|
51
|
+
/// a node view that representsmultiple types of nodes. You will
|
|
52
|
+
/// need to check the type of the nodes you get in `update` and
|
|
53
|
+
/// return `false` for types you cannot handle.
|
|
54
|
+
multiType?: boolean
|
|
55
|
+
|
|
48
56
|
/// Can be used to override the way the node's selected status (as a
|
|
49
57
|
/// node selection) is displayed.
|
|
50
58
|
selectNode?: () => void
|
|
@@ -938,7 +946,7 @@ class CustomNodeViewDesc extends NodeViewDesc {
|
|
|
938
946
|
// updates the children.
|
|
939
947
|
update(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, view: EditorView) {
|
|
940
948
|
if (this.dirty == NODE_DIRTY) return false
|
|
941
|
-
if (this.spec.update) {
|
|
949
|
+
if (this.spec.update && (this.node.type == node.type || this.spec.multiType)) {
|
|
942
950
|
let result = this.spec.update(node, outerDeco, innerDeco)
|
|
943
951
|
if (result) this.updateInner(node, outerDeco, innerDeco, view)
|
|
944
952
|
return result
|
|
@@ -1249,6 +1257,7 @@ class ViewTreeUpdater {
|
|
|
1249
1257
|
this.index++
|
|
1250
1258
|
return true
|
|
1251
1259
|
} else if (!locked && (updated = this.recreateWrapper(next, node, outerDeco, innerDeco, view, pos))) {
|
|
1260
|
+
this.destroyBetween(this.index, i)
|
|
1252
1261
|
this.top.children[this.index] = updated
|
|
1253
1262
|
if (updated.contentDOM) {
|
|
1254
1263
|
updated.dirty = CONTENT_DIRTY
|
|
@@ -1270,7 +1279,8 @@ class ViewTreeUpdater {
|
|
|
1270
1279
|
recreateWrapper(next: NodeViewDesc, node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource,
|
|
1271
1280
|
view: EditorView, pos: number) {
|
|
1272
1281
|
if (next.dirty || node.isAtom || !next.children.length ||
|
|
1273
|
-
!next.node.content.eq(node.content)
|
|
1282
|
+
!next.node.content.eq(node.content) ||
|
|
1283
|
+
!sameOuterDeco(outerDeco, next.outerDeco) || !innerDeco.eq(next.innerDeco)) return null
|
|
1274
1284
|
let wrapper = NodeViewDesc.create(this.top, node, outerDeco, innerDeco, view, pos)
|
|
1275
1285
|
if (wrapper.contentDOM) {
|
|
1276
1286
|
wrapper.children = next.children
|