prosemirror-view 1.30.1 → 1.31.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 +26 -0
- package/dist/index.cjs +157 -40
- package/dist/index.d.ts +6 -4
- package/dist/index.js +126 -37
- package/package.json +1 -1
- package/src/browser.ts +1 -0
- package/src/capturekeys.ts +36 -9
- package/src/dom.ts +13 -0
- package/src/domchange.ts +10 -2
- package/src/domcoords.ts +22 -16
- package/src/index.ts +6 -4
- package/src/input.ts +2 -0
- package/src/viewdesc.ts +31 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
## 1.31.0 (2023-04-26)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix an issue where, when the browser failed to return a cursor for a set of coordinates (as in a shadow root in Chrome) the library's own algorithm worked poorly for coordinates between lines in a textblock.
|
|
6
|
+
|
|
7
|
+
Improve performance of changing a parent node (by type or attributes) without modifying its children by reusing the DOM of the child nodes.
|
|
8
|
+
|
|
9
|
+
Fix an issue where, in right-to-left context, the left and right arrow keys could get stuck on a selectable leaf node because the editor was moving the selection in the wrong direction.
|
|
10
|
+
|
|
11
|
+
Fix a regression in `coordsAtPos` when querying the position of a line wrap.
|
|
12
|
+
|
|
13
|
+
### New features
|
|
14
|
+
|
|
15
|
+
Attach a `"composition"` meta field to transactions generated from DOM changes.
|
|
16
|
+
|
|
17
|
+
## 1.30.2 (2023-03-13)
|
|
18
|
+
|
|
19
|
+
### Bug fixes
|
|
20
|
+
|
|
21
|
+
Fix an issue where `posAtCoords` could incorrectly return positions outside of a table when querying a coordinate inside it.
|
|
22
|
+
|
|
23
|
+
Update the type of the `getPos` callback to include undefined in its return type. Update broken link (ViewProps -> EditorProps) Also detect iOS enter when the new block type is a less common node
|
|
24
|
+
|
|
25
|
+
Fix an issue where Enter on iOS would sometimes not be handled by the library.
|
|
26
|
+
|
|
1
27
|
## 1.30.1 (2023-02-04)
|
|
2
28
|
|
|
3
29
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
|
|
4
4
|
|
|
5
|
+
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
|
|
6
|
+
|
|
7
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
8
|
+
|
|
9
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
|
|
10
|
+
|
|
5
11
|
function _get() { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(arguments.length < 3 ? target : receiver); } return desc.value; }; } return _get.apply(this, arguments); }
|
|
6
12
|
|
|
7
13
|
function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }
|
|
@@ -129,6 +135,26 @@ function deepActiveElement(doc) {
|
|
|
129
135
|
return elt;
|
|
130
136
|
}
|
|
131
137
|
|
|
138
|
+
function caretFromPoint(doc, x, y) {
|
|
139
|
+
if (doc.caretPositionFromPoint) {
|
|
140
|
+
try {
|
|
141
|
+
var pos = doc.caretPositionFromPoint(x, y);
|
|
142
|
+
if (pos) return {
|
|
143
|
+
node: pos.offsetNode,
|
|
144
|
+
offset: pos.offset
|
|
145
|
+
};
|
|
146
|
+
} catch (_) {}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (doc.caretRangeFromPoint) {
|
|
150
|
+
var range = doc.caretRangeFromPoint(x, y);
|
|
151
|
+
if (range) return {
|
|
152
|
+
node: range.startContainer,
|
|
153
|
+
offset: range.startOffset
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
132
158
|
var nav = typeof navigator != "undefined" ? navigator : null;
|
|
133
159
|
var doc = typeof document != "undefined" ? document : null;
|
|
134
160
|
var agent = nav && nav.userAgent || "";
|
|
@@ -147,6 +173,7 @@ var chrome_version = _chrome ? +_chrome[1] : 0;
|
|
|
147
173
|
var safari = !ie && !!nav && /Apple Computer/.test(nav.vendor);
|
|
148
174
|
var ios = safari && (/Mobile\/\w+/.test(agent) || !!nav && nav.maxTouchPoints > 2);
|
|
149
175
|
var mac = ios || (nav ? /Mac/.test(nav.platform) : false);
|
|
176
|
+
var windows = nav ? /Win/.test(nav.platform) : false;
|
|
150
177
|
var android = /Android \d/.test(agent);
|
|
151
178
|
var webkit = !!doc && "webkitFontSmoothing" in doc.documentElement.style;
|
|
152
179
|
var webkit_version = webkit ? +(/\bAppleWebKit\/(\d+)/.exec(navigator.userAgent) || [0, 0])[1] : 0;
|
|
@@ -303,6 +330,7 @@ function findOffsetInNode(node, coords) {
|
|
|
303
330
|
offset = 0;
|
|
304
331
|
var rowBot = coords.top,
|
|
305
332
|
rowTop = coords.top;
|
|
333
|
+
var firstBelow, coordsBelow;
|
|
306
334
|
|
|
307
335
|
for (var child = node.firstChild, childIndex = 0; child; child = child.nextSibling, childIndex++) {
|
|
308
336
|
var rects = void 0;
|
|
@@ -326,12 +354,24 @@ function findOffsetInNode(node, coords) {
|
|
|
326
354
|
if (child.nodeType == 1 && dx) offset = childIndex + (coords.left >= (rect.left + rect.right) / 2 ? 1 : 0);
|
|
327
355
|
continue;
|
|
328
356
|
}
|
|
357
|
+
} else if (rect.top > coords.top && !firstBelow && rect.left <= coords.left && rect.right >= coords.left) {
|
|
358
|
+
firstBelow = child;
|
|
359
|
+
coordsBelow = {
|
|
360
|
+
left: Math.max(rect.left, Math.min(rect.right, coords.left)),
|
|
361
|
+
top: rect.top
|
|
362
|
+
};
|
|
329
363
|
}
|
|
330
364
|
|
|
331
365
|
if (!closest && (coords.left >= rect.right && coords.top >= rect.top || coords.left >= rect.left && coords.top >= rect.bottom)) offset = childIndex + 1;
|
|
332
366
|
}
|
|
333
367
|
}
|
|
334
368
|
|
|
369
|
+
if (!closest && firstBelow) {
|
|
370
|
+
closest = firstBelow;
|
|
371
|
+
coordsClosest = coordsBelow;
|
|
372
|
+
dxClosest = 0;
|
|
373
|
+
}
|
|
374
|
+
|
|
335
375
|
if (closest && closest.nodeType == 3) return findOffsetInText(closest, coordsClosest);
|
|
336
376
|
if (!closest || dxClosest && closest.nodeType == 1) return {
|
|
337
377
|
node: node,
|
|
@@ -388,15 +428,16 @@ function posFromElement(view, elt, coords) {
|
|
|
388
428
|
function posFromCaret(view, node, offset, coords) {
|
|
389
429
|
var outsideBlock = -1;
|
|
390
430
|
|
|
391
|
-
for (var cur = node;;) {
|
|
431
|
+
for (var cur = node, sawBlock = false;;) {
|
|
392
432
|
if (cur == view.dom) break;
|
|
393
433
|
var desc = view.docView.nearestDesc(cur, true);
|
|
394
434
|
if (!desc) return null;
|
|
395
435
|
|
|
396
|
-
if (desc.dom.nodeType == 1 && (desc.node.isBlock && desc.parent || !desc.contentDOM)) {
|
|
436
|
+
if (desc.dom.nodeType == 1 && (desc.node.isBlock && desc.parent && !sawBlock || !desc.contentDOM)) {
|
|
397
437
|
var rect = desc.dom.getBoundingClientRect();
|
|
398
438
|
|
|
399
|
-
if (desc.node.isBlock && desc.parent) {
|
|
439
|
+
if (desc.node.isBlock && desc.parent && !sawBlock) {
|
|
440
|
+
sawBlock = true;
|
|
400
441
|
if (rect.left > coords.left || rect.top > coords.top) outsideBlock = desc.posBefore;else if (rect.right < coords.left || rect.bottom < coords.top) outsideBlock = desc.posAfter;
|
|
401
442
|
}
|
|
402
443
|
|
|
@@ -439,25 +480,11 @@ function _posAtCoords(view, coords) {
|
|
|
439
480
|
var doc = view.dom.ownerDocument,
|
|
440
481
|
node,
|
|
441
482
|
offset = 0;
|
|
483
|
+
var caret = caretFromPoint(doc, coords.left, coords.top);
|
|
442
484
|
|
|
443
|
-
if (
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
if (_pos) {
|
|
448
|
-
node = _pos.offsetNode;
|
|
449
|
-
offset = _pos.offset;
|
|
450
|
-
}
|
|
451
|
-
} catch (_) {}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
if (!node && doc.caretRangeFromPoint) {
|
|
455
|
-
var range = doc.caretRangeFromPoint(coords.left, coords.top);
|
|
456
|
-
|
|
457
|
-
if (range) {
|
|
458
|
-
node = range.startContainer;
|
|
459
|
-
offset = range.startOffset;
|
|
460
|
-
}
|
|
485
|
+
if (caret) {
|
|
486
|
+
node = caret.node;
|
|
487
|
+
offset = caret.offset;
|
|
461
488
|
}
|
|
462
489
|
|
|
463
490
|
var elt = (view.root.elementFromPoint ? view.root : doc).elementFromPoint(coords.left, coords.top);
|
|
@@ -501,9 +528,19 @@ function _posAtCoords(view, coords) {
|
|
|
501
528
|
};
|
|
502
529
|
}
|
|
503
530
|
|
|
531
|
+
function nonZero(rect) {
|
|
532
|
+
return rect.top < rect.bottom || rect.left < rect.right;
|
|
533
|
+
}
|
|
534
|
+
|
|
504
535
|
function singleRect(target, bias) {
|
|
505
536
|
var rects = target.getClientRects();
|
|
506
|
-
|
|
537
|
+
|
|
538
|
+
if (rects.length) {
|
|
539
|
+
var first = rects[bias < 0 ? 0 : rects.length - 1];
|
|
540
|
+
if (nonZero(first)) return first;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return Array.prototype.find.call(rects, nonZero) || target.getBoundingClientRect();
|
|
507
544
|
}
|
|
508
545
|
|
|
509
546
|
var BIDI = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
|
|
@@ -547,7 +584,7 @@ function _coordsAtPos(view, pos, side) {
|
|
|
547
584
|
to++;
|
|
548
585
|
}
|
|
549
586
|
|
|
550
|
-
return flattenV(singleRect(textRange(node, from, to),
|
|
587
|
+
return flattenV(singleRect(textRange(node, from, to), takeSide), takeSide < 0);
|
|
551
588
|
}
|
|
552
589
|
}
|
|
553
590
|
|
|
@@ -1424,7 +1461,6 @@ var NodeViewDesc = function (_ViewDesc4) {
|
|
|
1424
1461
|
_this4.outerDeco = outerDeco;
|
|
1425
1462
|
_this4.innerDeco = innerDeco;
|
|
1426
1463
|
_this4.nodeDOM = nodeDOM;
|
|
1427
|
-
if (contentDOM) _this4.updateChildren(view, pos);
|
|
1428
1464
|
return _this4;
|
|
1429
1465
|
}
|
|
1430
1466
|
|
|
@@ -1495,7 +1531,7 @@ var NodeViewDesc = function (_ViewDesc4) {
|
|
|
1495
1531
|
}, function (child, outerDeco, innerDeco, i) {
|
|
1496
1532
|
updater.syncToMarks(child.marks, inline, view);
|
|
1497
1533
|
var compIndex;
|
|
1498
|
-
if (updater.findNodeMatch(child, outerDeco, innerDeco, i)) ;else if (compositionInChild && view.state.selection.from > off && view.state.selection.to < off + child.nodeSize && (compIndex = updater.findIndexWithChild(composition.node)) > -1 && updater.updateNodeAt(child, outerDeco, innerDeco, compIndex, view)) ;else if (updater.updateNextNode(child, outerDeco, innerDeco, view, i)) ;else {
|
|
1534
|
+
if (updater.findNodeMatch(child, outerDeco, innerDeco, i)) ;else if (compositionInChild && view.state.selection.from > off && view.state.selection.to < off + child.nodeSize && (compIndex = updater.findIndexWithChild(composition.node)) > -1 && updater.updateNodeAt(child, outerDeco, innerDeco, compIndex, view)) ;else if (updater.updateNextNode(child, outerDeco, innerDeco, view, i, off)) ;else {
|
|
1499
1535
|
updater.addNode(child, outerDeco, innerDeco, view, off);
|
|
1500
1536
|
}
|
|
1501
1537
|
off += child.nodeSize;
|
|
@@ -1649,7 +1685,9 @@ var NodeViewDesc = function (_ViewDesc4) {
|
|
|
1649
1685
|
|
|
1650
1686
|
function docViewDesc(doc, outerDeco, innerDeco, dom, view) {
|
|
1651
1687
|
applyOuterDeco(dom, outerDeco, doc);
|
|
1652
|
-
|
|
1688
|
+
var docView = new NodeViewDesc(undefined, doc, outerDeco, innerDeco, dom, dom, dom, view, 0);
|
|
1689
|
+
if (docView.contentDOM) docView.updateChildren(view, 0);
|
|
1690
|
+
return docView;
|
|
1653
1691
|
}
|
|
1654
1692
|
|
|
1655
1693
|
var TextViewDesc = function (_NodeViewDesc) {
|
|
@@ -2133,7 +2171,7 @@ var ViewTreeUpdater = function () {
|
|
|
2133
2171
|
}
|
|
2134
2172
|
}, {
|
|
2135
2173
|
key: "updateNextNode",
|
|
2136
|
-
value: function updateNextNode(node, outerDeco, innerDeco, view, index) {
|
|
2174
|
+
value: function updateNextNode(node, outerDeco, innerDeco, view, index, pos) {
|
|
2137
2175
|
for (var i = this.index; i < this.top.children.length; i++) {
|
|
2138
2176
|
var next = this.top.children[i];
|
|
2139
2177
|
|
|
@@ -2141,7 +2179,8 @@ var ViewTreeUpdater = function () {
|
|
|
2141
2179
|
var _preMatch = this.preMatch.matched.get(next);
|
|
2142
2180
|
|
|
2143
2181
|
if (_preMatch != null && _preMatch != index) return false;
|
|
2144
|
-
var nextDOM = next.dom
|
|
2182
|
+
var nextDOM = next.dom,
|
|
2183
|
+
updated = void 0;
|
|
2145
2184
|
var locked = this.isLocked(nextDOM) && !(node.isText && next.node && next.node.isText && next.nodeDOM.nodeValue == node.text && next.dirty != NODE_DIRTY && sameOuterDeco(outerDeco, next.outerDeco));
|
|
2146
2185
|
|
|
2147
2186
|
if (!locked && next.update(node, outerDeco, innerDeco, view)) {
|
|
@@ -2149,6 +2188,11 @@ var ViewTreeUpdater = function () {
|
|
|
2149
2188
|
if (next.dom != nextDOM) this.changed = true;
|
|
2150
2189
|
this.index++;
|
|
2151
2190
|
return true;
|
|
2191
|
+
} else if (!locked && (updated = this.recreateWrapper(next, node, outerDeco, innerDeco, view, pos))) {
|
|
2192
|
+
this.top.children[this.index] = updated;
|
|
2193
|
+
this.changed = true;
|
|
2194
|
+
this.index++;
|
|
2195
|
+
return true;
|
|
2152
2196
|
}
|
|
2153
2197
|
|
|
2154
2198
|
break;
|
|
@@ -2157,10 +2201,40 @@ var ViewTreeUpdater = function () {
|
|
|
2157
2201
|
|
|
2158
2202
|
return false;
|
|
2159
2203
|
}
|
|
2204
|
+
}, {
|
|
2205
|
+
key: "recreateWrapper",
|
|
2206
|
+
value: function recreateWrapper(next, node, outerDeco, innerDeco, view, pos) {
|
|
2207
|
+
if (next.dirty || node.isAtom || !next.children.length || !next.node.content.eq(node.content)) return null;
|
|
2208
|
+
var wrapper = NodeViewDesc.create(this.top, node, outerDeco, innerDeco, view, pos);
|
|
2209
|
+
if (!wrapper.contentDOM) return null;
|
|
2210
|
+
wrapper.children = next.children;
|
|
2211
|
+
next.children = [];
|
|
2212
|
+
|
|
2213
|
+
var _iterator = _createForOfIteratorHelper(wrapper.children),
|
|
2214
|
+
_step;
|
|
2215
|
+
|
|
2216
|
+
try {
|
|
2217
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
2218
|
+
var ch = _step.value;
|
|
2219
|
+
ch.parent = wrapper;
|
|
2220
|
+
}
|
|
2221
|
+
} catch (err) {
|
|
2222
|
+
_iterator.e(err);
|
|
2223
|
+
} finally {
|
|
2224
|
+
_iterator.f();
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
wrapper.dirty = CONTENT_DIRTY;
|
|
2228
|
+
wrapper.updateChildren(view, pos + 1);
|
|
2229
|
+
wrapper.dirty = NOT_DIRTY;
|
|
2230
|
+
return wrapper;
|
|
2231
|
+
}
|
|
2160
2232
|
}, {
|
|
2161
2233
|
key: "addNode",
|
|
2162
2234
|
value: function addNode(node, outerDeco, innerDeco, view, pos) {
|
|
2163
|
-
|
|
2235
|
+
var desc = NodeViewDesc.create(this.top, node, outerDeco, innerDeco, view, pos);
|
|
2236
|
+
if (desc.contentDOM) desc.updateChildren(view, pos + 1);
|
|
2237
|
+
this.top.children.splice(this.index++, 0, desc);
|
|
2164
2238
|
this.changed = true;
|
|
2165
2239
|
}
|
|
2166
2240
|
}, {
|
|
@@ -2664,7 +2738,7 @@ function selectHorizontally(view, dir, mods) {
|
|
|
2664
2738
|
if (sel instanceof prosemirrorState.TextSelection) {
|
|
2665
2739
|
if (!sel.empty || mods.indexOf("s") > -1) {
|
|
2666
2740
|
return false;
|
|
2667
|
-
} else if (view.endOfTextblock(dir > 0 ? "
|
|
2741
|
+
} else if (view.endOfTextblock(dir > 0 ? "forward" : "backward")) {
|
|
2668
2742
|
var next = moveSelectionBlock(view.state, dir);
|
|
2669
2743
|
if (next && next instanceof prosemirrorState.NodeSelection) return apply(view, next);
|
|
2670
2744
|
return false;
|
|
@@ -2703,7 +2777,11 @@ function isIgnorable(dom) {
|
|
|
2703
2777
|
return desc && desc.size == 0 && (dom.nextSibling || dom.nodeName != "BR");
|
|
2704
2778
|
}
|
|
2705
2779
|
|
|
2706
|
-
function
|
|
2780
|
+
function skipIgnoredNodes(view, dir) {
|
|
2781
|
+
return dir < 0 ? skipIgnoredNodesBefore(view) : skipIgnoredNodesAfter(view);
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2784
|
+
function skipIgnoredNodesBefore(view) {
|
|
2707
2785
|
var sel = view.domSelectionRange();
|
|
2708
2786
|
var node = sel.focusNode,
|
|
2709
2787
|
offset = sel.focusOffset;
|
|
@@ -2753,7 +2831,7 @@ function skipIgnoredNodesLeft(view) {
|
|
|
2753
2831
|
if (force) setSelFocus(view, node, offset);else if (moveNode) setSelFocus(view, moveNode, moveOffset);
|
|
2754
2832
|
}
|
|
2755
2833
|
|
|
2756
|
-
function
|
|
2834
|
+
function skipIgnoredNodesAfter(view) {
|
|
2757
2835
|
var sel = view.domSelectionRange();
|
|
2758
2836
|
var node = sel.focusNode,
|
|
2759
2837
|
offset = sel.focusOffset;
|
|
@@ -2821,6 +2899,31 @@ function setSelFocus(view, node, offset) {
|
|
|
2821
2899
|
}, 50);
|
|
2822
2900
|
}
|
|
2823
2901
|
|
|
2902
|
+
function findDirection(view, pos) {
|
|
2903
|
+
var $pos = view.state.doc.resolve(pos);
|
|
2904
|
+
|
|
2905
|
+
if (!(chrome || windows) && $pos.parent.inlineContent) {
|
|
2906
|
+
var coords = view.coordsAtPos(pos);
|
|
2907
|
+
|
|
2908
|
+
if (pos > $pos.start()) {
|
|
2909
|
+
var before = view.coordsAtPos(pos - 1);
|
|
2910
|
+
var mid = (before.top + before.bottom) / 2;
|
|
2911
|
+
if (mid > coords.top && mid < coords.bottom && Math.abs(before.left - coords.left) > 1) return before.left < coords.left ? "ltr" : "rtl";
|
|
2912
|
+
}
|
|
2913
|
+
|
|
2914
|
+
if (pos < $pos.end()) {
|
|
2915
|
+
var after = view.coordsAtPos(pos + 1);
|
|
2916
|
+
|
|
2917
|
+
var _mid = (after.top + after.bottom) / 2;
|
|
2918
|
+
|
|
2919
|
+
if (_mid > coords.top && _mid < coords.bottom && Math.abs(after.left - coords.left) > 1) return after.left > coords.left ? "ltr" : "rtl";
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
|
|
2923
|
+
var computed = getComputedStyle(view.dom).direction;
|
|
2924
|
+
return computed == "rtl" ? "rtl" : "ltr";
|
|
2925
|
+
}
|
|
2926
|
+
|
|
2824
2927
|
function selectVertically(view, dir, mods) {
|
|
2825
2928
|
var sel = view.state.selection;
|
|
2826
2929
|
if (sel instanceof prosemirrorState.TextSelection && !sel.empty || mods.indexOf("s") > -1) return false;
|
|
@@ -2901,19 +3004,22 @@ function captureKeyDown(view, event) {
|
|
|
2901
3004
|
mods = getMods(event);
|
|
2902
3005
|
|
|
2903
3006
|
if (code == 8 || mac && code == 72 && mods == "c") {
|
|
2904
|
-
return stopNativeHorizontalDelete(view, -1) ||
|
|
3007
|
+
return stopNativeHorizontalDelete(view, -1) || skipIgnoredNodes(view, -1);
|
|
2905
3008
|
} else if (code == 46 || mac && code == 68 && mods == "c") {
|
|
2906
|
-
return stopNativeHorizontalDelete(view, 1) ||
|
|
3009
|
+
return stopNativeHorizontalDelete(view, 1) || skipIgnoredNodes(view, 1);
|
|
2907
3010
|
} else if (code == 13 || code == 27) {
|
|
2908
3011
|
return true;
|
|
2909
3012
|
} else if (code == 37 || mac && code == 66 && mods == "c") {
|
|
2910
|
-
|
|
3013
|
+
var dir = code == 37 ? findDirection(view, view.state.selection.from) == "ltr" ? -1 : 1 : -1;
|
|
3014
|
+
return selectHorizontally(view, dir, mods) || skipIgnoredNodes(view, dir);
|
|
2911
3015
|
} else if (code == 39 || mac && code == 70 && mods == "c") {
|
|
2912
|
-
|
|
3016
|
+
var _dir = code == 39 ? findDirection(view, view.state.selection.from) == "ltr" ? 1 : -1 : 1;
|
|
3017
|
+
|
|
3018
|
+
return selectHorizontally(view, _dir, mods) || skipIgnoredNodes(view, _dir);
|
|
2913
3019
|
} else if (code == 38 || mac && code == 80 && mods == "c") {
|
|
2914
|
-
return selectVertically(view, -1, mods) ||
|
|
3020
|
+
return selectVertically(view, -1, mods) || skipIgnoredNodes(view, -1);
|
|
2915
3021
|
} else if (code == 40 || mac && code == 78 && mods == "c") {
|
|
2916
|
-
return safariDownArrowBug(view) || selectVertically(view, 1, mods) ||
|
|
3022
|
+
return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodesAfter(view);
|
|
2917
3023
|
} else if (mods == (mac ? "m" : "c") && (code == 66 || code == 73 || code == 89 || code == 90)) {
|
|
2918
3024
|
return true;
|
|
2919
3025
|
}
|
|
@@ -3240,6 +3346,7 @@ var InputState = _createClass(function InputState() {
|
|
|
3240
3346
|
this.composingTimeout = -1;
|
|
3241
3347
|
this.compositionNodes = [];
|
|
3242
3348
|
this.compositionEndedAt = -2e8;
|
|
3349
|
+
this.compositionID = 1;
|
|
3243
3350
|
this.domChangeCount = 0;
|
|
3244
3351
|
this.eventHandlers = Object.create(null);
|
|
3245
3352
|
this.hideSelectionGuard = null;
|
|
@@ -3703,6 +3810,7 @@ editHandlers.compositionend = function (view, event) {
|
|
|
3703
3810
|
if (view.composing) {
|
|
3704
3811
|
view.input.composing = false;
|
|
3705
3812
|
view.input.compositionEndedAt = event.timeStamp;
|
|
3813
|
+
view.input.compositionID++;
|
|
3706
3814
|
scheduleComposeEnd(view, 20);
|
|
3707
3815
|
}
|
|
3708
3816
|
};
|
|
@@ -5169,6 +5277,8 @@ function ruleFromNode(dom) {
|
|
|
5169
5277
|
return null;
|
|
5170
5278
|
}
|
|
5171
5279
|
|
|
5280
|
+
var isInline = /^(a|abbr|acronym|b|bd[io]|big|br|button|cite|code|data(list)?|del|dfn|em|i|ins|kbd|label|map|mark|meter|output|q|ruby|s|samp|small|span|strong|su[bp]|time|u|tt|var)$/i;
|
|
5281
|
+
|
|
5172
5282
|
function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
5173
5283
|
if (from < 0) {
|
|
5174
5284
|
var origin = view.input.lastSelectionTime > Date.now() - 50 ? view.input.lastSelectionOrigin : null;
|
|
@@ -5182,6 +5292,7 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
5182
5292
|
var _tr = view.state.tr.setSelection(newSel);
|
|
5183
5293
|
|
|
5184
5294
|
if (origin == "pointer") _tr.setMeta("pointer", true);else if (origin == "key") _tr.scrollIntoView();
|
|
5295
|
+
if (view.composing) _tr.setMeta("composition", view.input.compositionID);
|
|
5185
5296
|
view.dispatch(_tr);
|
|
5186
5297
|
}
|
|
5187
5298
|
|
|
@@ -5210,7 +5321,7 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
5210
5321
|
var change = findDiff(compare.content, parse.doc.content, parse.from, preferredPos, preferredSide);
|
|
5211
5322
|
|
|
5212
5323
|
if ((ios && view.input.lastIOSEnter > Date.now() - 225 || android) && addedNodes.some(function (n) {
|
|
5213
|
-
return n.
|
|
5324
|
+
return n.nodeType == 1 && !isInline.test(n.nodeName);
|
|
5214
5325
|
}) && (!change || change.endA >= change.endB) && view.someProp("handleKeyDown", function (f) {
|
|
5215
5326
|
return f(view, keyEvent(13, "Enter"));
|
|
5216
5327
|
})) {
|
|
@@ -5229,7 +5340,12 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
5229
5340
|
if (parse.sel) {
|
|
5230
5341
|
var _sel = resolveSelection(view, view.state.doc, parse.sel);
|
|
5231
5342
|
|
|
5232
|
-
if (_sel && !_sel.eq(view.state.selection))
|
|
5343
|
+
if (_sel && !_sel.eq(view.state.selection)) {
|
|
5344
|
+
var _tr2 = view.state.tr.setSelection(_sel);
|
|
5345
|
+
|
|
5346
|
+
if (view.composing) _tr2.setMeta("composition", view.input.compositionID);
|
|
5347
|
+
view.dispatch(_tr2);
|
|
5348
|
+
}
|
|
5233
5349
|
}
|
|
5234
5350
|
|
|
5235
5351
|
return;
|
|
@@ -5331,6 +5447,7 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
5331
5447
|
}
|
|
5332
5448
|
|
|
5333
5449
|
if (storedMarks) tr.ensureMarks(storedMarks);
|
|
5450
|
+
if (view.composing) tr.setMeta("composition", view.input.compositionID);
|
|
5334
5451
|
view.dispatch(tr.scrollIntoView());
|
|
5335
5452
|
}
|
|
5336
5453
|
|
package/dist/index.d.ts
CHANGED
|
@@ -601,12 +601,12 @@ declare class EditorView {
|
|
|
601
601
|
dispatch(tr: Transaction): void;
|
|
602
602
|
}
|
|
603
603
|
/**
|
|
604
|
-
The type of function [provided](https://prosemirror.net/docs/ref/#view.
|
|
604
|
+
The type of function [provided](https://prosemirror.net/docs/ref/#view.EditorProps.nodeViews) to
|
|
605
605
|
create [node views](https://prosemirror.net/docs/ref/#view.NodeView).
|
|
606
606
|
*/
|
|
607
|
-
declare type NodeViewConstructor = (node: Node, view: EditorView, getPos: () => number, decorations: readonly Decoration[], innerDecorations: DecorationSource) => NodeView;
|
|
607
|
+
declare type NodeViewConstructor = (node: Node, view: EditorView, getPos: () => number | undefined, decorations: readonly Decoration[], innerDecorations: DecorationSource) => NodeView;
|
|
608
608
|
/**
|
|
609
|
-
The function types [used](https://prosemirror.net/docs/ref/#view.
|
|
609
|
+
The function types [used](https://prosemirror.net/docs/ref/#view.EditorProps.markViews) to create
|
|
610
610
|
mark views.
|
|
611
611
|
*/
|
|
612
612
|
declare type MarkViewConstructor = (mark: Mark, view: EditorView, inline: boolean) => {
|
|
@@ -767,6 +767,8 @@ interface EditorProps<P = any> {
|
|
|
767
767
|
node's display behavior. The third argument `getPos` is a
|
|
768
768
|
function that can be called to get the node's current position,
|
|
769
769
|
which can be useful when creating transactions to update it.
|
|
770
|
+
Note that if the node is not in the document, the position
|
|
771
|
+
returned by this function will be `undefined`.
|
|
770
772
|
|
|
771
773
|
`decorations` is an array of node or inline decorations that are
|
|
772
774
|
active around the node. They are automatically drawn in the
|
|
@@ -782,7 +784,7 @@ interface EditorProps<P = any> {
|
|
|
782
784
|
inner decorations.
|
|
783
785
|
|
|
784
786
|
(For backwards compatibility reasons, [mark
|
|
785
|
-
views](https://prosemirror.net/docs/ref/#view.
|
|
787
|
+
views](https://prosemirror.net/docs/ref/#view.EditorProps.markViews) can also be included in this
|
|
786
788
|
object.)
|
|
787
789
|
*/
|
|
788
790
|
nodeViews?: {
|
package/dist/index.js
CHANGED
|
@@ -94,6 +94,21 @@ function deepActiveElement(doc) {
|
|
|
94
94
|
elt = elt.shadowRoot.activeElement;
|
|
95
95
|
return elt;
|
|
96
96
|
}
|
|
97
|
+
function caretFromPoint(doc, x, y) {
|
|
98
|
+
if (doc.caretPositionFromPoint) {
|
|
99
|
+
try { // Firefox throws for this call in hard-to-predict circumstances (#994)
|
|
100
|
+
let pos = doc.caretPositionFromPoint(x, y);
|
|
101
|
+
if (pos)
|
|
102
|
+
return { node: pos.offsetNode, offset: pos.offset };
|
|
103
|
+
}
|
|
104
|
+
catch (_) { }
|
|
105
|
+
}
|
|
106
|
+
if (doc.caretRangeFromPoint) {
|
|
107
|
+
let range = doc.caretRangeFromPoint(x, y);
|
|
108
|
+
if (range)
|
|
109
|
+
return { node: range.startContainer, offset: range.startOffset };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
97
112
|
|
|
98
113
|
const nav = typeof navigator != "undefined" ? navigator : null;
|
|
99
114
|
const doc = typeof document != "undefined" ? document : null;
|
|
@@ -112,6 +127,7 @@ const safari = !ie && !!nav && /Apple Computer/.test(nav.vendor);
|
|
|
112
127
|
// Is true for both iOS and iPadOS for convenience
|
|
113
128
|
const ios = safari && (/Mobile\/\w+/.test(agent) || !!nav && nav.maxTouchPoints > 2);
|
|
114
129
|
const mac = ios || (nav ? /Mac/.test(nav.platform) : false);
|
|
130
|
+
const windows = nav ? /Win/.test(nav.platform) : false;
|
|
115
131
|
const android = /Android \d/.test(agent);
|
|
116
132
|
const webkit = !!doc && "webkitFontSmoothing" in doc.documentElement.style;
|
|
117
133
|
const webkit_version = webkit ? +(/\bAppleWebKit\/(\d+)/.exec(navigator.userAgent) || [0, 0])[1] : 0;
|
|
@@ -237,6 +253,7 @@ function focusPreventScroll(dom) {
|
|
|
237
253
|
function findOffsetInNode(node, coords) {
|
|
238
254
|
let closest, dxClosest = 2e8, coordsClosest, offset = 0;
|
|
239
255
|
let rowBot = coords.top, rowTop = coords.top;
|
|
256
|
+
let firstBelow, coordsBelow;
|
|
240
257
|
for (let child = node.firstChild, childIndex = 0; child; child = child.nextSibling, childIndex++) {
|
|
241
258
|
let rects;
|
|
242
259
|
if (child.nodeType == 1)
|
|
@@ -264,11 +281,20 @@ function findOffsetInNode(node, coords) {
|
|
|
264
281
|
continue;
|
|
265
282
|
}
|
|
266
283
|
}
|
|
284
|
+
else if (rect.top > coords.top && !firstBelow && rect.left <= coords.left && rect.right >= coords.left) {
|
|
285
|
+
firstBelow = child;
|
|
286
|
+
coordsBelow = { left: Math.max(rect.left, Math.min(rect.right, coords.left)), top: rect.top };
|
|
287
|
+
}
|
|
267
288
|
if (!closest && (coords.left >= rect.right && coords.top >= rect.top ||
|
|
268
289
|
coords.left >= rect.left && coords.top >= rect.bottom))
|
|
269
290
|
offset = childIndex + 1;
|
|
270
291
|
}
|
|
271
292
|
}
|
|
293
|
+
if (!closest && firstBelow) {
|
|
294
|
+
closest = firstBelow;
|
|
295
|
+
coordsClosest = coordsBelow;
|
|
296
|
+
dxClosest = 0;
|
|
297
|
+
}
|
|
272
298
|
if (closest && closest.nodeType == 3)
|
|
273
299
|
return findOffsetInText(closest, coordsClosest);
|
|
274
300
|
if (!closest || (dxClosest && closest.nodeType == 1))
|
|
@@ -315,15 +341,16 @@ function posFromCaret(view, node, offset, coords) {
|
|
|
315
341
|
// fall outside of. If so, we take the position before/after that
|
|
316
342
|
// block. If not, we call `posFromDOM` on the raw node/offset.
|
|
317
343
|
let outsideBlock = -1;
|
|
318
|
-
for (let cur = node;;) {
|
|
344
|
+
for (let cur = node, sawBlock = false;;) {
|
|
319
345
|
if (cur == view.dom)
|
|
320
346
|
break;
|
|
321
347
|
let desc = view.docView.nearestDesc(cur, true);
|
|
322
348
|
if (!desc)
|
|
323
349
|
return null;
|
|
324
|
-
if (desc.dom.nodeType == 1 && (desc.node.isBlock && desc.parent || !desc.contentDOM)) {
|
|
350
|
+
if (desc.dom.nodeType == 1 && (desc.node.isBlock && desc.parent && !sawBlock || !desc.contentDOM)) {
|
|
325
351
|
let rect = desc.dom.getBoundingClientRect();
|
|
326
|
-
if (desc.node.isBlock && desc.parent) {
|
|
352
|
+
if (desc.node.isBlock && desc.parent && !sawBlock) {
|
|
353
|
+
sawBlock = true;
|
|
327
354
|
if (rect.left > coords.left || rect.top > coords.top)
|
|
328
355
|
outsideBlock = desc.posBefore;
|
|
329
356
|
else if (rect.right < coords.left || rect.bottom < coords.top)
|
|
@@ -362,19 +389,9 @@ function elementFromPoint(element, coords, box) {
|
|
|
362
389
|
// Given an x,y position on the editor, get the position in the document.
|
|
363
390
|
function posAtCoords(view, coords) {
|
|
364
391
|
let doc = view.dom.ownerDocument, node, offset = 0;
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
if (pos)
|
|
369
|
-
({ offsetNode: node, offset } = pos);
|
|
370
|
-
}
|
|
371
|
-
catch (_) { }
|
|
372
|
-
}
|
|
373
|
-
if (!node && doc.caretRangeFromPoint) {
|
|
374
|
-
let range = doc.caretRangeFromPoint(coords.left, coords.top);
|
|
375
|
-
if (range)
|
|
376
|
-
({ startContainer: node, startOffset: offset } = range);
|
|
377
|
-
}
|
|
392
|
+
let caret = caretFromPoint(doc, coords.left, coords.top);
|
|
393
|
+
if (caret)
|
|
394
|
+
({ node, offset } = caret);
|
|
378
395
|
let elt = (view.root.elementFromPoint ? view.root : doc)
|
|
379
396
|
.elementFromPoint(coords.left, coords.top);
|
|
380
397
|
let pos;
|
|
@@ -423,9 +440,17 @@ function posAtCoords(view, coords) {
|
|
|
423
440
|
let desc = view.docView.nearestDesc(elt, true);
|
|
424
441
|
return { pos, inside: desc ? desc.posAtStart - desc.border : -1 };
|
|
425
442
|
}
|
|
443
|
+
function nonZero(rect) {
|
|
444
|
+
return rect.top < rect.bottom || rect.left < rect.right;
|
|
445
|
+
}
|
|
426
446
|
function singleRect(target, bias) {
|
|
427
447
|
let rects = target.getClientRects();
|
|
428
|
-
|
|
448
|
+
if (rects.length) {
|
|
449
|
+
let first = rects[bias < 0 ? 0 : rects.length - 1];
|
|
450
|
+
if (nonZero(first))
|
|
451
|
+
return first;
|
|
452
|
+
}
|
|
453
|
+
return Array.prototype.find.call(rects, nonZero) || target.getBoundingClientRect();
|
|
429
454
|
}
|
|
430
455
|
const BIDI = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
|
|
431
456
|
// Given a position in the document model, get a bounding box of the
|
|
@@ -467,7 +492,7 @@ function coordsAtPos(view, pos, side) {
|
|
|
467
492
|
else {
|
|
468
493
|
to++;
|
|
469
494
|
}
|
|
470
|
-
return flattenV(singleRect(textRange(node, from, to),
|
|
495
|
+
return flattenV(singleRect(textRange(node, from, to), takeSide), takeSide < 0);
|
|
471
496
|
}
|
|
472
497
|
}
|
|
473
498
|
let $dom = view.state.doc.resolve(pos - (atom || 0));
|
|
@@ -1158,8 +1183,6 @@ class NodeViewDesc extends ViewDesc {
|
|
|
1158
1183
|
this.outerDeco = outerDeco;
|
|
1159
1184
|
this.innerDeco = innerDeco;
|
|
1160
1185
|
this.nodeDOM = nodeDOM;
|
|
1161
|
-
if (contentDOM)
|
|
1162
|
-
this.updateChildren(view, pos);
|
|
1163
1186
|
}
|
|
1164
1187
|
// By default, a node is rendered using the `toDOM` method from the
|
|
1165
1188
|
// node type spec. But client code can use the `nodeViews` spec to
|
|
@@ -1272,7 +1295,7 @@ class NodeViewDesc extends ViewDesc {
|
|
|
1272
1295
|
view.state.selection.to < off + child.nodeSize &&
|
|
1273
1296
|
(compIndex = updater.findIndexWithChild(composition.node)) > -1 &&
|
|
1274
1297
|
updater.updateNodeAt(child, outerDeco, innerDeco, compIndex, view)) ;
|
|
1275
|
-
else if (updater.updateNextNode(child, outerDeco, innerDeco, view, i)) ;
|
|
1298
|
+
else if (updater.updateNextNode(child, outerDeco, innerDeco, view, i, off)) ;
|
|
1276
1299
|
else {
|
|
1277
1300
|
// Add it as a new view
|
|
1278
1301
|
updater.addNode(child, outerDeco, innerDeco, view, off);
|
|
@@ -1386,7 +1409,10 @@ class NodeViewDesc extends ViewDesc {
|
|
|
1386
1409
|
// and used by the view class.
|
|
1387
1410
|
function docViewDesc(doc, outerDeco, innerDeco, dom, view) {
|
|
1388
1411
|
applyOuterDeco(dom, outerDeco, doc);
|
|
1389
|
-
|
|
1412
|
+
let docView = new NodeViewDesc(undefined, doc, outerDeco, innerDeco, dom, dom, dom, view, 0);
|
|
1413
|
+
if (docView.contentDOM)
|
|
1414
|
+
docView.updateChildren(view, 0);
|
|
1415
|
+
return docView;
|
|
1390
1416
|
}
|
|
1391
1417
|
class TextViewDesc extends NodeViewDesc {
|
|
1392
1418
|
constructor(parent, node, outerDeco, innerDeco, dom, nodeDOM, view) {
|
|
@@ -1759,14 +1785,14 @@ class ViewTreeUpdater {
|
|
|
1759
1785
|
}
|
|
1760
1786
|
// Try to update the next node, if any, to the given data. Checks
|
|
1761
1787
|
// pre-matches to avoid overwriting nodes that could still be used.
|
|
1762
|
-
updateNextNode(node, outerDeco, innerDeco, view, index) {
|
|
1788
|
+
updateNextNode(node, outerDeco, innerDeco, view, index, pos) {
|
|
1763
1789
|
for (let i = this.index; i < this.top.children.length; i++) {
|
|
1764
1790
|
let next = this.top.children[i];
|
|
1765
1791
|
if (next instanceof NodeViewDesc) {
|
|
1766
1792
|
let preMatch = this.preMatch.matched.get(next);
|
|
1767
1793
|
if (preMatch != null && preMatch != index)
|
|
1768
1794
|
return false;
|
|
1769
|
-
let nextDOM = next.dom;
|
|
1795
|
+
let nextDOM = next.dom, updated;
|
|
1770
1796
|
// Can't update if nextDOM is or contains this.lock, except if
|
|
1771
1797
|
// it's a text node whose content already matches the new text
|
|
1772
1798
|
// and whose decorations match the new ones.
|
|
@@ -1780,14 +1806,41 @@ class ViewTreeUpdater {
|
|
|
1780
1806
|
this.index++;
|
|
1781
1807
|
return true;
|
|
1782
1808
|
}
|
|
1809
|
+
else if (!locked && (updated = this.recreateWrapper(next, node, outerDeco, innerDeco, view, pos))) {
|
|
1810
|
+
this.top.children[this.index] = updated;
|
|
1811
|
+
this.changed = true;
|
|
1812
|
+
this.index++;
|
|
1813
|
+
return true;
|
|
1814
|
+
}
|
|
1783
1815
|
break;
|
|
1784
1816
|
}
|
|
1785
1817
|
}
|
|
1786
1818
|
return false;
|
|
1787
1819
|
}
|
|
1820
|
+
// When a node with content is replaced by a different node with
|
|
1821
|
+
// identical content, move over its children.
|
|
1822
|
+
recreateWrapper(next, node, outerDeco, innerDeco, view, pos) {
|
|
1823
|
+
if (next.dirty || node.isAtom || !next.children.length ||
|
|
1824
|
+
!next.node.content.eq(node.content))
|
|
1825
|
+
return null;
|
|
1826
|
+
let wrapper = NodeViewDesc.create(this.top, node, outerDeco, innerDeco, view, pos);
|
|
1827
|
+
if (!wrapper.contentDOM)
|
|
1828
|
+
return null;
|
|
1829
|
+
wrapper.children = next.children;
|
|
1830
|
+
next.children = [];
|
|
1831
|
+
for (let ch of wrapper.children)
|
|
1832
|
+
ch.parent = wrapper;
|
|
1833
|
+
wrapper.dirty = CONTENT_DIRTY;
|
|
1834
|
+
wrapper.updateChildren(view, pos + 1);
|
|
1835
|
+
wrapper.dirty = NOT_DIRTY;
|
|
1836
|
+
return wrapper;
|
|
1837
|
+
}
|
|
1788
1838
|
// Insert the node as a newly created node desc.
|
|
1789
1839
|
addNode(node, outerDeco, innerDeco, view, pos) {
|
|
1790
|
-
|
|
1840
|
+
let desc = NodeViewDesc.create(this.top, node, outerDeco, innerDeco, view, pos);
|
|
1841
|
+
if (desc.contentDOM)
|
|
1842
|
+
desc.updateChildren(view, pos + 1);
|
|
1843
|
+
this.top.children.splice(this.index++, 0, desc);
|
|
1791
1844
|
this.changed = true;
|
|
1792
1845
|
}
|
|
1793
1846
|
placeWidget(widget, view, pos) {
|
|
@@ -2259,7 +2312,7 @@ function selectHorizontally(view, dir, mods) {
|
|
|
2259
2312
|
if (!sel.empty || mods.indexOf("s") > -1) {
|
|
2260
2313
|
return false;
|
|
2261
2314
|
}
|
|
2262
|
-
else if (view.endOfTextblock(dir > 0 ? "
|
|
2315
|
+
else if (view.endOfTextblock(dir > 0 ? "forward" : "backward")) {
|
|
2263
2316
|
let next = moveSelectionBlock(view.state, dir);
|
|
2264
2317
|
if (next && (next instanceof NodeSelection))
|
|
2265
2318
|
return apply(view, next);
|
|
@@ -2303,9 +2356,12 @@ function isIgnorable(dom) {
|
|
|
2303
2356
|
let desc = dom.pmViewDesc;
|
|
2304
2357
|
return desc && desc.size == 0 && (dom.nextSibling || dom.nodeName != "BR");
|
|
2305
2358
|
}
|
|
2359
|
+
function skipIgnoredNodes(view, dir) {
|
|
2360
|
+
return dir < 0 ? skipIgnoredNodesBefore(view) : skipIgnoredNodesAfter(view);
|
|
2361
|
+
}
|
|
2306
2362
|
// Make sure the cursor isn't directly after one or more ignored
|
|
2307
2363
|
// nodes, which will confuse the browser's cursor motion logic.
|
|
2308
|
-
function
|
|
2364
|
+
function skipIgnoredNodesBefore(view) {
|
|
2309
2365
|
let sel = view.domSelectionRange();
|
|
2310
2366
|
let node = sel.focusNode, offset = sel.focusOffset;
|
|
2311
2367
|
if (!node)
|
|
@@ -2364,7 +2420,7 @@ function skipIgnoredNodesLeft(view) {
|
|
|
2364
2420
|
}
|
|
2365
2421
|
// Make sure the cursor isn't directly before one or more ignored
|
|
2366
2422
|
// nodes.
|
|
2367
|
-
function
|
|
2423
|
+
function skipIgnoredNodesAfter(view) {
|
|
2368
2424
|
let sel = view.domSelectionRange();
|
|
2369
2425
|
let node = sel.focusNode, offset = sel.focusOffset;
|
|
2370
2426
|
if (!node)
|
|
@@ -2433,6 +2489,26 @@ function setSelFocus(view, node, offset) {
|
|
|
2433
2489
|
selectionToDOM(view);
|
|
2434
2490
|
}, 50);
|
|
2435
2491
|
}
|
|
2492
|
+
function findDirection(view, pos) {
|
|
2493
|
+
let $pos = view.state.doc.resolve(pos);
|
|
2494
|
+
if (!(chrome || windows) && $pos.parent.inlineContent) {
|
|
2495
|
+
let coords = view.coordsAtPos(pos);
|
|
2496
|
+
if (pos > $pos.start()) {
|
|
2497
|
+
let before = view.coordsAtPos(pos - 1);
|
|
2498
|
+
let mid = (before.top + before.bottom) / 2;
|
|
2499
|
+
if (mid > coords.top && mid < coords.bottom && Math.abs(before.left - coords.left) > 1)
|
|
2500
|
+
return before.left < coords.left ? "ltr" : "rtl";
|
|
2501
|
+
}
|
|
2502
|
+
if (pos < $pos.end()) {
|
|
2503
|
+
let after = view.coordsAtPos(pos + 1);
|
|
2504
|
+
let mid = (after.top + after.bottom) / 2;
|
|
2505
|
+
if (mid > coords.top && mid < coords.bottom && Math.abs(after.left - coords.left) > 1)
|
|
2506
|
+
return after.left > coords.left ? "ltr" : "rtl";
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
let computed = getComputedStyle(view.dom).direction;
|
|
2510
|
+
return computed == "rtl" ? "rtl" : "ltr";
|
|
2511
|
+
}
|
|
2436
2512
|
// Check whether vertical selection motion would involve node
|
|
2437
2513
|
// selections. If so, apply it (if not, the result is left to the
|
|
2438
2514
|
// browser)
|
|
@@ -2520,25 +2596,27 @@ function getMods(event) {
|
|
|
2520
2596
|
function captureKeyDown(view, event) {
|
|
2521
2597
|
let code = event.keyCode, mods = getMods(event);
|
|
2522
2598
|
if (code == 8 || (mac && code == 72 && mods == "c")) { // Backspace, Ctrl-h on Mac
|
|
2523
|
-
return stopNativeHorizontalDelete(view, -1) ||
|
|
2599
|
+
return stopNativeHorizontalDelete(view, -1) || skipIgnoredNodes(view, -1);
|
|
2524
2600
|
}
|
|
2525
2601
|
else if (code == 46 || (mac && code == 68 && mods == "c")) { // Delete, Ctrl-d on Mac
|
|
2526
|
-
return stopNativeHorizontalDelete(view, 1) ||
|
|
2602
|
+
return stopNativeHorizontalDelete(view, 1) || skipIgnoredNodes(view, 1);
|
|
2527
2603
|
}
|
|
2528
2604
|
else if (code == 13 || code == 27) { // Enter, Esc
|
|
2529
2605
|
return true;
|
|
2530
2606
|
}
|
|
2531
2607
|
else if (code == 37 || (mac && code == 66 && mods == "c")) { // Left arrow, Ctrl-b on Mac
|
|
2532
|
-
|
|
2608
|
+
let dir = code == 37 ? (findDirection(view, view.state.selection.from) == "ltr" ? -1 : 1) : -1;
|
|
2609
|
+
return selectHorizontally(view, dir, mods) || skipIgnoredNodes(view, dir);
|
|
2533
2610
|
}
|
|
2534
2611
|
else if (code == 39 || (mac && code == 70 && mods == "c")) { // Right arrow, Ctrl-f on Mac
|
|
2535
|
-
|
|
2612
|
+
let dir = code == 39 ? (findDirection(view, view.state.selection.from) == "ltr" ? 1 : -1) : 1;
|
|
2613
|
+
return selectHorizontally(view, dir, mods) || skipIgnoredNodes(view, dir);
|
|
2536
2614
|
}
|
|
2537
2615
|
else if (code == 38 || (mac && code == 80 && mods == "c")) { // Up arrow, Ctrl-p on Mac
|
|
2538
|
-
return selectVertically(view, -1, mods) ||
|
|
2616
|
+
return selectVertically(view, -1, mods) || skipIgnoredNodes(view, -1);
|
|
2539
2617
|
}
|
|
2540
2618
|
else if (code == 40 || (mac && code == 78 && mods == "c")) { // Down arrow, Ctrl-n on Mac
|
|
2541
|
-
return safariDownArrowBug(view) || selectVertically(view, 1, mods) ||
|
|
2619
|
+
return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodesAfter(view);
|
|
2542
2620
|
}
|
|
2543
2621
|
else if (mods == (mac ? "m" : "c") &&
|
|
2544
2622
|
(code == 66 || code == 73 || code == 89 || code == 90)) { // Mod-[biyz]
|
|
@@ -2817,6 +2895,7 @@ class InputState {
|
|
|
2817
2895
|
this.composingTimeout = -1;
|
|
2818
2896
|
this.compositionNodes = [];
|
|
2819
2897
|
this.compositionEndedAt = -2e8;
|
|
2898
|
+
this.compositionID = 1;
|
|
2820
2899
|
this.domChangeCount = 0;
|
|
2821
2900
|
this.eventHandlers = Object.create(null);
|
|
2822
2901
|
this.hideSelectionGuard = null;
|
|
@@ -3246,6 +3325,7 @@ editHandlers.compositionend = (view, event) => {
|
|
|
3246
3325
|
if (view.composing) {
|
|
3247
3326
|
view.input.composing = false;
|
|
3248
3327
|
view.input.compositionEndedAt = event.timeStamp;
|
|
3328
|
+
view.input.compositionID++;
|
|
3249
3329
|
scheduleComposeEnd(view, 20);
|
|
3250
3330
|
}
|
|
3251
3331
|
};
|
|
@@ -4574,6 +4654,7 @@ function ruleFromNode(dom) {
|
|
|
4574
4654
|
}
|
|
4575
4655
|
return null;
|
|
4576
4656
|
}
|
|
4657
|
+
const isInline = /^(a|abbr|acronym|b|bd[io]|big|br|button|cite|code|data(list)?|del|dfn|em|i|ins|kbd|label|map|mark|meter|output|q|ruby|s|samp|small|span|strong|su[bp]|time|u|tt|var)$/i;
|
|
4577
4658
|
function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
4578
4659
|
if (from < 0) {
|
|
4579
4660
|
let origin = view.input.lastSelectionTime > Date.now() - 50 ? view.input.lastSelectionOrigin : null;
|
|
@@ -4588,6 +4669,8 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
4588
4669
|
tr.setMeta("pointer", true);
|
|
4589
4670
|
else if (origin == "key")
|
|
4590
4671
|
tr.scrollIntoView();
|
|
4672
|
+
if (view.composing)
|
|
4673
|
+
tr.setMeta("composition", view.input.compositionID);
|
|
4591
4674
|
view.dispatch(tr);
|
|
4592
4675
|
}
|
|
4593
4676
|
return;
|
|
@@ -4612,7 +4695,7 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
4612
4695
|
view.input.lastKeyCode = null;
|
|
4613
4696
|
let change = findDiff(compare.content, parse.doc.content, parse.from, preferredPos, preferredSide);
|
|
4614
4697
|
if ((ios && view.input.lastIOSEnter > Date.now() - 225 || android) &&
|
|
4615
|
-
addedNodes.some(n => n.
|
|
4698
|
+
addedNodes.some(n => n.nodeType == 1 && !isInline.test(n.nodeName)) &&
|
|
4616
4699
|
(!change || change.endA >= change.endB) &&
|
|
4617
4700
|
view.someProp("handleKeyDown", f => f(view, keyEvent(13, "Enter")))) {
|
|
4618
4701
|
view.input.lastIOSEnter = 0;
|
|
@@ -4626,8 +4709,12 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
4626
4709
|
else {
|
|
4627
4710
|
if (parse.sel) {
|
|
4628
4711
|
let sel = resolveSelection(view, view.state.doc, parse.sel);
|
|
4629
|
-
if (sel && !sel.eq(view.state.selection))
|
|
4630
|
-
view.
|
|
4712
|
+
if (sel && !sel.eq(view.state.selection)) {
|
|
4713
|
+
let tr = view.state.tr.setSelection(sel);
|
|
4714
|
+
if (view.composing)
|
|
4715
|
+
tr.setMeta("composition", view.input.compositionID);
|
|
4716
|
+
view.dispatch(tr);
|
|
4717
|
+
}
|
|
4631
4718
|
}
|
|
4632
4719
|
return;
|
|
4633
4720
|
}
|
|
@@ -4758,6 +4845,8 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
4758
4845
|
}
|
|
4759
4846
|
if (storedMarks)
|
|
4760
4847
|
tr.ensureMarks(storedMarks);
|
|
4848
|
+
if (view.composing)
|
|
4849
|
+
tr.setMeta("composition", view.input.compositionID);
|
|
4761
4850
|
view.dispatch(tr.scrollIntoView());
|
|
4762
4851
|
}
|
|
4763
4852
|
function resolveSelection(view, doc, parsedSel) {
|
package/package.json
CHANGED
package/src/browser.ts
CHANGED
|
@@ -18,6 +18,7 @@ export const safari = !ie && !!nav && /Apple Computer/.test(nav.vendor)
|
|
|
18
18
|
// Is true for both iOS and iPadOS for convenience
|
|
19
19
|
export const ios = safari && (/Mobile\/\w+/.test(agent) || !!nav && nav.maxTouchPoints > 2)
|
|
20
20
|
export const mac = ios || (nav ? /Mac/.test(nav.platform) : false)
|
|
21
|
+
export const windows = nav ? /Win/.test(nav.platform) : false
|
|
21
22
|
export const android = /Android \d/.test(agent)
|
|
22
23
|
export const webkit = !!doc && "webkitFontSmoothing" in doc.documentElement.style
|
|
23
24
|
export const webkit_version = webkit ? +(/\bAppleWebKit\/(\d+)/.exec(navigator.userAgent) || [0, 0])[1] : 0
|
package/src/capturekeys.ts
CHANGED
|
@@ -21,7 +21,7 @@ function selectHorizontally(view: EditorView, dir: number, mods: string) {
|
|
|
21
21
|
if (sel instanceof TextSelection) {
|
|
22
22
|
if (!sel.empty || mods.indexOf("s") > -1) {
|
|
23
23
|
return false
|
|
24
|
-
} else if (view.endOfTextblock(dir > 0 ? "
|
|
24
|
+
} else if (view.endOfTextblock(dir > 0 ? "forward" : "backward")) {
|
|
25
25
|
let next = moveSelectionBlock(view.state, dir)
|
|
26
26
|
if (next && (next instanceof NodeSelection)) return apply(view, next)
|
|
27
27
|
return false
|
|
@@ -59,9 +59,13 @@ function isIgnorable(dom: Node) {
|
|
|
59
59
|
return desc && desc.size == 0 && (dom.nextSibling || dom.nodeName != "BR")
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
function skipIgnoredNodes(view: EditorView, dir: number) {
|
|
63
|
+
return dir < 0 ? skipIgnoredNodesBefore(view) : skipIgnoredNodesAfter(view)
|
|
64
|
+
}
|
|
65
|
+
|
|
62
66
|
// Make sure the cursor isn't directly after one or more ignored
|
|
63
67
|
// nodes, which will confuse the browser's cursor motion logic.
|
|
64
|
-
function
|
|
68
|
+
function skipIgnoredNodesBefore(view: EditorView) {
|
|
65
69
|
let sel = view.domSelectionRange()
|
|
66
70
|
let node = sel.focusNode!, offset = sel.focusOffset
|
|
67
71
|
if (!node) return
|
|
@@ -109,7 +113,7 @@ function skipIgnoredNodesLeft(view: EditorView) {
|
|
|
109
113
|
|
|
110
114
|
// Make sure the cursor isn't directly before one or more ignored
|
|
111
115
|
// nodes.
|
|
112
|
-
function
|
|
116
|
+
function skipIgnoredNodesAfter(view: EditorView) {
|
|
113
117
|
let sel = view.domSelectionRange()
|
|
114
118
|
let node = sel.focusNode!, offset = sel.focusOffset
|
|
115
119
|
if (!node) return
|
|
@@ -171,6 +175,27 @@ function setSelFocus(view: EditorView, node: Node, offset: number) {
|
|
|
171
175
|
}, 50)
|
|
172
176
|
}
|
|
173
177
|
|
|
178
|
+
function findDirection(view: EditorView, pos: number): "rtl" | "ltr" {
|
|
179
|
+
let $pos = view.state.doc.resolve(pos)
|
|
180
|
+
if (!(browser.chrome || browser.windows) && $pos.parent.inlineContent) {
|
|
181
|
+
let coords = view.coordsAtPos(pos)
|
|
182
|
+
if (pos > $pos.start()) {
|
|
183
|
+
let before = view.coordsAtPos(pos - 1)
|
|
184
|
+
let mid = (before.top + before.bottom) / 2
|
|
185
|
+
if (mid > coords.top && mid < coords.bottom && Math.abs(before.left - coords.left) > 1)
|
|
186
|
+
return before.left < coords.left ? "ltr" : "rtl"
|
|
187
|
+
}
|
|
188
|
+
if (pos < $pos.end()) {
|
|
189
|
+
let after = view.coordsAtPos(pos + 1)
|
|
190
|
+
let mid = (after.top + after.bottom) / 2
|
|
191
|
+
if (mid > coords.top && mid < coords.bottom && Math.abs(after.left - coords.left) > 1)
|
|
192
|
+
return after.left > coords.left ? "ltr" : "rtl"
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
let computed = getComputedStyle(view.dom).direction
|
|
196
|
+
return computed == "rtl" ? "rtl" : "ltr"
|
|
197
|
+
}
|
|
198
|
+
|
|
174
199
|
// Check whether vertical selection motion would involve node
|
|
175
200
|
// selections. If so, apply it (if not, the result is left to the
|
|
176
201
|
// browser)
|
|
@@ -252,19 +277,21 @@ function getMods(event: KeyboardEvent) {
|
|
|
252
277
|
export function captureKeyDown(view: EditorView, event: KeyboardEvent) {
|
|
253
278
|
let code = event.keyCode, mods = getMods(event)
|
|
254
279
|
if (code == 8 || (browser.mac && code == 72 && mods == "c")) { // Backspace, Ctrl-h on Mac
|
|
255
|
-
return stopNativeHorizontalDelete(view, -1) ||
|
|
280
|
+
return stopNativeHorizontalDelete(view, -1) || skipIgnoredNodes(view, -1)
|
|
256
281
|
} else if (code == 46 || (browser.mac && code == 68 && mods == "c")) { // Delete, Ctrl-d on Mac
|
|
257
|
-
return stopNativeHorizontalDelete(view, 1) ||
|
|
282
|
+
return stopNativeHorizontalDelete(view, 1) || skipIgnoredNodes(view, 1)
|
|
258
283
|
} else if (code == 13 || code == 27) { // Enter, Esc
|
|
259
284
|
return true
|
|
260
285
|
} else if (code == 37 || (browser.mac && code == 66 && mods == "c")) { // Left arrow, Ctrl-b on Mac
|
|
261
|
-
|
|
286
|
+
let dir = code == 37 ? (findDirection(view, view.state.selection.from) == "ltr" ? -1 : 1) : -1
|
|
287
|
+
return selectHorizontally(view, dir, mods) || skipIgnoredNodes(view, dir)
|
|
262
288
|
} else if (code == 39 || (browser.mac && code == 70 && mods == "c")) { // Right arrow, Ctrl-f on Mac
|
|
263
|
-
|
|
289
|
+
let dir = code == 39 ? (findDirection(view, view.state.selection.from) == "ltr" ? 1 : -1) : 1
|
|
290
|
+
return selectHorizontally(view, dir, mods) || skipIgnoredNodes(view, dir)
|
|
264
291
|
} else if (code == 38 || (browser.mac && code == 80 && mods == "c")) { // Up arrow, Ctrl-p on Mac
|
|
265
|
-
return selectVertically(view, -1, mods) ||
|
|
292
|
+
return selectVertically(view, -1, mods) || skipIgnoredNodes(view, -1)
|
|
266
293
|
} else if (code == 40 || (browser.mac && code == 78 && mods == "c")) { // Down arrow, Ctrl-n on Mac
|
|
267
|
-
return safariDownArrowBug(view) || selectVertically(view, 1, mods) ||
|
|
294
|
+
return safariDownArrowBug(view) || selectVertically(view, 1, mods) || skipIgnoredNodesAfter(view)
|
|
268
295
|
} else if (mods == (browser.mac ? "m" : "c") &&
|
|
269
296
|
(code == 66 || code == 73 || code == 89 || code == 90)) { // Mod-[biyz]
|
|
270
297
|
return true
|
package/src/dom.ts
CHANGED
|
@@ -100,3 +100,16 @@ export function deepActiveElement(doc: Document) {
|
|
|
100
100
|
while (elt && elt.shadowRoot) elt = elt.shadowRoot.activeElement
|
|
101
101
|
return elt
|
|
102
102
|
}
|
|
103
|
+
|
|
104
|
+
export function caretFromPoint(doc: Document, x: number, y: number): {node: Node, offset: number} | undefined {
|
|
105
|
+
if ((doc as any).caretPositionFromPoint) {
|
|
106
|
+
try { // Firefox throws for this call in hard-to-predict circumstances (#994)
|
|
107
|
+
let pos = (doc as any).caretPositionFromPoint(x, y)
|
|
108
|
+
if (pos) return {node: pos.offsetNode, offset: pos.offset}
|
|
109
|
+
} catch (_) {}
|
|
110
|
+
}
|
|
111
|
+
if (doc.caretRangeFromPoint) {
|
|
112
|
+
let range = doc.caretRangeFromPoint(x, y)
|
|
113
|
+
if (range) return {node: range.startContainer, offset: range.startOffset}
|
|
114
|
+
}
|
|
115
|
+
}
|
package/src/domchange.ts
CHANGED
|
@@ -76,6 +76,8 @@ function ruleFromNode(dom: DOMNode): ParseRule | null {
|
|
|
76
76
|
return null
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
const isInline = /^(a|abbr|acronym|b|bd[io]|big|br|button|cite|code|data(list)?|del|dfn|em|i|ins|kbd|label|map|mark|meter|output|q|ruby|s|samp|small|span|strong|su[bp]|time|u|tt|var)$/i
|
|
80
|
+
|
|
79
81
|
export function readDOMChange(view: EditorView, from: number, to: number, typeOver: boolean, addedNodes: readonly DOMNode[]) {
|
|
80
82
|
if (from < 0) {
|
|
81
83
|
let origin = view.input.lastSelectionTime > Date.now() - 50 ? view.input.lastSelectionOrigin : null
|
|
@@ -88,6 +90,7 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
|
|
|
88
90
|
let tr = view.state.tr.setSelection(newSel)
|
|
89
91
|
if (origin == "pointer") tr.setMeta("pointer", true)
|
|
90
92
|
else if (origin == "key") tr.scrollIntoView()
|
|
93
|
+
if (view.composing) tr.setMeta("composition", view.input.compositionID)
|
|
91
94
|
view.dispatch(tr)
|
|
92
95
|
}
|
|
93
96
|
return
|
|
@@ -115,7 +118,7 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
|
|
|
115
118
|
|
|
116
119
|
let change = findDiff(compare.content, parse.doc.content, parse.from, preferredPos, preferredSide)
|
|
117
120
|
if ((browser.ios && view.input.lastIOSEnter > Date.now() - 225 || browser.android) &&
|
|
118
|
-
addedNodes.some(n => n.
|
|
121
|
+
addedNodes.some(n => n.nodeType == 1 && !isInline.test(n.nodeName)) &&
|
|
119
122
|
(!change || change.endA >= change.endB) &&
|
|
120
123
|
view.someProp("handleKeyDown", f => f(view, keyEvent(13, "Enter")))) {
|
|
121
124
|
view.input.lastIOSEnter = 0
|
|
@@ -128,7 +131,11 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
|
|
|
128
131
|
} else {
|
|
129
132
|
if (parse.sel) {
|
|
130
133
|
let sel = resolveSelection(view, view.state.doc, parse.sel)
|
|
131
|
-
if (sel && !sel.eq(view.state.selection))
|
|
134
|
+
if (sel && !sel.eq(view.state.selection)) {
|
|
135
|
+
let tr = view.state.tr.setSelection(sel)
|
|
136
|
+
if (view.composing) tr.setMeta("composition", view.input.compositionID)
|
|
137
|
+
view.dispatch(tr)
|
|
138
|
+
}
|
|
132
139
|
}
|
|
133
140
|
return
|
|
134
141
|
}
|
|
@@ -261,6 +268,7 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
|
|
|
261
268
|
tr.setSelection(sel)
|
|
262
269
|
}
|
|
263
270
|
if (storedMarks) tr.ensureMarks(storedMarks)
|
|
271
|
+
if (view.composing) tr.setMeta("composition", view.input.compositionID)
|
|
264
272
|
view.dispatch(tr.scrollIntoView())
|
|
265
273
|
}
|
|
266
274
|
|
package/src/domcoords.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {EditorState} from "prosemirror-state"
|
|
2
|
-
import {nodeSize, textRange, parentNode} from "./dom"
|
|
2
|
+
import {nodeSize, textRange, parentNode, caretFromPoint} from "./dom"
|
|
3
3
|
import * as browser from "./browser"
|
|
4
4
|
import {EditorView} from "./index"
|
|
5
5
|
|
|
@@ -133,6 +133,7 @@ export function focusPreventScroll(dom: HTMLElement) {
|
|
|
133
133
|
function findOffsetInNode(node: HTMLElement, coords: {top: number, left: number}): {node: Node, offset: number} {
|
|
134
134
|
let closest, dxClosest = 2e8, coordsClosest: {left: number, top: number} | undefined, offset = 0
|
|
135
135
|
let rowBot = coords.top, rowTop = coords.top
|
|
136
|
+
let firstBelow: Node | undefined, coordsBelow: {left: number, top: number} | undefined
|
|
136
137
|
for (let child = node.firstChild, childIndex = 0; child; child = child.nextSibling, childIndex++) {
|
|
137
138
|
let rects
|
|
138
139
|
if (child.nodeType == 1) rects = (child as HTMLElement).getClientRects()
|
|
@@ -157,12 +158,16 @@ function findOffsetInNode(node: HTMLElement, coords: {top: number, left: number}
|
|
|
157
158
|
offset = childIndex + (coords.left >= (rect.left + rect.right) / 2 ? 1 : 0)
|
|
158
159
|
continue
|
|
159
160
|
}
|
|
161
|
+
} else if (rect.top > coords.top && !firstBelow && rect.left <= coords.left && rect.right >= coords.left) {
|
|
162
|
+
firstBelow = child
|
|
163
|
+
coordsBelow = {left: Math.max(rect.left, Math.min(rect.right, coords.left)), top: rect.top}
|
|
160
164
|
}
|
|
161
165
|
if (!closest && (coords.left >= rect.right && coords.top >= rect.top ||
|
|
162
166
|
coords.left >= rect.left && coords.top >= rect.bottom))
|
|
163
167
|
offset = childIndex + 1
|
|
164
168
|
}
|
|
165
169
|
}
|
|
170
|
+
if (!closest && firstBelow) { closest = firstBelow; coordsClosest = coordsBelow; dxClosest = 0 }
|
|
166
171
|
if (closest && closest.nodeType == 3) return findOffsetInText(closest as Text, coordsClosest!)
|
|
167
172
|
if (!closest || (dxClosest && closest.nodeType == 1)) return {node, offset}
|
|
168
173
|
return findOffsetInNode(closest as HTMLElement, coordsClosest!)
|
|
@@ -211,13 +216,14 @@ function posFromCaret(view: EditorView, node: Node, offset: number, coords: {top
|
|
|
211
216
|
// fall outside of. If so, we take the position before/after that
|
|
212
217
|
// block. If not, we call `posFromDOM` on the raw node/offset.
|
|
213
218
|
let outsideBlock = -1
|
|
214
|
-
for (let cur = node;;) {
|
|
219
|
+
for (let cur = node, sawBlock = false;;) {
|
|
215
220
|
if (cur == view.dom) break
|
|
216
221
|
let desc = view.docView.nearestDesc(cur, true)
|
|
217
222
|
if (!desc) return null
|
|
218
|
-
if (desc.dom.nodeType == 1 && (desc.node.isBlock && desc.parent || !desc.contentDOM)) {
|
|
223
|
+
if (desc.dom.nodeType == 1 && (desc.node.isBlock && desc.parent && !sawBlock || !desc.contentDOM)) {
|
|
219
224
|
let rect = (desc.dom as HTMLElement).getBoundingClientRect()
|
|
220
|
-
if (desc.node.isBlock && desc.parent) {
|
|
225
|
+
if (desc.node.isBlock && desc.parent && !sawBlock) {
|
|
226
|
+
sawBlock = true
|
|
221
227
|
if (rect.left > coords.left || rect.top > coords.top) outsideBlock = desc.posBefore
|
|
222
228
|
else if (rect.right < coords.left || rect.bottom < coords.top) outsideBlock = desc.posAfter
|
|
223
229
|
}
|
|
@@ -254,16 +260,8 @@ function elementFromPoint(element: HTMLElement, coords: {top: number, left: numb
|
|
|
254
260
|
// Given an x,y position on the editor, get the position in the document.
|
|
255
261
|
export function posAtCoords(view: EditorView, coords: {top: number, left: number}) {
|
|
256
262
|
let doc = view.dom.ownerDocument, node: Node | undefined, offset = 0
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
let pos = (doc as any).caretPositionFromPoint(coords.left, coords.top)
|
|
260
|
-
if (pos) ({offsetNode: node, offset} = pos)
|
|
261
|
-
} catch (_) {}
|
|
262
|
-
}
|
|
263
|
-
if (!node && doc.caretRangeFromPoint) {
|
|
264
|
-
let range = doc.caretRangeFromPoint(coords.left, coords.top)
|
|
265
|
-
if (range) ({startContainer: node, startOffset: offset} = range)
|
|
266
|
-
}
|
|
263
|
+
let caret = caretFromPoint(doc, coords.left, coords.top)
|
|
264
|
+
if (caret) ({node, offset} = caret)
|
|
267
265
|
|
|
268
266
|
let elt = ((view.root as any).elementFromPoint ? view.root : doc)
|
|
269
267
|
.elementFromPoint(coords.left, coords.top) as HTMLElement
|
|
@@ -311,9 +309,17 @@ export function posAtCoords(view: EditorView, coords: {top: number, left: number
|
|
|
311
309
|
return {pos, inside: desc ? desc.posAtStart - desc.border : -1}
|
|
312
310
|
}
|
|
313
311
|
|
|
312
|
+
function nonZero(rect: DOMRect) {
|
|
313
|
+
return rect.top < rect.bottom || rect.left < rect.right
|
|
314
|
+
}
|
|
315
|
+
|
|
314
316
|
function singleRect(target: HTMLElement | Range, bias: number): DOMRect {
|
|
315
317
|
let rects = target.getClientRects()
|
|
316
|
-
|
|
318
|
+
if (rects.length) {
|
|
319
|
+
let first = rects[bias < 0 ? 0 : rects.length - 1]
|
|
320
|
+
if (nonZero(first)) return first
|
|
321
|
+
}
|
|
322
|
+
return Array.prototype.find.call(rects, nonZero) || target.getBoundingClientRect()
|
|
317
323
|
}
|
|
318
324
|
|
|
319
325
|
const BIDI = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/
|
|
@@ -347,7 +353,7 @@ export function coordsAtPos(view: EditorView, pos: number, side: number): Rect {
|
|
|
347
353
|
else if (side >= 0 && offset == node.nodeValue!.length) { from--; takeSide = 1 }
|
|
348
354
|
else if (side < 0) { from-- }
|
|
349
355
|
else { to ++ }
|
|
350
|
-
return flattenV(singleRect(textRange(node as Text, from, to),
|
|
356
|
+
return flattenV(singleRect(textRange(node as Text, from, to), takeSide), takeSide < 0)
|
|
351
357
|
}
|
|
352
358
|
}
|
|
353
359
|
|
package/src/index.ts
CHANGED
|
@@ -544,12 +544,12 @@ function checkStateComponent(plugin: Plugin) {
|
|
|
544
544
|
throw new RangeError("Plugins passed directly to the view must not have a state component")
|
|
545
545
|
}
|
|
546
546
|
|
|
547
|
-
/// The type of function [provided](#view.
|
|
547
|
+
/// The type of function [provided](#view.EditorProps.nodeViews) to
|
|
548
548
|
/// create [node views](#view.NodeView).
|
|
549
|
-
export type NodeViewConstructor = (node: Node, view: EditorView, getPos: () => number,
|
|
549
|
+
export type NodeViewConstructor = (node: Node, view: EditorView, getPos: () => number | undefined,
|
|
550
550
|
decorations: readonly Decoration[], innerDecorations: DecorationSource) => NodeView
|
|
551
551
|
|
|
552
|
-
/// The function types [used](#view.
|
|
552
|
+
/// The function types [used](#view.EditorProps.markViews) to create
|
|
553
553
|
/// mark views.
|
|
554
554
|
export type MarkViewConstructor = (mark: Mark, view: EditorView, inline: boolean) => {dom: HTMLElement, contentDOM?: HTMLElement}
|
|
555
555
|
|
|
@@ -684,6 +684,8 @@ export interface EditorProps<P = any> {
|
|
|
684
684
|
/// node's display behavior. The third argument `getPos` is a
|
|
685
685
|
/// function that can be called to get the node's current position,
|
|
686
686
|
/// which can be useful when creating transactions to update it.
|
|
687
|
+
/// Note that if the node is not in the document, the position
|
|
688
|
+
/// returned by this function will be `undefined`.
|
|
687
689
|
///
|
|
688
690
|
/// `decorations` is an array of node or inline decorations that are
|
|
689
691
|
/// active around the node. They are automatically drawn in the
|
|
@@ -699,7 +701,7 @@ export interface EditorProps<P = any> {
|
|
|
699
701
|
/// inner decorations.
|
|
700
702
|
///
|
|
701
703
|
/// (For backwards compatibility reasons, [mark
|
|
702
|
-
/// views](#view.
|
|
704
|
+
/// views](#view.EditorProps.markViews) can also be included in this
|
|
703
705
|
/// object.)
|
|
704
706
|
nodeViews?: {[node: string]: NodeViewConstructor}
|
|
705
707
|
|
package/src/input.ts
CHANGED
|
@@ -33,6 +33,7 @@ export class InputState {
|
|
|
33
33
|
composingTimeout = -1
|
|
34
34
|
compositionNodes: ViewDesc[] = []
|
|
35
35
|
compositionEndedAt = -2e8
|
|
36
|
+
compositionID = 1
|
|
36
37
|
domChangeCount = 0
|
|
37
38
|
eventHandlers: {[event: string]: (event: Event) => void} = Object.create(null)
|
|
38
39
|
hideSelectionGuard: (() => void) | null = null
|
|
@@ -486,6 +487,7 @@ editHandlers.compositionend = (view, event) => {
|
|
|
486
487
|
if (view.composing) {
|
|
487
488
|
view.input.composing = false
|
|
488
489
|
view.input.compositionEndedAt = event.timeStamp
|
|
490
|
+
view.input.compositionID++
|
|
489
491
|
scheduleComposeEnd(view, 20)
|
|
490
492
|
}
|
|
491
493
|
}
|
package/src/viewdesc.ts
CHANGED
|
@@ -619,7 +619,6 @@ export class NodeViewDesc extends ViewDesc {
|
|
|
619
619
|
pos: number
|
|
620
620
|
) {
|
|
621
621
|
super(parent, [], dom, contentDOM)
|
|
622
|
-
if (contentDOM) this.updateChildren(view, pos)
|
|
623
622
|
}
|
|
624
623
|
|
|
625
624
|
// By default, a node is rendered using the `toDOM` method from the
|
|
@@ -733,7 +732,7 @@ export class NodeViewDesc extends ViewDesc {
|
|
|
733
732
|
(compIndex = updater.findIndexWithChild(composition!.node)) > -1 &&
|
|
734
733
|
updater.updateNodeAt(child, outerDeco, innerDeco, compIndex, view)) {
|
|
735
734
|
// Updated the specific node that holds the composition
|
|
736
|
-
} else if (updater.updateNextNode(child, outerDeco, innerDeco, view, i)) {
|
|
735
|
+
} else if (updater.updateNextNode(child, outerDeco, innerDeco, view, i, off)) {
|
|
737
736
|
// Could update an existing node to reflect this node
|
|
738
737
|
} else {
|
|
739
738
|
// Add it as a new view
|
|
@@ -846,7 +845,9 @@ export class NodeViewDesc extends ViewDesc {
|
|
|
846
845
|
export function docViewDesc(doc: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource,
|
|
847
846
|
dom: HTMLElement, view: EditorView): NodeViewDesc {
|
|
848
847
|
applyOuterDeco(dom, outerDeco, doc)
|
|
849
|
-
|
|
848
|
+
let docView = new NodeViewDesc(undefined, doc, outerDeco, innerDeco, dom, dom, dom, view, 0)
|
|
849
|
+
if (docView.contentDOM) docView.updateChildren(view, 0)
|
|
850
|
+
return docView
|
|
850
851
|
}
|
|
851
852
|
|
|
852
853
|
class TextViewDesc extends NodeViewDesc {
|
|
@@ -1222,13 +1223,13 @@ class ViewTreeUpdater {
|
|
|
1222
1223
|
// Try to update the next node, if any, to the given data. Checks
|
|
1223
1224
|
// pre-matches to avoid overwriting nodes that could still be used.
|
|
1224
1225
|
updateNextNode(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource,
|
|
1225
|
-
view: EditorView, index: number): boolean {
|
|
1226
|
+
view: EditorView, index: number, pos: number): boolean {
|
|
1226
1227
|
for (let i = this.index; i < this.top.children.length; i++) {
|
|
1227
1228
|
let next = this.top.children[i]
|
|
1228
1229
|
if (next instanceof NodeViewDesc) {
|
|
1229
1230
|
let preMatch = this.preMatch.matched.get(next)
|
|
1230
1231
|
if (preMatch != null && preMatch != index) return false
|
|
1231
|
-
let nextDOM = next.dom
|
|
1232
|
+
let nextDOM = next.dom, updated
|
|
1232
1233
|
|
|
1233
1234
|
// Can't update if nextDOM is or contains this.lock, except if
|
|
1234
1235
|
// it's a text node whose content already matches the new text
|
|
@@ -1241,6 +1242,11 @@ class ViewTreeUpdater {
|
|
|
1241
1242
|
if (next.dom != nextDOM) this.changed = true
|
|
1242
1243
|
this.index++
|
|
1243
1244
|
return true
|
|
1245
|
+
} else if (!locked && (updated = this.recreateWrapper(next, node, outerDeco, innerDeco, view, pos))) {
|
|
1246
|
+
this.top.children[this.index] = updated
|
|
1247
|
+
this.changed = true
|
|
1248
|
+
this.index++
|
|
1249
|
+
return true
|
|
1244
1250
|
}
|
|
1245
1251
|
break
|
|
1246
1252
|
}
|
|
@@ -1248,9 +1254,28 @@ class ViewTreeUpdater {
|
|
|
1248
1254
|
return false
|
|
1249
1255
|
}
|
|
1250
1256
|
|
|
1257
|
+
// When a node with content is replaced by a different node with
|
|
1258
|
+
// identical content, move over its children.
|
|
1259
|
+
recreateWrapper(next: NodeViewDesc, node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource,
|
|
1260
|
+
view: EditorView, pos: number) {
|
|
1261
|
+
if (next.dirty || node.isAtom || !next.children.length ||
|
|
1262
|
+
!next.node.content.eq(node.content)) return null
|
|
1263
|
+
let wrapper = NodeViewDesc.create(this.top, node, outerDeco, innerDeco, view, pos)
|
|
1264
|
+
if (!wrapper.contentDOM) return null
|
|
1265
|
+
wrapper.children = next.children
|
|
1266
|
+
next.children = []
|
|
1267
|
+
for (let ch of wrapper.children) ch.parent = wrapper
|
|
1268
|
+
wrapper.dirty = CONTENT_DIRTY
|
|
1269
|
+
wrapper.updateChildren(view, pos + 1)
|
|
1270
|
+
wrapper.dirty = NOT_DIRTY
|
|
1271
|
+
return wrapper
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1251
1274
|
// Insert the node as a newly created node desc.
|
|
1252
1275
|
addNode(node: Node, outerDeco: readonly Decoration[], innerDeco: DecorationSource, view: EditorView, pos: number) {
|
|
1253
|
-
|
|
1276
|
+
let desc = NodeViewDesc.create(this.top, node, outerDeco, innerDeco, view, pos)
|
|
1277
|
+
if (desc.contentDOM) desc.updateChildren(view, pos + 1)
|
|
1278
|
+
this.top.children.splice(this.index++, 0, desc)
|
|
1254
1279
|
this.changed = true
|
|
1255
1280
|
}
|
|
1256
1281
|
|