prosemirror-view 1.31.5 → 1.31.7
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 +18 -0
- package/dist/index.cjs +77 -14
- package/dist/index.js +65 -15
- package/package.json +1 -1
- package/src/capturekeys.ts +37 -1
- package/src/dom.ts +1 -1
- package/src/domchange.ts +6 -3
- package/src/domcoords.ts +5 -3
- package/src/domobserver.ts +7 -5
- package/src/input.ts +4 -0
- package/src/viewdesc.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
## 1.31.7 (2023-07-28)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix a bug where, when a compositionend event was received before the composition's final changes were processed, the resulting transaction didn't have its composition metadata set.
|
|
6
|
+
|
|
7
|
+
Fix an issue that caused cursor motion to stop at uneditable nodes that aren't widget decorations.
|
|
8
|
+
|
|
9
|
+
When scrolling an element higher than the viewport into view, make sure its top is visible.
|
|
10
|
+
|
|
11
|
+
## 1.31.6 (2023-07-14)
|
|
12
|
+
|
|
13
|
+
### Bug fixes
|
|
14
|
+
|
|
15
|
+
Fix a bug that caused `posAtCoords` to return incorrect results for coordinates over inline-decorated text.
|
|
16
|
+
|
|
17
|
+
Don't try to scroll fixed-positioned elements into view by scrolling their parent elements.
|
|
18
|
+
|
|
1
19
|
## 1.31.5 (2023-06-19)
|
|
2
20
|
|
|
3
21
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -216,7 +216,7 @@ function scrollRectIntoView(view, rect, startDOM) {
|
|
|
216
216
|
var bounding = atTop ? windowRect(doc) : clientRect(elt);
|
|
217
217
|
var moveX = 0,
|
|
218
218
|
moveY = 0;
|
|
219
|
-
if (rect.top < bounding.top + getSide(scrollThreshold, "top")) moveY = -(bounding.top - rect.top + getSide(scrollMargin, "top"));else if (rect.bottom > bounding.bottom - getSide(scrollThreshold, "bottom")) moveY = rect.bottom - bounding.bottom + getSide(scrollMargin, "bottom");
|
|
219
|
+
if (rect.top < bounding.top + getSide(scrollThreshold, "top")) moveY = -(bounding.top - rect.top + getSide(scrollMargin, "top"));else if (rect.bottom > bounding.bottom - getSide(scrollThreshold, "bottom")) moveY = rect.bottom - rect.top > bounding.bottom - bounding.top ? rect.top + getSide(scrollMargin, "top") - bounding.top : rect.bottom - bounding.bottom + getSide(scrollMargin, "bottom");
|
|
220
220
|
if (rect.left < bounding.left + getSide(scrollThreshold, "left")) moveX = -(bounding.left - rect.left + getSide(scrollMargin, "left"));else if (rect.right > bounding.right - getSide(scrollThreshold, "right")) moveX = rect.right - bounding.right + getSide(scrollMargin, "right");
|
|
221
221
|
|
|
222
222
|
if (moveX || moveY) {
|
|
@@ -238,7 +238,7 @@ function scrollRectIntoView(view, rect, startDOM) {
|
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
-
if (atTop) break;
|
|
241
|
+
if (atTop || /^(fixed|sticky)$/.test(getComputedStyle(parent).position)) break;
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
244
|
|
|
@@ -441,7 +441,7 @@ function posFromCaret(view, node, offset, coords) {
|
|
|
441
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;
|
|
442
442
|
}
|
|
443
443
|
|
|
444
|
-
if (!desc.contentDOM && outsideBlock < 0) {
|
|
444
|
+
if (!desc.contentDOM && outsideBlock < 0 && !desc.node.isText) {
|
|
445
445
|
var before = desc.node.isBlock ? coords.top < (rect.top + rect.bottom) / 2 : coords.left < (rect.left + rect.right) / 2;
|
|
446
446
|
return before ? desc.posBefore : desc.posAfter;
|
|
447
447
|
}
|
|
@@ -1393,7 +1393,7 @@ var MarkViewDesc = function (_ViewDesc3) {
|
|
|
1393
1393
|
return {
|
|
1394
1394
|
mark: this.mark.type.name,
|
|
1395
1395
|
attrs: this.mark.attrs,
|
|
1396
|
-
contentElement: this.contentDOM
|
|
1396
|
+
contentElement: this.contentDOM
|
|
1397
1397
|
};
|
|
1398
1398
|
}
|
|
1399
1399
|
}, {
|
|
@@ -2774,6 +2774,7 @@ function nodeLen(node) {
|
|
|
2774
2774
|
}
|
|
2775
2775
|
|
|
2776
2776
|
function isIgnorable(dom) {
|
|
2777
|
+
if (dom.contentEditable == "false") return true;
|
|
2777
2778
|
var desc = dom.pmViewDesc;
|
|
2778
2779
|
return desc && desc.size == 0 && (dom.nextSibling || dom.nodeName != "BR");
|
|
2779
2780
|
}
|
|
@@ -2880,7 +2881,45 @@ function isBlockNode(dom) {
|
|
|
2880
2881
|
return desc && desc.node && desc.node.isBlock;
|
|
2881
2882
|
}
|
|
2882
2883
|
|
|
2884
|
+
function textNodeAfter(node, offset) {
|
|
2885
|
+
while (node && offset == node.childNodes.length && !hasBlockDesc(node)) {
|
|
2886
|
+
offset = domIndex(node) + 1;
|
|
2887
|
+
node = node.parentNode;
|
|
2888
|
+
}
|
|
2889
|
+
|
|
2890
|
+
while (node && offset < node.childNodes.length) {
|
|
2891
|
+
node = node.childNodes[offset];
|
|
2892
|
+
if (node.nodeType == 3) return node;
|
|
2893
|
+
offset = 0;
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
|
|
2897
|
+
function textNodeBefore(node, offset) {
|
|
2898
|
+
while (node && !offset && !hasBlockDesc(node)) {
|
|
2899
|
+
offset = domIndex(node);
|
|
2900
|
+
node = node.parentNode;
|
|
2901
|
+
}
|
|
2902
|
+
|
|
2903
|
+
while (node && offset) {
|
|
2904
|
+
node = node.childNodes[offset - 1];
|
|
2905
|
+
if (node.nodeType == 3) return node;
|
|
2906
|
+
offset = node.childNodes.length;
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
|
|
2883
2910
|
function setSelFocus(view, node, offset) {
|
|
2911
|
+
if (node.nodeType != 3) {
|
|
2912
|
+
var before, after;
|
|
2913
|
+
|
|
2914
|
+
if (after = textNodeAfter(node, offset)) {
|
|
2915
|
+
node = after;
|
|
2916
|
+
offset = 0;
|
|
2917
|
+
} else if (before = textNodeBefore(node, offset)) {
|
|
2918
|
+
node = before;
|
|
2919
|
+
offset = before.nodeValue.length;
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
|
|
2884
2923
|
var sel = view.domSelection();
|
|
2885
2924
|
|
|
2886
2925
|
if (selectionCollapsed(sel)) {
|
|
@@ -3349,6 +3388,7 @@ var InputState = _createClass(function InputState() {
|
|
|
3349
3388
|
this.compositionNodes = [];
|
|
3350
3389
|
this.compositionEndedAt = -2e8;
|
|
3351
3390
|
this.compositionID = 1;
|
|
3391
|
+
this.compositionPendingChanges = 0;
|
|
3352
3392
|
this.domChangeCount = 0;
|
|
3353
3393
|
this.eventHandlers = Object.create(null);
|
|
3354
3394
|
this.hideSelectionGuard = null;
|
|
@@ -3812,6 +3852,10 @@ editHandlers.compositionend = function (view, event) {
|
|
|
3812
3852
|
if (view.composing) {
|
|
3813
3853
|
view.input.composing = false;
|
|
3814
3854
|
view.input.compositionEndedAt = event.timeStamp;
|
|
3855
|
+
view.input.compositionPendingChanges = view.domObserver.pendingRecords().length ? view.input.compositionID : 0;
|
|
3856
|
+
if (view.input.compositionPendingChanges) Promise.resolve().then(function () {
|
|
3857
|
+
return view.domObserver.flush();
|
|
3858
|
+
});
|
|
3815
3859
|
view.input.compositionID++;
|
|
3816
3860
|
scheduleComposeEnd(view, 20);
|
|
3817
3861
|
}
|
|
@@ -5018,18 +5062,34 @@ var DOMObserver = function () {
|
|
|
5018
5062
|
return true;
|
|
5019
5063
|
}
|
|
5020
5064
|
}
|
|
5065
|
+
}, {
|
|
5066
|
+
key: "pendingRecords",
|
|
5067
|
+
value: function pendingRecords() {
|
|
5068
|
+
if (this.observer) {
|
|
5069
|
+
var _iterator2 = _createForOfIteratorHelper(this.observer.takeRecords()),
|
|
5070
|
+
_step2;
|
|
5071
|
+
|
|
5072
|
+
try {
|
|
5073
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
5074
|
+
var mut = _step2.value;
|
|
5075
|
+
this.queue.push(mut);
|
|
5076
|
+
}
|
|
5077
|
+
} catch (err) {
|
|
5078
|
+
_iterator2.e(err);
|
|
5079
|
+
} finally {
|
|
5080
|
+
_iterator2.f();
|
|
5081
|
+
}
|
|
5082
|
+
}
|
|
5083
|
+
|
|
5084
|
+
return this.queue;
|
|
5085
|
+
}
|
|
5021
5086
|
}, {
|
|
5022
5087
|
key: "flush",
|
|
5023
5088
|
value: function flush() {
|
|
5024
5089
|
var view = this.view;
|
|
5025
5090
|
if (!view.docView || this.flushingSoon > -1) return;
|
|
5026
|
-
var mutations = this.
|
|
5027
|
-
|
|
5028
|
-
if (this.queue.length) {
|
|
5029
|
-
mutations = this.queue.concat(mutations);
|
|
5030
|
-
this.queue.length = 0;
|
|
5031
|
-
}
|
|
5032
|
-
|
|
5091
|
+
var mutations = this.pendingRecords();
|
|
5092
|
+
if (mutations.length) this.queue = [];
|
|
5033
5093
|
var sel = view.domSelectionRange();
|
|
5034
5094
|
var newSel = !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(view) && !this.ignoreSelectionChange(sel);
|
|
5035
5095
|
var from = -1,
|
|
@@ -5284,6 +5344,9 @@ function ruleFromNode(dom) {
|
|
|
5284
5344
|
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;
|
|
5285
5345
|
|
|
5286
5346
|
function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
5347
|
+
var compositionID = view.input.compositionPendingChanges || (view.composing ? view.input.compositionID : 0);
|
|
5348
|
+
view.input.compositionPendingChanges = 0;
|
|
5349
|
+
|
|
5287
5350
|
if (from < 0) {
|
|
5288
5351
|
var origin = view.input.lastSelectionTime > Date.now() - 50 ? view.input.lastSelectionOrigin : null;
|
|
5289
5352
|
var newSel = selectionFromDOM(view, origin);
|
|
@@ -5296,7 +5359,7 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
5296
5359
|
var _tr = view.state.tr.setSelection(newSel);
|
|
5297
5360
|
|
|
5298
5361
|
if (origin == "pointer") _tr.setMeta("pointer", true);else if (origin == "key") _tr.scrollIntoView();
|
|
5299
|
-
if (
|
|
5362
|
+
if (compositionID) _tr.setMeta("composition", compositionID);
|
|
5300
5363
|
view.dispatch(_tr);
|
|
5301
5364
|
}
|
|
5302
5365
|
|
|
@@ -5347,7 +5410,7 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
5347
5410
|
if (_sel && !_sel.eq(view.state.selection)) {
|
|
5348
5411
|
var _tr2 = view.state.tr.setSelection(_sel);
|
|
5349
5412
|
|
|
5350
|
-
if (
|
|
5413
|
+
if (compositionID) _tr2.setMeta("composition", compositionID);
|
|
5351
5414
|
view.dispatch(_tr2);
|
|
5352
5415
|
}
|
|
5353
5416
|
}
|
|
@@ -5451,7 +5514,7 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
5451
5514
|
}
|
|
5452
5515
|
|
|
5453
5516
|
if (storedMarks) tr.ensureMarks(storedMarks);
|
|
5454
|
-
if (
|
|
5517
|
+
if (compositionID) tr.setMeta("composition", compositionID);
|
|
5455
5518
|
view.dispatch(tr.scrollIntoView());
|
|
5456
5519
|
}
|
|
5457
5520
|
|
package/dist/index.js
CHANGED
|
@@ -163,7 +163,9 @@ function scrollRectIntoView(view, rect, startDOM) {
|
|
|
163
163
|
if (rect.top < bounding.top + getSide(scrollThreshold, "top"))
|
|
164
164
|
moveY = -(bounding.top - rect.top + getSide(scrollMargin, "top"));
|
|
165
165
|
else if (rect.bottom > bounding.bottom - getSide(scrollThreshold, "bottom"))
|
|
166
|
-
moveY = rect.bottom - bounding.bottom
|
|
166
|
+
moveY = rect.bottom - rect.top > bounding.bottom - bounding.top
|
|
167
|
+
? rect.top + getSide(scrollMargin, "top") - bounding.top
|
|
168
|
+
: rect.bottom - bounding.bottom + getSide(scrollMargin, "bottom");
|
|
167
169
|
if (rect.left < bounding.left + getSide(scrollThreshold, "left"))
|
|
168
170
|
moveX = -(bounding.left - rect.left + getSide(scrollMargin, "left"));
|
|
169
171
|
else if (rect.right > bounding.right - getSide(scrollThreshold, "right"))
|
|
@@ -182,7 +184,7 @@ function scrollRectIntoView(view, rect, startDOM) {
|
|
|
182
184
|
rect = { left: rect.left - dX, top: rect.top - dY, right: rect.right - dX, bottom: rect.bottom - dY };
|
|
183
185
|
}
|
|
184
186
|
}
|
|
185
|
-
if (atTop)
|
|
187
|
+
if (atTop || /^(fixed|sticky)$/.test(getComputedStyle(parent).position))
|
|
186
188
|
break;
|
|
187
189
|
}
|
|
188
190
|
}
|
|
@@ -356,7 +358,7 @@ function posFromCaret(view, node, offset, coords) {
|
|
|
356
358
|
else if (rect.right < coords.left || rect.bottom < coords.top)
|
|
357
359
|
outsideBlock = desc.posAfter;
|
|
358
360
|
}
|
|
359
|
-
if (!desc.contentDOM && outsideBlock < 0) {
|
|
361
|
+
if (!desc.contentDOM && outsideBlock < 0 && !desc.node.isText) {
|
|
360
362
|
// If we are inside a leaf, return the side of the leaf closer to the coords
|
|
361
363
|
let before = desc.node.isBlock ? coords.top < (rect.top + rect.bottom) / 2
|
|
362
364
|
: coords.left < (rect.left + rect.right) / 2;
|
|
@@ -1145,7 +1147,7 @@ class MarkViewDesc extends ViewDesc {
|
|
|
1145
1147
|
parseRule() {
|
|
1146
1148
|
if ((this.dirty & NODE_DIRTY) || this.mark.type.spec.reparseInView)
|
|
1147
1149
|
return null;
|
|
1148
|
-
return { mark: this.mark.type.name, attrs: this.mark.attrs, contentElement: this.contentDOM
|
|
1150
|
+
return { mark: this.mark.type.name, attrs: this.mark.attrs, contentElement: this.contentDOM };
|
|
1149
1151
|
}
|
|
1150
1152
|
matchesMark(mark) { return this.dirty != NODE_DIRTY && this.mark.eq(mark); }
|
|
1151
1153
|
markDirty(from, to) {
|
|
@@ -2354,6 +2356,8 @@ function nodeLen(node) {
|
|
|
2354
2356
|
return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
|
|
2355
2357
|
}
|
|
2356
2358
|
function isIgnorable(dom) {
|
|
2359
|
+
if (dom.contentEditable == "false")
|
|
2360
|
+
return true;
|
|
2357
2361
|
let desc = dom.pmViewDesc;
|
|
2358
2362
|
return desc && desc.size == 0 && (dom.nextSibling || dom.nodeName != "BR");
|
|
2359
2363
|
}
|
|
@@ -2470,7 +2474,42 @@ function isBlockNode(dom) {
|
|
|
2470
2474
|
let desc = dom.pmViewDesc;
|
|
2471
2475
|
return desc && desc.node && desc.node.isBlock;
|
|
2472
2476
|
}
|
|
2477
|
+
function textNodeAfter(node, offset) {
|
|
2478
|
+
while (node && offset == node.childNodes.length && !hasBlockDesc(node)) {
|
|
2479
|
+
offset = domIndex(node) + 1;
|
|
2480
|
+
node = node.parentNode;
|
|
2481
|
+
}
|
|
2482
|
+
while (node && offset < node.childNodes.length) {
|
|
2483
|
+
node = node.childNodes[offset];
|
|
2484
|
+
if (node.nodeType == 3)
|
|
2485
|
+
return node;
|
|
2486
|
+
offset = 0;
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
function textNodeBefore(node, offset) {
|
|
2490
|
+
while (node && !offset && !hasBlockDesc(node)) {
|
|
2491
|
+
offset = domIndex(node);
|
|
2492
|
+
node = node.parentNode;
|
|
2493
|
+
}
|
|
2494
|
+
while (node && offset) {
|
|
2495
|
+
node = node.childNodes[offset - 1];
|
|
2496
|
+
if (node.nodeType == 3)
|
|
2497
|
+
return node;
|
|
2498
|
+
offset = node.childNodes.length;
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2473
2501
|
function setSelFocus(view, node, offset) {
|
|
2502
|
+
if (node.nodeType != 3) {
|
|
2503
|
+
let before, after;
|
|
2504
|
+
if (after = textNodeAfter(node, offset)) {
|
|
2505
|
+
node = after;
|
|
2506
|
+
offset = 0;
|
|
2507
|
+
}
|
|
2508
|
+
else if (before = textNodeBefore(node, offset)) {
|
|
2509
|
+
node = before;
|
|
2510
|
+
offset = before.nodeValue.length;
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2474
2513
|
let sel = view.domSelection();
|
|
2475
2514
|
if (selectionCollapsed(sel)) {
|
|
2476
2515
|
let range = document.createRange();
|
|
@@ -2899,6 +2938,8 @@ class InputState {
|
|
|
2899
2938
|
this.compositionNodes = [];
|
|
2900
2939
|
this.compositionEndedAt = -2e8;
|
|
2901
2940
|
this.compositionID = 1;
|
|
2941
|
+
// Set to a composition ID when there are pending changes at compositionend
|
|
2942
|
+
this.compositionPendingChanges = 0;
|
|
2902
2943
|
this.domChangeCount = 0;
|
|
2903
2944
|
this.eventHandlers = Object.create(null);
|
|
2904
2945
|
this.hideSelectionGuard = null;
|
|
@@ -3328,6 +3369,9 @@ editHandlers.compositionend = (view, event) => {
|
|
|
3328
3369
|
if (view.composing) {
|
|
3329
3370
|
view.input.composing = false;
|
|
3330
3371
|
view.input.compositionEndedAt = event.timeStamp;
|
|
3372
|
+
view.input.compositionPendingChanges = view.domObserver.pendingRecords().length ? view.input.compositionID : 0;
|
|
3373
|
+
if (view.input.compositionPendingChanges)
|
|
3374
|
+
Promise.resolve().then(() => view.domObserver.flush());
|
|
3331
3375
|
view.input.compositionID++;
|
|
3332
3376
|
scheduleComposeEnd(view, 20);
|
|
3333
3377
|
}
|
|
@@ -4432,15 +4476,19 @@ class DOMObserver {
|
|
|
4432
4476
|
return true;
|
|
4433
4477
|
}
|
|
4434
4478
|
}
|
|
4479
|
+
pendingRecords() {
|
|
4480
|
+
if (this.observer)
|
|
4481
|
+
for (let mut of this.observer.takeRecords())
|
|
4482
|
+
this.queue.push(mut);
|
|
4483
|
+
return this.queue;
|
|
4484
|
+
}
|
|
4435
4485
|
flush() {
|
|
4436
4486
|
let { view } = this;
|
|
4437
4487
|
if (!view.docView || this.flushingSoon > -1)
|
|
4438
4488
|
return;
|
|
4439
|
-
let mutations = this.
|
|
4440
|
-
if (
|
|
4441
|
-
|
|
4442
|
-
this.queue.length = 0;
|
|
4443
|
-
}
|
|
4489
|
+
let mutations = this.pendingRecords();
|
|
4490
|
+
if (mutations.length)
|
|
4491
|
+
this.queue = [];
|
|
4444
4492
|
let sel = view.domSelectionRange();
|
|
4445
4493
|
let newSel = !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(view) && !this.ignoreSelectionChange(sel);
|
|
4446
4494
|
let from = -1, to = -1, typeOver = false, added = [];
|
|
@@ -4661,6 +4709,8 @@ function ruleFromNode(dom) {
|
|
|
4661
4709
|
}
|
|
4662
4710
|
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;
|
|
4663
4711
|
function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
4712
|
+
let compositionID = view.input.compositionPendingChanges || (view.composing ? view.input.compositionID : 0);
|
|
4713
|
+
view.input.compositionPendingChanges = 0;
|
|
4664
4714
|
if (from < 0) {
|
|
4665
4715
|
let origin = view.input.lastSelectionTime > Date.now() - 50 ? view.input.lastSelectionOrigin : null;
|
|
4666
4716
|
let newSel = selectionFromDOM(view, origin);
|
|
@@ -4674,8 +4724,8 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
4674
4724
|
tr.setMeta("pointer", true);
|
|
4675
4725
|
else if (origin == "key")
|
|
4676
4726
|
tr.scrollIntoView();
|
|
4677
|
-
if (
|
|
4678
|
-
tr.setMeta("composition",
|
|
4727
|
+
if (compositionID)
|
|
4728
|
+
tr.setMeta("composition", compositionID);
|
|
4679
4729
|
view.dispatch(tr);
|
|
4680
4730
|
}
|
|
4681
4731
|
return;
|
|
@@ -4716,8 +4766,8 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
4716
4766
|
let sel = resolveSelection(view, view.state.doc, parse.sel);
|
|
4717
4767
|
if (sel && !sel.eq(view.state.selection)) {
|
|
4718
4768
|
let tr = view.state.tr.setSelection(sel);
|
|
4719
|
-
if (
|
|
4720
|
-
tr.setMeta("composition",
|
|
4769
|
+
if (compositionID)
|
|
4770
|
+
tr.setMeta("composition", compositionID);
|
|
4721
4771
|
view.dispatch(tr);
|
|
4722
4772
|
}
|
|
4723
4773
|
}
|
|
@@ -4850,8 +4900,8 @@ function readDOMChange(view, from, to, typeOver, addedNodes) {
|
|
|
4850
4900
|
}
|
|
4851
4901
|
if (storedMarks)
|
|
4852
4902
|
tr.ensureMarks(storedMarks);
|
|
4853
|
-
if (
|
|
4854
|
-
tr.setMeta("composition",
|
|
4903
|
+
if (compositionID)
|
|
4904
|
+
tr.setMeta("composition", compositionID);
|
|
4855
4905
|
view.dispatch(tr.scrollIntoView());
|
|
4856
4906
|
}
|
|
4857
4907
|
function resolveSelection(view, doc, parsedSel) {
|
package/package.json
CHANGED
package/src/capturekeys.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {Selection, NodeSelection, TextSelection, AllSelection, EditorState} from "prosemirror-state"
|
|
2
2
|
import {EditorView} from "./index"
|
|
3
3
|
import * as browser from "./browser"
|
|
4
|
-
import {domIndex, selectionCollapsed} from "./dom"
|
|
4
|
+
import {domIndex, selectionCollapsed, hasBlockDesc} from "./dom"
|
|
5
5
|
import {selectionToDOM} from "./selection"
|
|
6
6
|
|
|
7
7
|
function moveSelectionBlock(state: EditorState, dir: number) {
|
|
@@ -55,6 +55,7 @@ function nodeLen(node: Node) {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
function isIgnorable(dom: Node) {
|
|
58
|
+
if ((dom as HTMLElement).contentEditable == "false") return true
|
|
58
59
|
let desc = dom.pmViewDesc
|
|
59
60
|
return desc && desc.size == 0 && (dom.nextSibling || dom.nodeName != "BR")
|
|
60
61
|
}
|
|
@@ -156,7 +157,42 @@ function isBlockNode(dom: Node) {
|
|
|
156
157
|
return desc && desc.node && desc.node.isBlock
|
|
157
158
|
}
|
|
158
159
|
|
|
160
|
+
function textNodeAfter(node: Node | null, offset: number): Text | undefined {
|
|
161
|
+
while (node && offset == node.childNodes.length && !hasBlockDesc(node)) {
|
|
162
|
+
offset = domIndex(node) + 1
|
|
163
|
+
node = node.parentNode
|
|
164
|
+
}
|
|
165
|
+
while (node && offset < node.childNodes.length) {
|
|
166
|
+
node = node.childNodes[offset]
|
|
167
|
+
if (node.nodeType == 3) return node as Text
|
|
168
|
+
offset = 0
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function textNodeBefore(node: Node | null, offset: number): Text | undefined {
|
|
173
|
+
while (node && !offset && !hasBlockDesc(node)) {
|
|
174
|
+
offset = domIndex(node)
|
|
175
|
+
node = node.parentNode
|
|
176
|
+
}
|
|
177
|
+
while (node && offset) {
|
|
178
|
+
node = node.childNodes[offset - 1]
|
|
179
|
+
if (node.nodeType == 3) return node as Text
|
|
180
|
+
offset = node.childNodes.length
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
159
184
|
function setSelFocus(view: EditorView, node: Node, offset: number) {
|
|
185
|
+
if (node.nodeType != 3) {
|
|
186
|
+
let before, after
|
|
187
|
+
if (after = textNodeAfter(node, offset)) {
|
|
188
|
+
node = after
|
|
189
|
+
offset = 0
|
|
190
|
+
} else if (before = textNodeBefore(node, offset)) {
|
|
191
|
+
node = before
|
|
192
|
+
offset = before.nodeValue!.length
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
160
196
|
let sel = view.domSelection()
|
|
161
197
|
if (selectionCollapsed(sel)) {
|
|
162
198
|
let range = document.createRange()
|
package/src/dom.ts
CHANGED
|
@@ -74,7 +74,7 @@ export function isOnEdge(node: Node, offset: number, parent: Node) {
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
function hasBlockDesc(dom: Node) {
|
|
77
|
+
export function hasBlockDesc(dom: Node) {
|
|
78
78
|
let desc
|
|
79
79
|
for (let cur: Node | null = dom; cur; cur = cur.parentNode) if (desc = cur.pmViewDesc) break
|
|
80
80
|
return desc && desc.node && desc.node.isBlock && (desc.dom == dom || desc.contentDOM == dom)
|
package/src/domchange.ts
CHANGED
|
@@ -79,6 +79,9 @@ function ruleFromNode(dom: DOMNode): ParseRule | null {
|
|
|
79
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
80
|
|
|
81
81
|
export function readDOMChange(view: EditorView, from: number, to: number, typeOver: boolean, addedNodes: readonly DOMNode[]) {
|
|
82
|
+
let compositionID = view.input.compositionPendingChanges || (view.composing ? view.input.compositionID : 0)
|
|
83
|
+
view.input.compositionPendingChanges = 0
|
|
84
|
+
|
|
82
85
|
if (from < 0) {
|
|
83
86
|
let origin = view.input.lastSelectionTime > Date.now() - 50 ? view.input.lastSelectionOrigin : null
|
|
84
87
|
let newSel = selectionFromDOM(view, origin)
|
|
@@ -90,7 +93,7 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
|
|
|
90
93
|
let tr = view.state.tr.setSelection(newSel)
|
|
91
94
|
if (origin == "pointer") tr.setMeta("pointer", true)
|
|
92
95
|
else if (origin == "key") tr.scrollIntoView()
|
|
93
|
-
if (
|
|
96
|
+
if (compositionID) tr.setMeta("composition", compositionID)
|
|
94
97
|
view.dispatch(tr)
|
|
95
98
|
}
|
|
96
99
|
return
|
|
@@ -133,7 +136,7 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
|
|
|
133
136
|
let sel = resolveSelection(view, view.state.doc, parse.sel)
|
|
134
137
|
if (sel && !sel.eq(view.state.selection)) {
|
|
135
138
|
let tr = view.state.tr.setSelection(sel)
|
|
136
|
-
if (
|
|
139
|
+
if (compositionID) tr.setMeta("composition", compositionID)
|
|
137
140
|
view.dispatch(tr)
|
|
138
141
|
}
|
|
139
142
|
}
|
|
@@ -268,7 +271,7 @@ export function readDOMChange(view: EditorView, from: number, to: number, typeOv
|
|
|
268
271
|
tr.setSelection(sel)
|
|
269
272
|
}
|
|
270
273
|
if (storedMarks) tr.ensureMarks(storedMarks)
|
|
271
|
-
if (
|
|
274
|
+
if (compositionID) tr.setMeta("composition", compositionID)
|
|
272
275
|
view.dispatch(tr.scrollIntoView())
|
|
273
276
|
}
|
|
274
277
|
|
package/src/domcoords.ts
CHANGED
|
@@ -37,7 +37,9 @@ export function scrollRectIntoView(view: EditorView, rect: Rect, startDOM: Node)
|
|
|
37
37
|
if (rect.top < bounding.top + getSide(scrollThreshold, "top"))
|
|
38
38
|
moveY = -(bounding.top - rect.top + getSide(scrollMargin, "top"))
|
|
39
39
|
else if (rect.bottom > bounding.bottom - getSide(scrollThreshold, "bottom"))
|
|
40
|
-
moveY = rect.bottom - bounding.bottom
|
|
40
|
+
moveY = rect.bottom - rect.top > bounding.bottom - bounding.top
|
|
41
|
+
? rect.top + getSide(scrollMargin, "top") - bounding.top
|
|
42
|
+
: rect.bottom - bounding.bottom + getSide(scrollMargin, "bottom")
|
|
41
43
|
if (rect.left < bounding.left + getSide(scrollThreshold, "left"))
|
|
42
44
|
moveX = -(bounding.left - rect.left + getSide(scrollMargin, "left"))
|
|
43
45
|
else if (rect.right > bounding.right - getSide(scrollThreshold, "right"))
|
|
@@ -53,7 +55,7 @@ export function scrollRectIntoView(view: EditorView, rect: Rect, startDOM: Node)
|
|
|
53
55
|
rect = {left: rect.left - dX, top: rect.top - dY, right: rect.right - dX, bottom: rect.bottom - dY}
|
|
54
56
|
}
|
|
55
57
|
}
|
|
56
|
-
if (atTop) break
|
|
58
|
+
if (atTop || /^(fixed|sticky)$/.test(getComputedStyle(parent as HTMLElement).position)) break
|
|
57
59
|
}
|
|
58
60
|
}
|
|
59
61
|
|
|
@@ -227,7 +229,7 @@ function posFromCaret(view: EditorView, node: Node, offset: number, coords: {top
|
|
|
227
229
|
if (rect.left > coords.left || rect.top > coords.top) outsideBlock = desc.posBefore
|
|
228
230
|
else if (rect.right < coords.left || rect.bottom < coords.top) outsideBlock = desc.posAfter
|
|
229
231
|
}
|
|
230
|
-
if (!desc.contentDOM && outsideBlock < 0) {
|
|
232
|
+
if (!desc.contentDOM && outsideBlock < 0 && !desc.node.isText) {
|
|
231
233
|
// If we are inside a leaf, return the side of the leaf closer to the coords
|
|
232
234
|
let before = desc.node.isBlock ? coords.top < (rect.top + rect.bottom) / 2
|
|
233
235
|
: coords.left < (rect.left + rect.right) / 2
|
package/src/domobserver.ts
CHANGED
|
@@ -157,14 +157,16 @@ export class DOMObserver {
|
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
pendingRecords() {
|
|
161
|
+
if (this.observer) for (let mut of this.observer.takeRecords()) this.queue.push(mut)
|
|
162
|
+
return this.queue
|
|
163
|
+
}
|
|
164
|
+
|
|
160
165
|
flush() {
|
|
161
166
|
let {view} = this
|
|
162
167
|
if (!view.docView || this.flushingSoon > -1) return
|
|
163
|
-
let mutations = this.
|
|
164
|
-
if (
|
|
165
|
-
mutations = this.queue.concat(mutations)
|
|
166
|
-
this.queue.length = 0
|
|
167
|
-
}
|
|
168
|
+
let mutations = this.pendingRecords()
|
|
169
|
+
if (mutations.length) this.queue = []
|
|
168
170
|
|
|
169
171
|
let sel = view.domSelectionRange()
|
|
170
172
|
let newSel = !this.suppressingSelectionUpdates && !this.currentSelection.eq(sel) && hasFocusAndSelection(view) && !this.ignoreSelectionChange(sel)
|
package/src/input.ts
CHANGED
|
@@ -34,6 +34,8 @@ export class InputState {
|
|
|
34
34
|
compositionNodes: ViewDesc[] = []
|
|
35
35
|
compositionEndedAt = -2e8
|
|
36
36
|
compositionID = 1
|
|
37
|
+
// Set to a composition ID when there are pending changes at compositionend
|
|
38
|
+
compositionPendingChanges = 0
|
|
37
39
|
domChangeCount = 0
|
|
38
40
|
eventHandlers: {[event: string]: (event: Event) => void} = Object.create(null)
|
|
39
41
|
hideSelectionGuard: (() => void) | null = null
|
|
@@ -487,6 +489,8 @@ editHandlers.compositionend = (view, event) => {
|
|
|
487
489
|
if (view.composing) {
|
|
488
490
|
view.input.composing = false
|
|
489
491
|
view.input.compositionEndedAt = event.timeStamp
|
|
492
|
+
view.input.compositionPendingChanges = view.domObserver.pendingRecords().length ? view.input.compositionID : 0
|
|
493
|
+
if (view.input.compositionPendingChanges) Promise.resolve().then(() => view.domObserver.flush())
|
|
490
494
|
view.input.compositionID++
|
|
491
495
|
scheduleComposeEnd(view, 20)
|
|
492
496
|
}
|
package/src/viewdesc.ts
CHANGED
|
@@ -562,7 +562,7 @@ class CompositionViewDesc extends ViewDesc {
|
|
|
562
562
|
// some cases they will be split more often than would appear
|
|
563
563
|
// necessary.
|
|
564
564
|
class MarkViewDesc extends ViewDesc {
|
|
565
|
-
constructor(parent: ViewDesc, readonly mark: Mark, dom: DOMNode, contentDOM: HTMLElement
|
|
565
|
+
constructor(parent: ViewDesc, readonly mark: Mark, dom: DOMNode, contentDOM: HTMLElement) {
|
|
566
566
|
super(parent, [], dom, contentDOM)
|
|
567
567
|
}
|
|
568
568
|
|
|
@@ -576,7 +576,7 @@ class MarkViewDesc extends ViewDesc {
|
|
|
576
576
|
|
|
577
577
|
parseRule(): ParseRule | null {
|
|
578
578
|
if ((this.dirty & NODE_DIRTY) || this.mark.type.spec.reparseInView) return null
|
|
579
|
-
return {mark: this.mark.type.name, attrs: this.mark.attrs, contentElement: this.contentDOM
|
|
579
|
+
return {mark: this.mark.type.name, attrs: this.mark.attrs, contentElement: this.contentDOM!}
|
|
580
580
|
}
|
|
581
581
|
|
|
582
582
|
matchesMark(mark: Mark) { return this.dirty != NODE_DIRTY && this.mark.eq(mark) }
|