@codemirror/view 6.9.5 → 6.10.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 +57 -58
- package/dist/index.d.ts +19 -11
- package/dist/index.js +57 -58
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
## 6.10.0 (2023-04-25)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix a crash in `drawSelection` when a measured position falls on a position that doesn't have corresponding screen coordinates.
|
|
6
|
+
|
|
7
|
+
Work around unhelpful interaction observer behavior that could cause the editor to not notice it was visible.
|
|
8
|
+
|
|
9
|
+
Give the cursor next to a line-wrapped placeholder a single-line height.
|
|
10
|
+
|
|
11
|
+
Make sure drop events below the editable element in a fixed-height editor get handled properly.
|
|
12
|
+
|
|
13
|
+
### New features
|
|
14
|
+
|
|
15
|
+
Widget decorations can now define custom `coordsAtPos` methods to control the way the editor computes screen positions at or in the widget.
|
|
16
|
+
|
|
17
|
+
## 6.9.6 (2023-04-21)
|
|
18
|
+
|
|
19
|
+
### Bug fixes
|
|
20
|
+
|
|
21
|
+
Fix an issue where, when escape was pressed followed by a key that the editor handled, followed by tab, the tab would still move focus.
|
|
22
|
+
|
|
23
|
+
Fix an issue where, in some circumstances, the editor would ignore text changes at the end of a composition.
|
|
24
|
+
|
|
25
|
+
Allow inline widgets to be updated to a different length via `updateDOM`.
|
|
26
|
+
|
|
1
27
|
## 6.9.5 (2023-04-17)
|
|
2
28
|
|
|
3
29
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -90,7 +90,6 @@ function scanFor(node, off, targetNode, targetOff, dir) {
|
|
|
90
90
|
function maxOffset(node) {
|
|
91
91
|
return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
|
|
92
92
|
}
|
|
93
|
-
const Rect0 = { left: 0, right: 0, top: 0, bottom: 0 };
|
|
94
93
|
function flattenRect(rect, left) {
|
|
95
94
|
let x = left ? rect.left : rect.right;
|
|
96
95
|
return { left: x, right: x, top: rect.top, bottom: rect.bottom };
|
|
@@ -353,10 +352,6 @@ class ContentView {
|
|
|
353
352
|
posAfter(view) {
|
|
354
353
|
return this.posBefore(view) + view.length;
|
|
355
354
|
}
|
|
356
|
-
// Will return a rectangle directly before (when side < 0), after
|
|
357
|
-
// (side > 0) or directly on (when the browser supports it) the
|
|
358
|
-
// given position.
|
|
359
|
-
coordsAt(_pos, _side) { return null; }
|
|
360
355
|
sync(view, track) {
|
|
361
356
|
if (this.dirty & 2 /* Dirty.Node */) {
|
|
362
357
|
let parent = this.dom;
|
|
@@ -815,7 +810,7 @@ function textCoords(text, pos, side) {
|
|
|
815
810
|
}
|
|
816
811
|
let rects = textRange(text, from, to).getClientRects();
|
|
817
812
|
if (!rects.length)
|
|
818
|
-
return
|
|
813
|
+
return null;
|
|
819
814
|
let rect = rects[(flatten ? flatten < 0 : side >= 0) ? 0 : rects.length - 1];
|
|
820
815
|
if (browser.safari && !flatten && rect.width == 0)
|
|
821
816
|
rect = Array.prototype.find.call(rects, r => r.width) || rect;
|
|
@@ -856,15 +851,15 @@ class WidgetView extends ContentView {
|
|
|
856
851
|
return true;
|
|
857
852
|
}
|
|
858
853
|
become(other) {
|
|
859
|
-
if (other
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
854
|
+
if (other instanceof WidgetView && other.side == this.side &&
|
|
855
|
+
this.widget.constructor == other.widget.constructor) {
|
|
856
|
+
if (!this.widget.compare(other.widget))
|
|
857
|
+
this.markDirty(true);
|
|
858
|
+
if (this.dom && !this.prevWidget)
|
|
859
|
+
this.prevWidget = this.widget;
|
|
860
|
+
this.widget = other.widget;
|
|
861
|
+
this.length = other.length;
|
|
862
|
+
return true;
|
|
868
863
|
}
|
|
869
864
|
return false;
|
|
870
865
|
}
|
|
@@ -886,9 +881,12 @@ class WidgetView extends ContentView {
|
|
|
886
881
|
}
|
|
887
882
|
domBoundsAround() { return null; }
|
|
888
883
|
coordsAt(pos, side) {
|
|
884
|
+
let custom = this.widget.coordsAt(this.dom, pos, side);
|
|
885
|
+
if (custom)
|
|
886
|
+
return custom;
|
|
889
887
|
let rects = this.dom.getClientRects(), rect = null;
|
|
890
888
|
if (!rects.length)
|
|
891
|
-
return
|
|
889
|
+
return null;
|
|
892
890
|
for (let i = pos > 0 ? rects.length - 1 : 0;; i += (pos > 0 ? -1 : 1)) {
|
|
893
891
|
rect = rects[i];
|
|
894
892
|
if (pos > 0 ? i == 0 : i == rects.length - 1 || rect.top < rect.bottom)
|
|
@@ -1055,12 +1053,7 @@ class WidgetBufferView extends ContentView {
|
|
|
1055
1053
|
localPosFromDOM() { return 0; }
|
|
1056
1054
|
domBoundsAround() { return null; }
|
|
1057
1055
|
coordsAt(pos) {
|
|
1058
|
-
|
|
1059
|
-
// Since the <img> height doesn't correspond to text height, try
|
|
1060
|
-
// to borrow the height from some sibling node.
|
|
1061
|
-
let siblingRect = inlineSiblingRect(this, this.side > 0 ? -1 : 1);
|
|
1062
|
-
return siblingRect && siblingRect.top < imgRect.bottom && siblingRect.bottom > imgRect.top
|
|
1063
|
-
? { left: imgRect.left, right: imgRect.right, top: siblingRect.top, bottom: siblingRect.bottom } : imgRect;
|
|
1056
|
+
return this.dom.getBoundingClientRect();
|
|
1064
1057
|
}
|
|
1065
1058
|
get overrideDOMText() {
|
|
1066
1059
|
return state.Text.empty;
|
|
@@ -1068,31 +1061,6 @@ class WidgetBufferView extends ContentView {
|
|
|
1068
1061
|
get isHidden() { return true; }
|
|
1069
1062
|
}
|
|
1070
1063
|
TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
|
|
1071
|
-
function inlineSiblingRect(view, side) {
|
|
1072
|
-
let parent = view.parent, index = parent ? parent.children.indexOf(view) : -1;
|
|
1073
|
-
while (parent && index >= 0) {
|
|
1074
|
-
if (side < 0 ? index > 0 : index < parent.children.length) {
|
|
1075
|
-
let next = parent.children[index + side];
|
|
1076
|
-
if (next instanceof TextView) {
|
|
1077
|
-
let nextRect = next.coordsAt(side < 0 ? next.length : 0, side);
|
|
1078
|
-
if (nextRect)
|
|
1079
|
-
return nextRect;
|
|
1080
|
-
}
|
|
1081
|
-
index += side;
|
|
1082
|
-
}
|
|
1083
|
-
else if (parent instanceof MarkView && parent.parent) {
|
|
1084
|
-
index = parent.parent.children.indexOf(parent) + (side < 0 ? 0 : 1);
|
|
1085
|
-
parent = parent.parent;
|
|
1086
|
-
}
|
|
1087
|
-
else {
|
|
1088
|
-
let last = parent.dom.lastChild;
|
|
1089
|
-
if (last && last.nodeName == "BR")
|
|
1090
|
-
return last.getClientRects()[0];
|
|
1091
|
-
break;
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
return undefined;
|
|
1095
|
-
}
|
|
1096
1064
|
function inlineDOMAtPos(parent, pos) {
|
|
1097
1065
|
let dom = parent.dom, { children } = parent, i = 0;
|
|
1098
1066
|
for (let off = 0; i < children.length; i++) {
|
|
@@ -1139,12 +1107,12 @@ function coordsInChildren(view, pos, side) {
|
|
|
1139
1107
|
if (child.children.length) {
|
|
1140
1108
|
scan(child, pos - off);
|
|
1141
1109
|
}
|
|
1142
|
-
else if ((!after || after
|
|
1110
|
+
else if ((!after || after.isHidden && side > 0) &&
|
|
1143
1111
|
(end > pos || off == end && child.getSide() > 0)) {
|
|
1144
1112
|
after = child;
|
|
1145
1113
|
afterPos = pos - off;
|
|
1146
1114
|
}
|
|
1147
|
-
else if (off < pos || (off == end && child.getSide() < 0)) {
|
|
1115
|
+
else if (off < pos || (off == end && child.getSide() < 0) && !child.isHidden) {
|
|
1148
1116
|
before = child;
|
|
1149
1117
|
beforePos = pos - off;
|
|
1150
1118
|
}
|
|
@@ -1250,6 +1218,14 @@ class WidgetType {
|
|
|
1250
1218
|
*/
|
|
1251
1219
|
ignoreEvent(event) { return true; }
|
|
1252
1220
|
/**
|
|
1221
|
+
Override the way screen coordinates for positions at/in the
|
|
1222
|
+
widget are found. `pos` will be the offset into the widget, and
|
|
1223
|
+
`side` the side of the position that is being queried—less than
|
|
1224
|
+
zero for before, greater than zero for after, and zero for
|
|
1225
|
+
directly at that position.
|
|
1226
|
+
*/
|
|
1227
|
+
coordsAt(dom, pos, side) { return null; }
|
|
1228
|
+
/**
|
|
1253
1229
|
@internal
|
|
1254
1230
|
*/
|
|
1255
1231
|
get customView() { return null; }
|
|
@@ -1684,6 +1660,9 @@ class BlockWidgetView extends ContentView {
|
|
|
1684
1660
|
ignoreEvent(event) { return this.widget.ignoreEvent(event); }
|
|
1685
1661
|
get isEditable() { return false; }
|
|
1686
1662
|
get isWidget() { return true; }
|
|
1663
|
+
coordsAt(pos, side) {
|
|
1664
|
+
return this.widget.coordsAt(this.dom, pos, side);
|
|
1665
|
+
}
|
|
1687
1666
|
destroy() {
|
|
1688
1667
|
super.destroy();
|
|
1689
1668
|
if (this.dom)
|
|
@@ -3551,6 +3530,10 @@ class InputState {
|
|
|
3551
3530
|
}
|
|
3552
3531
|
}
|
|
3553
3532
|
});
|
|
3533
|
+
view.scrollDOM.addEventListener("drop", (event) => {
|
|
3534
|
+
if (event.target == view.scrollDOM && event.clientY > view.contentDOM.getBoundingClientRect().bottom)
|
|
3535
|
+
handleEvent(handlers.drop, event);
|
|
3536
|
+
});
|
|
3554
3537
|
if (browser.chrome && browser.chrome_version == 102) { // FIXME remove at some point
|
|
3555
3538
|
// On Chrome 102, viewport updates somehow stop wheel-based
|
|
3556
3539
|
// scrolling. Turning off pointer events during the scroll seems
|
|
@@ -3631,6 +3614,8 @@ class InputState {
|
|
|
3631
3614
|
this.lastKeyTime = Date.now();
|
|
3632
3615
|
if (event.keyCode == 9 && Date.now() < this.lastEscPress + 2000)
|
|
3633
3616
|
return true;
|
|
3617
|
+
if (event.keyCode != 27 && modifierCodes.indexOf(event.keyCode) < 0)
|
|
3618
|
+
view.inputState.lastEscPress = 0;
|
|
3634
3619
|
// Chrome for Android usually doesn't fire proper key events, but
|
|
3635
3620
|
// occasionally does, usually surrounded by a bunch of complicated
|
|
3636
3621
|
// composition changes. When an enter or backspace key event is
|
|
@@ -3900,8 +3885,6 @@ handlers.keydown = (view, event) => {
|
|
|
3900
3885
|
view.inputState.setSelectionOrigin("select");
|
|
3901
3886
|
if (event.keyCode == 27)
|
|
3902
3887
|
view.inputState.lastEscPress = Date.now();
|
|
3903
|
-
else if (modifierCodes.indexOf(event.keyCode) < 0)
|
|
3904
|
-
view.inputState.lastEscPress = 0;
|
|
3905
3888
|
};
|
|
3906
3889
|
handlers.touchstart = (view, e) => {
|
|
3907
3890
|
view.inputState.lastTouchTime = Date.now();
|
|
@@ -4203,13 +4186,23 @@ handlers.compositionend = view => {
|
|
|
4203
4186
|
view.inputState.compositionPendingKey = true;
|
|
4204
4187
|
view.inputState.compositionPendingChange = view.observer.pendingRecords().length > 0;
|
|
4205
4188
|
view.inputState.compositionFirstChange = null;
|
|
4206
|
-
if (browser.chrome && browser.android)
|
|
4189
|
+
if (browser.chrome && browser.android) {
|
|
4190
|
+
// Delay flushing for a bit on Android because it'll often fire a
|
|
4191
|
+
// bunch of contradictory changes in a row at end of compositon
|
|
4207
4192
|
view.observer.flushSoon();
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
}
|
|
4193
|
+
}
|
|
4194
|
+
else if (view.inputState.compositionPendingChange) {
|
|
4195
|
+
// If we found pending records, schedule a flush.
|
|
4196
|
+
Promise.resolve().then(() => view.observer.flush());
|
|
4197
|
+
}
|
|
4198
|
+
else {
|
|
4199
|
+
// Otherwise, make sure that, if no changes come in soon, the
|
|
4200
|
+
// composition view is cleared.
|
|
4201
|
+
setTimeout(() => {
|
|
4202
|
+
if (view.inputState.composing < 0 && view.docView.compositionDeco.size)
|
|
4203
|
+
view.update([]);
|
|
4204
|
+
}, 50);
|
|
4205
|
+
}
|
|
4213
4206
|
};
|
|
4214
4207
|
handlers.contextmenu = view => {
|
|
4215
4208
|
view.inputState.lastContextMenu = Date.now();
|
|
@@ -6057,7 +6050,7 @@ class DOMObserver {
|
|
|
6057
6050
|
if (this.intersecting != this.view.inView)
|
|
6058
6051
|
this.onScrollChanged(document.createEvent("Event"));
|
|
6059
6052
|
}
|
|
6060
|
-
}, {});
|
|
6053
|
+
}, { threshold: [0, .001] });
|
|
6061
6054
|
this.intersection.observe(this.dom);
|
|
6062
6055
|
this.gapIntersection = new IntersectionObserver(entries => {
|
|
6063
6056
|
if (entries.length > 0 && entries[entries.length - 1].intersectionRatio > 0)
|
|
@@ -7714,6 +7707,8 @@ function rectanglesForRange(view, className, range) {
|
|
|
7714
7707
|
// coordsAtPos queries, would break selection drawing.
|
|
7715
7708
|
let fromCoords = view.coordsAtPos(from, (from == line.to ? -2 : 2));
|
|
7716
7709
|
let toCoords = view.coordsAtPos(to, (to == line.from ? 2 : -2));
|
|
7710
|
+
if (!fromCoords || !toCoords)
|
|
7711
|
+
return;
|
|
7717
7712
|
top = Math.min(fromCoords.top, toCoords.top, top);
|
|
7718
7713
|
bottom = Math.max(fromCoords.bottom, toCoords.bottom, bottom);
|
|
7719
7714
|
if (dir == exports.Direction.LTR)
|
|
@@ -8355,6 +8350,10 @@ class Placeholder extends WidgetType {
|
|
|
8355
8350
|
wrap.setAttribute("aria-hidden", "true");
|
|
8356
8351
|
return wrap;
|
|
8357
8352
|
}
|
|
8353
|
+
coordsAt(dom) {
|
|
8354
|
+
let rects = dom.firstChild ? clientRectsFor(dom.firstChild) : [];
|
|
8355
|
+
return rects.length ? flattenRect(rects[0], window.getComputedStyle(dom).direction != "rtl") : null;
|
|
8356
|
+
}
|
|
8358
8357
|
ignoreEvent() { return false; }
|
|
8359
8358
|
}
|
|
8360
8359
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,17 @@ declare type Attrs = {
|
|
|
6
6
|
[name: string]: string;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
Basic rectangle type.
|
|
11
|
+
*/
|
|
12
|
+
interface Rect {
|
|
13
|
+
readonly left: number;
|
|
14
|
+
readonly right: number;
|
|
15
|
+
readonly top: number;
|
|
16
|
+
readonly bottom: number;
|
|
17
|
+
}
|
|
18
|
+
declare type ScrollStrategy = "nearest" | "start" | "end" | "center";
|
|
19
|
+
|
|
9
20
|
interface MarkDecorationSpec {
|
|
10
21
|
/**
|
|
11
22
|
Whether the mark covers its start and end position or not. This
|
|
@@ -169,6 +180,14 @@ declare abstract class WidgetType {
|
|
|
169
180
|
*/
|
|
170
181
|
ignoreEvent(event: Event): boolean;
|
|
171
182
|
/**
|
|
183
|
+
Override the way screen coordinates for positions at/in the
|
|
184
|
+
widget are found. `pos` will be the offset into the widget, and
|
|
185
|
+
`side` the side of the position that is being queried—less than
|
|
186
|
+
zero for before, greater than zero for after, and zero for
|
|
187
|
+
directly at that position.
|
|
188
|
+
*/
|
|
189
|
+
coordsAt(dom: HTMLElement, pos: number, side: number): Rect | null;
|
|
190
|
+
/**
|
|
172
191
|
This is called when the an instance of the widget is removed
|
|
173
192
|
from the editor view.
|
|
174
193
|
*/
|
|
@@ -271,17 +290,6 @@ declare abstract class Decoration extends RangeValue {
|
|
|
271
290
|
static none: DecorationSet;
|
|
272
291
|
}
|
|
273
292
|
|
|
274
|
-
/**
|
|
275
|
-
Basic rectangle type.
|
|
276
|
-
*/
|
|
277
|
-
interface Rect {
|
|
278
|
-
readonly left: number;
|
|
279
|
-
readonly right: number;
|
|
280
|
-
readonly top: number;
|
|
281
|
-
readonly bottom: number;
|
|
282
|
-
}
|
|
283
|
-
declare type ScrollStrategy = "nearest" | "start" | "end" | "center";
|
|
284
|
-
|
|
285
293
|
/**
|
|
286
294
|
Command functions are used in key bindings and other types of user
|
|
287
295
|
actions. Given an editor view, they check whether their effect can
|
package/dist/index.js
CHANGED
|
@@ -86,7 +86,6 @@ function scanFor(node, off, targetNode, targetOff, dir) {
|
|
|
86
86
|
function maxOffset(node) {
|
|
87
87
|
return node.nodeType == 3 ? node.nodeValue.length : node.childNodes.length;
|
|
88
88
|
}
|
|
89
|
-
const Rect0 = { left: 0, right: 0, top: 0, bottom: 0 };
|
|
90
89
|
function flattenRect(rect, left) {
|
|
91
90
|
let x = left ? rect.left : rect.right;
|
|
92
91
|
return { left: x, right: x, top: rect.top, bottom: rect.bottom };
|
|
@@ -349,10 +348,6 @@ class ContentView {
|
|
|
349
348
|
posAfter(view) {
|
|
350
349
|
return this.posBefore(view) + view.length;
|
|
351
350
|
}
|
|
352
|
-
// Will return a rectangle directly before (when side < 0), after
|
|
353
|
-
// (side > 0) or directly on (when the browser supports it) the
|
|
354
|
-
// given position.
|
|
355
|
-
coordsAt(_pos, _side) { return null; }
|
|
356
351
|
sync(view, track) {
|
|
357
352
|
if (this.dirty & 2 /* Dirty.Node */) {
|
|
358
353
|
let parent = this.dom;
|
|
@@ -811,7 +806,7 @@ function textCoords(text, pos, side) {
|
|
|
811
806
|
}
|
|
812
807
|
let rects = textRange(text, from, to).getClientRects();
|
|
813
808
|
if (!rects.length)
|
|
814
|
-
return
|
|
809
|
+
return null;
|
|
815
810
|
let rect = rects[(flatten ? flatten < 0 : side >= 0) ? 0 : rects.length - 1];
|
|
816
811
|
if (browser.safari && !flatten && rect.width == 0)
|
|
817
812
|
rect = Array.prototype.find.call(rects, r => r.width) || rect;
|
|
@@ -852,15 +847,15 @@ class WidgetView extends ContentView {
|
|
|
852
847
|
return true;
|
|
853
848
|
}
|
|
854
849
|
become(other) {
|
|
855
|
-
if (other
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
850
|
+
if (other instanceof WidgetView && other.side == this.side &&
|
|
851
|
+
this.widget.constructor == other.widget.constructor) {
|
|
852
|
+
if (!this.widget.compare(other.widget))
|
|
853
|
+
this.markDirty(true);
|
|
854
|
+
if (this.dom && !this.prevWidget)
|
|
855
|
+
this.prevWidget = this.widget;
|
|
856
|
+
this.widget = other.widget;
|
|
857
|
+
this.length = other.length;
|
|
858
|
+
return true;
|
|
864
859
|
}
|
|
865
860
|
return false;
|
|
866
861
|
}
|
|
@@ -882,9 +877,12 @@ class WidgetView extends ContentView {
|
|
|
882
877
|
}
|
|
883
878
|
domBoundsAround() { return null; }
|
|
884
879
|
coordsAt(pos, side) {
|
|
880
|
+
let custom = this.widget.coordsAt(this.dom, pos, side);
|
|
881
|
+
if (custom)
|
|
882
|
+
return custom;
|
|
885
883
|
let rects = this.dom.getClientRects(), rect = null;
|
|
886
884
|
if (!rects.length)
|
|
887
|
-
return
|
|
885
|
+
return null;
|
|
888
886
|
for (let i = pos > 0 ? rects.length - 1 : 0;; i += (pos > 0 ? -1 : 1)) {
|
|
889
887
|
rect = rects[i];
|
|
890
888
|
if (pos > 0 ? i == 0 : i == rects.length - 1 || rect.top < rect.bottom)
|
|
@@ -1051,12 +1049,7 @@ class WidgetBufferView extends ContentView {
|
|
|
1051
1049
|
localPosFromDOM() { return 0; }
|
|
1052
1050
|
domBoundsAround() { return null; }
|
|
1053
1051
|
coordsAt(pos) {
|
|
1054
|
-
|
|
1055
|
-
// Since the <img> height doesn't correspond to text height, try
|
|
1056
|
-
// to borrow the height from some sibling node.
|
|
1057
|
-
let siblingRect = inlineSiblingRect(this, this.side > 0 ? -1 : 1);
|
|
1058
|
-
return siblingRect && siblingRect.top < imgRect.bottom && siblingRect.bottom > imgRect.top
|
|
1059
|
-
? { left: imgRect.left, right: imgRect.right, top: siblingRect.top, bottom: siblingRect.bottom } : imgRect;
|
|
1052
|
+
return this.dom.getBoundingClientRect();
|
|
1060
1053
|
}
|
|
1061
1054
|
get overrideDOMText() {
|
|
1062
1055
|
return Text.empty;
|
|
@@ -1064,31 +1057,6 @@ class WidgetBufferView extends ContentView {
|
|
|
1064
1057
|
get isHidden() { return true; }
|
|
1065
1058
|
}
|
|
1066
1059
|
TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
|
|
1067
|
-
function inlineSiblingRect(view, side) {
|
|
1068
|
-
let parent = view.parent, index = parent ? parent.children.indexOf(view) : -1;
|
|
1069
|
-
while (parent && index >= 0) {
|
|
1070
|
-
if (side < 0 ? index > 0 : index < parent.children.length) {
|
|
1071
|
-
let next = parent.children[index + side];
|
|
1072
|
-
if (next instanceof TextView) {
|
|
1073
|
-
let nextRect = next.coordsAt(side < 0 ? next.length : 0, side);
|
|
1074
|
-
if (nextRect)
|
|
1075
|
-
return nextRect;
|
|
1076
|
-
}
|
|
1077
|
-
index += side;
|
|
1078
|
-
}
|
|
1079
|
-
else if (parent instanceof MarkView && parent.parent) {
|
|
1080
|
-
index = parent.parent.children.indexOf(parent) + (side < 0 ? 0 : 1);
|
|
1081
|
-
parent = parent.parent;
|
|
1082
|
-
}
|
|
1083
|
-
else {
|
|
1084
|
-
let last = parent.dom.lastChild;
|
|
1085
|
-
if (last && last.nodeName == "BR")
|
|
1086
|
-
return last.getClientRects()[0];
|
|
1087
|
-
break;
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
return undefined;
|
|
1091
|
-
}
|
|
1092
1060
|
function inlineDOMAtPos(parent, pos) {
|
|
1093
1061
|
let dom = parent.dom, { children } = parent, i = 0;
|
|
1094
1062
|
for (let off = 0; i < children.length; i++) {
|
|
@@ -1135,12 +1103,12 @@ function coordsInChildren(view, pos, side) {
|
|
|
1135
1103
|
if (child.children.length) {
|
|
1136
1104
|
scan(child, pos - off);
|
|
1137
1105
|
}
|
|
1138
|
-
else if ((!after || after
|
|
1106
|
+
else if ((!after || after.isHidden && side > 0) &&
|
|
1139
1107
|
(end > pos || off == end && child.getSide() > 0)) {
|
|
1140
1108
|
after = child;
|
|
1141
1109
|
afterPos = pos - off;
|
|
1142
1110
|
}
|
|
1143
|
-
else if (off < pos || (off == end && child.getSide() < 0)) {
|
|
1111
|
+
else if (off < pos || (off == end && child.getSide() < 0) && !child.isHidden) {
|
|
1144
1112
|
before = child;
|
|
1145
1113
|
beforePos = pos - off;
|
|
1146
1114
|
}
|
|
@@ -1246,6 +1214,14 @@ class WidgetType {
|
|
|
1246
1214
|
*/
|
|
1247
1215
|
ignoreEvent(event) { return true; }
|
|
1248
1216
|
/**
|
|
1217
|
+
Override the way screen coordinates for positions at/in the
|
|
1218
|
+
widget are found. `pos` will be the offset into the widget, and
|
|
1219
|
+
`side` the side of the position that is being queried—less than
|
|
1220
|
+
zero for before, greater than zero for after, and zero for
|
|
1221
|
+
directly at that position.
|
|
1222
|
+
*/
|
|
1223
|
+
coordsAt(dom, pos, side) { return null; }
|
|
1224
|
+
/**
|
|
1249
1225
|
@internal
|
|
1250
1226
|
*/
|
|
1251
1227
|
get customView() { return null; }
|
|
@@ -1679,6 +1655,9 @@ class BlockWidgetView extends ContentView {
|
|
|
1679
1655
|
ignoreEvent(event) { return this.widget.ignoreEvent(event); }
|
|
1680
1656
|
get isEditable() { return false; }
|
|
1681
1657
|
get isWidget() { return true; }
|
|
1658
|
+
coordsAt(pos, side) {
|
|
1659
|
+
return this.widget.coordsAt(this.dom, pos, side);
|
|
1660
|
+
}
|
|
1682
1661
|
destroy() {
|
|
1683
1662
|
super.destroy();
|
|
1684
1663
|
if (this.dom)
|
|
@@ -3545,6 +3524,10 @@ class InputState {
|
|
|
3545
3524
|
}
|
|
3546
3525
|
}
|
|
3547
3526
|
});
|
|
3527
|
+
view.scrollDOM.addEventListener("drop", (event) => {
|
|
3528
|
+
if (event.target == view.scrollDOM && event.clientY > view.contentDOM.getBoundingClientRect().bottom)
|
|
3529
|
+
handleEvent(handlers.drop, event);
|
|
3530
|
+
});
|
|
3548
3531
|
if (browser.chrome && browser.chrome_version == 102) { // FIXME remove at some point
|
|
3549
3532
|
// On Chrome 102, viewport updates somehow stop wheel-based
|
|
3550
3533
|
// scrolling. Turning off pointer events during the scroll seems
|
|
@@ -3625,6 +3608,8 @@ class InputState {
|
|
|
3625
3608
|
this.lastKeyTime = Date.now();
|
|
3626
3609
|
if (event.keyCode == 9 && Date.now() < this.lastEscPress + 2000)
|
|
3627
3610
|
return true;
|
|
3611
|
+
if (event.keyCode != 27 && modifierCodes.indexOf(event.keyCode) < 0)
|
|
3612
|
+
view.inputState.lastEscPress = 0;
|
|
3628
3613
|
// Chrome for Android usually doesn't fire proper key events, but
|
|
3629
3614
|
// occasionally does, usually surrounded by a bunch of complicated
|
|
3630
3615
|
// composition changes. When an enter or backspace key event is
|
|
@@ -3894,8 +3879,6 @@ handlers.keydown = (view, event) => {
|
|
|
3894
3879
|
view.inputState.setSelectionOrigin("select");
|
|
3895
3880
|
if (event.keyCode == 27)
|
|
3896
3881
|
view.inputState.lastEscPress = Date.now();
|
|
3897
|
-
else if (modifierCodes.indexOf(event.keyCode) < 0)
|
|
3898
|
-
view.inputState.lastEscPress = 0;
|
|
3899
3882
|
};
|
|
3900
3883
|
handlers.touchstart = (view, e) => {
|
|
3901
3884
|
view.inputState.lastTouchTime = Date.now();
|
|
@@ -4197,13 +4180,23 @@ handlers.compositionend = view => {
|
|
|
4197
4180
|
view.inputState.compositionPendingKey = true;
|
|
4198
4181
|
view.inputState.compositionPendingChange = view.observer.pendingRecords().length > 0;
|
|
4199
4182
|
view.inputState.compositionFirstChange = null;
|
|
4200
|
-
if (browser.chrome && browser.android)
|
|
4183
|
+
if (browser.chrome && browser.android) {
|
|
4184
|
+
// Delay flushing for a bit on Android because it'll often fire a
|
|
4185
|
+
// bunch of contradictory changes in a row at end of compositon
|
|
4201
4186
|
view.observer.flushSoon();
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
}
|
|
4187
|
+
}
|
|
4188
|
+
else if (view.inputState.compositionPendingChange) {
|
|
4189
|
+
// If we found pending records, schedule a flush.
|
|
4190
|
+
Promise.resolve().then(() => view.observer.flush());
|
|
4191
|
+
}
|
|
4192
|
+
else {
|
|
4193
|
+
// Otherwise, make sure that, if no changes come in soon, the
|
|
4194
|
+
// composition view is cleared.
|
|
4195
|
+
setTimeout(() => {
|
|
4196
|
+
if (view.inputState.composing < 0 && view.docView.compositionDeco.size)
|
|
4197
|
+
view.update([]);
|
|
4198
|
+
}, 50);
|
|
4199
|
+
}
|
|
4207
4200
|
};
|
|
4208
4201
|
handlers.contextmenu = view => {
|
|
4209
4202
|
view.inputState.lastContextMenu = Date.now();
|
|
@@ -6050,7 +6043,7 @@ class DOMObserver {
|
|
|
6050
6043
|
if (this.intersecting != this.view.inView)
|
|
6051
6044
|
this.onScrollChanged(document.createEvent("Event"));
|
|
6052
6045
|
}
|
|
6053
|
-
}, {});
|
|
6046
|
+
}, { threshold: [0, .001] });
|
|
6054
6047
|
this.intersection.observe(this.dom);
|
|
6055
6048
|
this.gapIntersection = new IntersectionObserver(entries => {
|
|
6056
6049
|
if (entries.length > 0 && entries[entries.length - 1].intersectionRatio > 0)
|
|
@@ -7707,6 +7700,8 @@ function rectanglesForRange(view, className, range) {
|
|
|
7707
7700
|
// coordsAtPos queries, would break selection drawing.
|
|
7708
7701
|
let fromCoords = view.coordsAtPos(from, (from == line.to ? -2 : 2));
|
|
7709
7702
|
let toCoords = view.coordsAtPos(to, (to == line.from ? 2 : -2));
|
|
7703
|
+
if (!fromCoords || !toCoords)
|
|
7704
|
+
return;
|
|
7710
7705
|
top = Math.min(fromCoords.top, toCoords.top, top);
|
|
7711
7706
|
bottom = Math.max(fromCoords.bottom, toCoords.bottom, bottom);
|
|
7712
7707
|
if (dir == Direction.LTR)
|
|
@@ -8348,6 +8343,10 @@ class Placeholder extends WidgetType {
|
|
|
8348
8343
|
wrap.setAttribute("aria-hidden", "true");
|
|
8349
8344
|
return wrap;
|
|
8350
8345
|
}
|
|
8346
|
+
coordsAt(dom) {
|
|
8347
|
+
let rects = dom.firstChild ? clientRectsFor(dom.firstChild) : [];
|
|
8348
|
+
return rects.length ? flattenRect(rects[0], window.getComputedStyle(dom).direction != "rtl") : null;
|
|
8349
|
+
}
|
|
8351
8350
|
ignoreEvent() { return false; }
|
|
8352
8351
|
}
|
|
8353
8352
|
/**
|