prosemirror-view 1.34.1 → 1.34.3
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 +16 -0
- package/dist/index.cjs +45 -14
- package/dist/index.js +38 -9
- package/package.json +1 -1
- package/src/clipboard.ts +10 -1
- package/src/domobserver.ts +5 -2
- package/src/input.ts +3 -1
- package/src/selection.ts +16 -4
- package/src/viewdesc.ts +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## 1.34.3 (2024-09-19)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix an issue that would, during some types of document updates, leave the document DOM in an improper form.
|
|
6
|
+
|
|
7
|
+
Handle the case where drag-selecting in Firefox creates a selection with multiple distinct ranges.
|
|
8
|
+
|
|
9
|
+
## 1.34.2 (2024-09-03)
|
|
10
|
+
|
|
11
|
+
### Bug fixes
|
|
12
|
+
|
|
13
|
+
Fix pasting in Chrome with the require-trusted-types-for CSP enabled.
|
|
14
|
+
|
|
15
|
+
Fix an issue where, for some kinds of IME on Windows, pressing Enter while in a composition would execute the resulting command on the wrong selection, erasing some typed content.
|
|
16
|
+
|
|
1
17
|
## 1.34.1 (2024-08-26)
|
|
2
18
|
|
|
3
19
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -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);
|
|
@@ -2766,6 +2783,15 @@ var _detachedDoc = null;
|
|
|
2766
2783
|
function detachedDoc() {
|
|
2767
2784
|
return _detachedDoc || (_detachedDoc = document.implementation.createHTMLDocument("title"));
|
|
2768
2785
|
}
|
|
2786
|
+
function maybeWrapTrusted(html) {
|
|
2787
|
+
var trustedTypes = window.trustedTypes;
|
|
2788
|
+
if (!trustedTypes) return html;
|
|
2789
|
+
return trustedTypes.createPolicy("detachedDocument", {
|
|
2790
|
+
createHTML: function createHTML(s) {
|
|
2791
|
+
return s;
|
|
2792
|
+
}
|
|
2793
|
+
}).createHTML(html);
|
|
2794
|
+
}
|
|
2769
2795
|
function readHTML(html) {
|
|
2770
2796
|
var metas = /^(\s*<meta [^>]*>)*/.exec(html);
|
|
2771
2797
|
if (metas) html = html.slice(metas[0].length);
|
|
@@ -2777,7 +2803,7 @@ function readHTML(html) {
|
|
|
2777
2803
|
}).join("") + html + wrap.map(function (n) {
|
|
2778
2804
|
return "</" + n + ">";
|
|
2779
2805
|
}).reverse().join("");
|
|
2780
|
-
elt.innerHTML = html;
|
|
2806
|
+
elt.innerHTML = maybeWrapTrusted(html);
|
|
2781
2807
|
if (wrap) for (var i = 0; i < wrap.length; i++) elt = elt.querySelector(wrap[i]) || elt;
|
|
2782
2808
|
return elt;
|
|
2783
2809
|
}
|
|
@@ -2901,7 +2927,7 @@ editHandlers.keydown = function (view, _event) {
|
|
|
2901
2927
|
view.input.lastKeyCode = event.keyCode;
|
|
2902
2928
|
view.input.lastKeyCodeTime = Date.now();
|
|
2903
2929
|
if (android && chrome && event.keyCode == 13) return;
|
|
2904
|
-
if (event.keyCode != 229) view.domObserver.forceFlush();
|
|
2930
|
+
if (view.domObserver.selectionChanged(view.domSelectionRange())) view.domObserver.flush();else if (event.keyCode != 229) view.domObserver.forceFlush();
|
|
2905
2931
|
if (ios && event.keyCode == 13 && !event.ctrlKey && !event.altKey && !event.metaKey) {
|
|
2906
2932
|
var now = Date.now();
|
|
2907
2933
|
view.input.lastIOSEnter = now;
|
|
@@ -4266,6 +4292,11 @@ var DOMObserver = function () {
|
|
|
4266
4292
|
}
|
|
4267
4293
|
return this.queue;
|
|
4268
4294
|
}
|
|
4295
|
+
}, {
|
|
4296
|
+
key: "selectionChanged",
|
|
4297
|
+
value: function selectionChanged(sel) {
|
|
4298
|
+
return !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(this.view) && !this.ignoreSelectionChange(sel);
|
|
4299
|
+
}
|
|
4269
4300
|
}, {
|
|
4270
4301
|
key: "flush",
|
|
4271
4302
|
value: function flush() {
|
|
@@ -4273,8 +4304,8 @@ var DOMObserver = function () {
|
|
|
4273
4304
|
if (!view.docView || this.flushingSoon > -1) return;
|
|
4274
4305
|
var mutations = this.pendingRecords();
|
|
4275
4306
|
if (mutations.length) this.queue = [];
|
|
4276
|
-
var sel = view.domSelectionRange()
|
|
4277
|
-
|
|
4307
|
+
var sel = view.domSelectionRange(),
|
|
4308
|
+
newSel = this.selectionChanged(sel);
|
|
4278
4309
|
var from = -1,
|
|
4279
4310
|
to = -1,
|
|
4280
4311
|
typeOver = false,
|
|
@@ -4403,11 +4434,11 @@ function rangeToSelectionRange(view, range) {
|
|
|
4403
4434
|
focusOffset = range.endOffset;
|
|
4404
4435
|
var currentAnchor = view.domAtPos(view.state.selection.anchor);
|
|
4405
4436
|
if (isEquivalentPosition(currentAnchor.node, currentAnchor.offset, focusNode, focusOffset)) {
|
|
4406
|
-
var
|
|
4407
|
-
anchorNode =
|
|
4408
|
-
anchorOffset =
|
|
4409
|
-
focusNode =
|
|
4410
|
-
focusOffset =
|
|
4437
|
+
var _ref5 = [focusNode, focusOffset, anchorNode, anchorOffset];
|
|
4438
|
+
anchorNode = _ref5[0];
|
|
4439
|
+
anchorOffset = _ref5[1];
|
|
4440
|
+
focusNode = _ref5[2];
|
|
4441
|
+
focusOffset = _ref5[3];
|
|
4411
4442
|
}
|
|
4412
4443
|
return {
|
|
4413
4444
|
anchorNode: anchorNode,
|
package/dist/index.js
CHANGED
|
@@ -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);
|
|
@@ -2937,6 +2953,15 @@ let _detachedDoc = null;
|
|
|
2937
2953
|
function detachedDoc() {
|
|
2938
2954
|
return _detachedDoc || (_detachedDoc = document.implementation.createHTMLDocument("title"));
|
|
2939
2955
|
}
|
|
2956
|
+
function maybeWrapTrusted(html) {
|
|
2957
|
+
let trustedTypes = window.trustedTypes;
|
|
2958
|
+
if (!trustedTypes)
|
|
2959
|
+
return html;
|
|
2960
|
+
// With the require-trusted-types-for CSP, Chrome will block
|
|
2961
|
+
// innerHTML, even on a detached document. This wraps the string in
|
|
2962
|
+
// a way that makes the browser allow us to use its parser again.
|
|
2963
|
+
return trustedTypes.createPolicy("detachedDocument", { createHTML: (s) => s }).createHTML(html);
|
|
2964
|
+
}
|
|
2940
2965
|
function readHTML(html) {
|
|
2941
2966
|
let metas = /^(\s*<meta [^>]*>)*/.exec(html);
|
|
2942
2967
|
if (metas)
|
|
@@ -2945,7 +2970,7 @@ function readHTML(html) {
|
|
|
2945
2970
|
let firstTag = /<([a-z][^>\s]+)/i.exec(html), wrap;
|
|
2946
2971
|
if (wrap = firstTag && wrapMap[firstTag[1].toLowerCase()])
|
|
2947
2972
|
html = wrap.map(n => "<" + n + ">").join("") + html + wrap.map(n => "</" + n + ">").reverse().join("");
|
|
2948
|
-
elt.innerHTML = html;
|
|
2973
|
+
elt.innerHTML = maybeWrapTrusted(html);
|
|
2949
2974
|
if (wrap)
|
|
2950
2975
|
for (let i = 0; i < wrap.length; i++)
|
|
2951
2976
|
elt = elt.querySelector(wrap[i]) || elt;
|
|
@@ -3086,7 +3111,9 @@ editHandlers.keydown = (view, _event) => {
|
|
|
3086
3111
|
// and handling them eagerly tends to corrupt the input.
|
|
3087
3112
|
if (android && chrome && event.keyCode == 13)
|
|
3088
3113
|
return;
|
|
3089
|
-
if (
|
|
3114
|
+
if (view.domObserver.selectionChanged(view.domSelectionRange()))
|
|
3115
|
+
view.domObserver.flush();
|
|
3116
|
+
else if (event.keyCode != 229)
|
|
3090
3117
|
view.domObserver.forceFlush();
|
|
3091
3118
|
// On iOS, if we preventDefault enter key presses, the virtual
|
|
3092
3119
|
// keyboard gets confused. So the hack here is to set a flag that
|
|
@@ -4609,6 +4636,9 @@ class DOMObserver {
|
|
|
4609
4636
|
this.queue.push(mut);
|
|
4610
4637
|
return this.queue;
|
|
4611
4638
|
}
|
|
4639
|
+
selectionChanged(sel) {
|
|
4640
|
+
return !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(this.view) && !this.ignoreSelectionChange(sel);
|
|
4641
|
+
}
|
|
4612
4642
|
flush() {
|
|
4613
4643
|
let { view } = this;
|
|
4614
4644
|
if (!view.docView || this.flushingSoon > -1)
|
|
@@ -4616,8 +4646,7 @@ class DOMObserver {
|
|
|
4616
4646
|
let mutations = this.pendingRecords();
|
|
4617
4647
|
if (mutations.length)
|
|
4618
4648
|
this.queue = [];
|
|
4619
|
-
let sel = view.domSelectionRange();
|
|
4620
|
-
let newSel = !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(view) && !this.ignoreSelectionChange(sel);
|
|
4649
|
+
let sel = view.domSelectionRange(), newSel = this.selectionChanged(sel);
|
|
4621
4650
|
let from = -1, to = -1, typeOver = false, added = [];
|
|
4622
4651
|
if (view.editable) {
|
|
4623
4652
|
for (let i = 0; i < mutations.length; i++) {
|
package/package.json
CHANGED
package/src/clipboard.ts
CHANGED
|
@@ -204,6 +204,15 @@ function detachedDoc() {
|
|
|
204
204
|
return _detachedDoc || (_detachedDoc = document.implementation.createHTMLDocument("title"))
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
+
function maybeWrapTrusted(html: string): string {
|
|
208
|
+
let trustedTypes = (window as any).trustedTypes
|
|
209
|
+
if (!trustedTypes) return html
|
|
210
|
+
// With the require-trusted-types-for CSP, Chrome will block
|
|
211
|
+
// innerHTML, even on a detached document. This wraps the string in
|
|
212
|
+
// a way that makes the browser allow us to use its parser again.
|
|
213
|
+
return trustedTypes.createPolicy("detachedDocument", {createHTML: (s: string) => s}).createHTML(html)
|
|
214
|
+
}
|
|
215
|
+
|
|
207
216
|
function readHTML(html: string) {
|
|
208
217
|
let metas = /^(\s*<meta [^>]*>)*/.exec(html)
|
|
209
218
|
if (metas) html = html.slice(metas[0].length)
|
|
@@ -211,7 +220,7 @@ function readHTML(html: string) {
|
|
|
211
220
|
let firstTag = /<([a-z][^>\s]+)/i.exec(html), wrap
|
|
212
221
|
if (wrap = firstTag && wrapMap[firstTag[1].toLowerCase()])
|
|
213
222
|
html = wrap.map(n => "<" + n + ">").join("") + html + wrap.map(n => "</" + n + ">").reverse().join("")
|
|
214
|
-
elt.innerHTML = html
|
|
223
|
+
elt.innerHTML = maybeWrapTrusted(html)
|
|
215
224
|
if (wrap) for (let i = 0; i < wrap.length; i++) elt = elt.querySelector(wrap[i]) || elt
|
|
216
225
|
return elt
|
|
217
226
|
}
|
package/src/domobserver.ts
CHANGED
|
@@ -163,14 +163,17 @@ 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
|
+
|
|
166
170
|
flush() {
|
|
167
171
|
let {view} = this
|
|
168
172
|
if (!view.docView || this.flushingSoon > -1) return
|
|
169
173
|
let mutations = this.pendingRecords()
|
|
170
174
|
if (mutations.length) this.queue = []
|
|
171
175
|
|
|
172
|
-
let sel = view.domSelectionRange()
|
|
173
|
-
let newSel = !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(view) && !this.ignoreSelectionChange(sel)
|
|
176
|
+
let sel = view.domSelectionRange(), newSel = this.selectionChanged(sel)
|
|
174
177
|
|
|
175
178
|
let from = -1, to = -1, typeOver = false, added: Node[] = []
|
|
176
179
|
if (view.editable) {
|
package/src/input.ts
CHANGED
|
@@ -112,7 +112,9 @@ 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
|
-
|
|
115
|
+
|
|
116
|
+
if (view.domObserver.selectionChanged(view.domSelectionRange())) view.domObserver.flush()
|
|
117
|
+
else if (event.keyCode != 229) view.domObserver.forceFlush()
|
|
116
118
|
|
|
117
119
|
// On iOS, if we preventDefault enter key presses, the virtual
|
|
118
120
|
// 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
|
@@ -1249,6 +1249,7 @@ class ViewTreeUpdater {
|
|
|
1249
1249
|
this.index++
|
|
1250
1250
|
return true
|
|
1251
1251
|
} else if (!locked && (updated = this.recreateWrapper(next, node, outerDeco, innerDeco, view, pos))) {
|
|
1252
|
+
this.destroyBetween(this.index, i)
|
|
1252
1253
|
this.top.children[this.index] = updated
|
|
1253
1254
|
if (updated.contentDOM) {
|
|
1254
1255
|
updated.dirty = CONTENT_DIRTY
|
|
@@ -1270,7 +1271,8 @@ class ViewTreeUpdater {
|
|
|
1270
1271
|
recreateWrapper(next: NodeViewDesc, node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource,
|
|
1271
1272
|
view: EditorView, pos: number) {
|
|
1272
1273
|
if (next.dirty || node.isAtom || !next.children.length ||
|
|
1273
|
-
!next.node.content.eq(node.content)
|
|
1274
|
+
!next.node.content.eq(node.content) ||
|
|
1275
|
+
!sameOuterDeco(outerDeco, next.outerDeco) || !innerDeco.eq(next.innerDeco)) return null
|
|
1274
1276
|
let wrapper = NodeViewDesc.create(this.top, node, outerDeco, innerDeco, view, pos)
|
|
1275
1277
|
if (wrapper.contentDOM) {
|
|
1276
1278
|
wrapper.children = next.children
|