@codemirror/view 6.13.2 → 6.14.1
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 +63 -51
- package/dist/index.d.cts +9 -4
- package/dist/index.d.ts +9 -4
- package/dist/index.js +63 -51
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
## 6.14.1 (2023-07-06)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix an issue where scrolling up through line-wrapped text would sometimes cause the scroll position to pop down.
|
|
6
|
+
|
|
7
|
+
Fix an issue where clicking wouldn't focus the editor on Firefox when it was in an iframe and already the active element of the frame.
|
|
8
|
+
|
|
9
|
+
Fix a bug that could cause compositions to be disrupted because their surrounding DOM was repurposed for some other piece of content.
|
|
10
|
+
|
|
11
|
+
Fix a bug where adding content to the editor could inappropriately move the scroll position.
|
|
12
|
+
|
|
13
|
+
Extend detection of Enter presses on Android to `beforeInput` events with an `"insertLineBreak"` type.
|
|
14
|
+
|
|
15
|
+
## 6.14.0 (2023-06-23)
|
|
16
|
+
|
|
17
|
+
### Bug fixes
|
|
18
|
+
|
|
19
|
+
When dragging text inside the editor, look at the state of Ctrl (or Alt on macOS) at the time of the drop, not the start of drag, to determine whether to move or copy the text.
|
|
20
|
+
|
|
21
|
+
Fix an issue where having a bunch of padding on lines could cause vertical cursor motion and `posAtCoords` to jump over lines.
|
|
22
|
+
|
|
23
|
+
### New features
|
|
24
|
+
|
|
25
|
+
Block widget decorations can now be given an `inlineOrder` option to make them appear in the same ordering as surrounding inline widgets.
|
|
26
|
+
|
|
1
27
|
## 6.13.2 (2023-06-13)
|
|
2
28
|
|
|
3
29
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -320,6 +320,9 @@ function atElementStart(doc, selection) {
|
|
|
320
320
|
}
|
|
321
321
|
}
|
|
322
322
|
}
|
|
323
|
+
function isScrolledToBottom(elt) {
|
|
324
|
+
return elt.scrollTop > Math.max(1, elt.scrollHeight - elt.clientHeight - 4);
|
|
325
|
+
}
|
|
323
326
|
|
|
324
327
|
class DOMPos {
|
|
325
328
|
constructor(node, offset, precise = true) {
|
|
@@ -362,7 +365,7 @@ class ContentView {
|
|
|
362
365
|
let prev = null, next;
|
|
363
366
|
for (let child of this.children) {
|
|
364
367
|
if (child.dirty) {
|
|
365
|
-
if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild)) {
|
|
368
|
+
if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild) && next != view.docView.compositionNode) {
|
|
366
369
|
let contentView = ContentView.get(next);
|
|
367
370
|
if (!contentView || !contentView.parent && contentView.canReuseDOM(child))
|
|
368
371
|
child.reuseDOM(next);
|
|
@@ -1327,7 +1330,9 @@ class Decoration extends state.RangeValue {
|
|
|
1327
1330
|
*/
|
|
1328
1331
|
static widget(spec) {
|
|
1329
1332
|
let side = Math.max(-10000, Math.min(10000, spec.side || 0)), block = !!spec.block;
|
|
1330
|
-
side += block
|
|
1333
|
+
side += (block && !spec.inlineOrder)
|
|
1334
|
+
? (side > 0 ? 300000000 /* BlockAfter */ : -400000000 /* BlockBefore */)
|
|
1335
|
+
: (side > 0 ? 100000000 /* InlineAfter */ : -100000000 /* InlineBefore */);
|
|
1331
1336
|
return new PointDecoration(spec, side, side, block, spec.widget || null, false);
|
|
1332
1337
|
}
|
|
1333
1338
|
/**
|
|
@@ -2599,6 +2604,7 @@ class DocView extends ContentView {
|
|
|
2599
2604
|
super();
|
|
2600
2605
|
this.view = view;
|
|
2601
2606
|
this.compositionDeco = Decoration.none;
|
|
2607
|
+
this.compositionNode = null;
|
|
2602
2608
|
this.decorations = [];
|
|
2603
2609
|
this.dynamicDecorationMap = [];
|
|
2604
2610
|
// Track a minimum width for the editor. When measuring sizes in
|
|
@@ -2638,10 +2644,8 @@ class DocView extends ContentView {
|
|
|
2638
2644
|
this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
|
|
2639
2645
|
}
|
|
2640
2646
|
}
|
|
2641
|
-
|
|
2642
|
-
this.
|
|
2643
|
-
else if (update.transactions.length || this.dirty)
|
|
2644
|
-
this.compositionDeco = computeCompositionDeco(this.view, update.changes);
|
|
2647
|
+
({ deco: this.compositionDeco, node: this.compositionNode } =
|
|
2648
|
+
this.view.inputState.composing < 0 ? noComp : computeCompositionDeco(this.view, update.changes));
|
|
2645
2649
|
// When the DOM nodes around the selection are moved to another
|
|
2646
2650
|
// parent, Chrome sometimes reports a different selection through
|
|
2647
2651
|
// getSelection than the one that it actually shows to the user.
|
|
@@ -3029,10 +3033,11 @@ function compositionSurroundingNode(view) {
|
|
|
3029
3033
|
return { from, to: from + cView.length, node: cView.dom, text: textNode };
|
|
3030
3034
|
}
|
|
3031
3035
|
}
|
|
3036
|
+
const noComp = { deco: Decoration.none, node: null };
|
|
3032
3037
|
function computeCompositionDeco(view, changes) {
|
|
3033
3038
|
let surrounding = compositionSurroundingNode(view);
|
|
3034
3039
|
if (!surrounding)
|
|
3035
|
-
return
|
|
3040
|
+
return noComp;
|
|
3036
3041
|
let { from, to, node, text: textNode } = surrounding;
|
|
3037
3042
|
let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
|
|
3038
3043
|
let { state } = view, reader = new DOMReader([], state);
|
|
@@ -3042,25 +3047,26 @@ function computeCompositionDeco(view, changes) {
|
|
|
3042
3047
|
reader.readRange(node.firstChild, null);
|
|
3043
3048
|
let { text } = reader;
|
|
3044
3049
|
if (text.indexOf(LineBreakPlaceholder) > -1)
|
|
3045
|
-
return
|
|
3050
|
+
return noComp; // Don't try to preserve multi-line compositions
|
|
3046
3051
|
if (newTo - newFrom < text.length) {
|
|
3047
3052
|
if (state.doc.sliceString(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
|
|
3048
3053
|
newTo = newFrom + text.length;
|
|
3049
3054
|
else if (state.doc.sliceString(Math.max(0, newTo - text.length), newTo) == text)
|
|
3050
3055
|
newFrom = newTo - text.length;
|
|
3051
3056
|
else
|
|
3052
|
-
return
|
|
3057
|
+
return noComp;
|
|
3053
3058
|
}
|
|
3054
3059
|
else if (state.doc.sliceString(newFrom, newTo) != text) {
|
|
3055
|
-
return
|
|
3060
|
+
return noComp;
|
|
3056
3061
|
}
|
|
3057
3062
|
let topView = ContentView.get(node);
|
|
3058
3063
|
if (topView instanceof CompositionView)
|
|
3059
3064
|
topView = topView.widget.topView;
|
|
3060
3065
|
else if (topView)
|
|
3061
3066
|
topView.parent = null;
|
|
3062
|
-
|
|
3067
|
+
let deco = Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode, topView), inclusive: true })
|
|
3063
3068
|
.range(newFrom, newTo));
|
|
3069
|
+
return { deco, node };
|
|
3064
3070
|
}
|
|
3065
3071
|
class CompositionWidget extends WidgetType {
|
|
3066
3072
|
constructor(top, text, topView) {
|
|
@@ -3272,7 +3278,7 @@ function posAtCoords(view, coords, precise, bias = -1) {
|
|
|
3272
3278
|
if (yOffset > docHeight)
|
|
3273
3279
|
return view.state.doc.length;
|
|
3274
3280
|
// Scan for a text block near the queried y position
|
|
3275
|
-
for (let halfLine = view.
|
|
3281
|
+
for (let halfLine = view.viewState.heightOracle.textHeight / 2, bounced = false;;) {
|
|
3276
3282
|
block = view.elementAtHeight(yOffset);
|
|
3277
3283
|
if (block.type == exports.BlockType.Text)
|
|
3278
3284
|
break;
|
|
@@ -3352,7 +3358,8 @@ function posAtCoords(view, coords, precise, bias = -1) {
|
|
|
3352
3358
|
function posAtCoordsImprecise(view, contentRect, block, x, y) {
|
|
3353
3359
|
let into = Math.round((x - contentRect.left) * view.defaultCharacterWidth);
|
|
3354
3360
|
if (view.lineWrapping && block.height > view.defaultLineHeight * 1.5) {
|
|
3355
|
-
let
|
|
3361
|
+
let textHeight = view.viewState.heightOracle.textHeight;
|
|
3362
|
+
let line = Math.floor((y - block.top - (view.defaultLineHeight - textHeight) * 0.5) / textHeight);
|
|
3356
3363
|
into += line * view.viewState.heightOracle.lineLength;
|
|
3357
3364
|
}
|
|
3358
3365
|
let content = view.state.sliceDoc(block.from, block.to);
|
|
@@ -3463,7 +3470,7 @@ function moveVertically(view, start, forward, distance) {
|
|
|
3463
3470
|
startY = (dir < 0 ? line.top : line.bottom) + docTop;
|
|
3464
3471
|
}
|
|
3465
3472
|
let resolvedGoal = rect.left + goal;
|
|
3466
|
-
let dist = distance !== null && distance !== void 0 ? distance : (view.
|
|
3473
|
+
let dist = distance !== null && distance !== void 0 ? distance : (view.viewState.heightOracle.textHeight >> 1);
|
|
3467
3474
|
for (let extra = 0;; extra += 10) {
|
|
3468
3475
|
let curY = startY + (dist + extra) * dir;
|
|
3469
3476
|
let pos = posAtCoords(view, { x: resolvedGoal, y: curY }, false, dir);
|
|
@@ -3722,6 +3729,7 @@ class InputState {
|
|
|
3722
3729
|
const PendingKeys = [
|
|
3723
3730
|
{ key: "Backspace", keyCode: 8, inputType: "deleteContentBackward" },
|
|
3724
3731
|
{ key: "Enter", keyCode: 13, inputType: "insertParagraph" },
|
|
3732
|
+
{ key: "Enter", keyCode: 13, inputType: "insertLineBreak" },
|
|
3725
3733
|
{ key: "Delete", keyCode: 46, inputType: "deleteContentForward" }
|
|
3726
3734
|
];
|
|
3727
3735
|
const EmacsyPendingKeys = "dthko";
|
|
@@ -3746,7 +3754,6 @@ class MouseSelection {
|
|
|
3746
3754
|
doc.addEventListener("mouseup", this.up = this.up.bind(this));
|
|
3747
3755
|
this.extend = startEvent.shiftKey;
|
|
3748
3756
|
this.multiple = view.state.facet(state.EditorState.allowMultipleSelections) && addsSelectionRange(view, startEvent);
|
|
3749
|
-
this.dragMove = dragMovesSelection(view, startEvent);
|
|
3750
3757
|
this.dragging = isInPrimarySelection(view, startEvent) && getClickType(startEvent) == 1 ? null : false;
|
|
3751
3758
|
}
|
|
3752
3759
|
start(event) {
|
|
@@ -3967,7 +3974,7 @@ handlers.mousedown = (view, event) => {
|
|
|
3967
3974
|
if (!style && event.button == 0)
|
|
3968
3975
|
style = basicMouseSelection(view, event);
|
|
3969
3976
|
if (style) {
|
|
3970
|
-
let mustFocus = view.
|
|
3977
|
+
let mustFocus = !view.hasFocus;
|
|
3971
3978
|
view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
|
|
3972
3979
|
if (mustFocus)
|
|
3973
3980
|
view.observer.ignore(() => focusPreventScroll(view.contentDOM));
|
|
@@ -4084,7 +4091,7 @@ function dropText(view, event, text, direct) {
|
|
|
4084
4091
|
let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
|
|
4085
4092
|
event.preventDefault();
|
|
4086
4093
|
let { mouseSelection } = view.inputState;
|
|
4087
|
-
let del = direct && mouseSelection && mouseSelection.dragging &&
|
|
4094
|
+
let del = direct && mouseSelection && mouseSelection.dragging && dragMovesSelection(view, event) ?
|
|
4088
4095
|
{ from: mouseSelection.dragging.from, to: mouseSelection.dragging.to } : null;
|
|
4089
4096
|
let ins = { from: dropPos, insert: text };
|
|
4090
4097
|
let changes = view.state.changes(del ? [del, ins] : ins);
|
|
@@ -5164,7 +5171,7 @@ class ViewState {
|
|
|
5164
5171
|
let contentChanges = update.changedRanges;
|
|
5165
5172
|
let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(prevDeco, this.stateDeco, update ? update.changes : state.ChangeSet.empty(this.state.doc.length)));
|
|
5166
5173
|
let prevHeight = this.heightMap.height;
|
|
5167
|
-
let scrollAnchor = this.scrolledToBottom ? null : this.
|
|
5174
|
+
let scrollAnchor = this.scrolledToBottom ? null : this.scrollAnchorAt(this.scrollTop);
|
|
5168
5175
|
this.heightMap = this.heightMap.applyChanges(this.stateDeco, update.startState.doc, this.heightOracle.setDoc(this.state.doc), heightChanges);
|
|
5169
5176
|
if (this.heightMap.height != prevHeight)
|
|
5170
5177
|
update.flags |= 2 /* Height */;
|
|
@@ -5224,7 +5231,7 @@ class ViewState {
|
|
|
5224
5231
|
this.scrollAnchorHeight = -1;
|
|
5225
5232
|
this.scrollTop = view.scrollDOM.scrollTop;
|
|
5226
5233
|
}
|
|
5227
|
-
this.scrolledToBottom =
|
|
5234
|
+
this.scrolledToBottom = isScrolledToBottom(view.scrollDOM);
|
|
5228
5235
|
// Pixel viewport
|
|
5229
5236
|
let pixelViewport = (this.printing ? fullPixelRange : visiblePixelRange)(dom, this.paddingTop);
|
|
5230
5237
|
let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
|
|
@@ -5467,6 +5474,10 @@ class ViewState {
|
|
|
5467
5474
|
lineBlockAtHeight(height) {
|
|
5468
5475
|
return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.heightOracle, 0, 0), this.scaler);
|
|
5469
5476
|
}
|
|
5477
|
+
scrollAnchorAt(scrollTop) {
|
|
5478
|
+
let block = this.lineBlockAtHeight(scrollTop + 8);
|
|
5479
|
+
return block.from >= this.viewport.from || this.viewportLines[0].top - scrollTop > 200 ? block : this.viewportLines[0];
|
|
5480
|
+
}
|
|
5470
5481
|
elementAtHeight(height) {
|
|
5471
5482
|
return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.heightOracle, 0, 0), this.scaler);
|
|
5472
5483
|
}
|
|
@@ -6839,22 +6850,23 @@ class EditorView {
|
|
|
6839
6850
|
let updated = null;
|
|
6840
6851
|
let sDOM = this.scrollDOM, { scrollTop } = sDOM;
|
|
6841
6852
|
let { scrollAnchorPos, scrollAnchorHeight } = this.viewState;
|
|
6853
|
+
if (scrollTop != this.viewState.scrollTop)
|
|
6854
|
+
scrollAnchorHeight = -1;
|
|
6842
6855
|
this.viewState.scrollAnchorHeight = -1;
|
|
6843
|
-
if (scrollAnchorHeight < 0 || scrollTop != this.viewState.scrollTop) {
|
|
6844
|
-
if (scrollTop > sDOM.scrollHeight - sDOM.clientHeight - 4) {
|
|
6845
|
-
scrollAnchorPos = -1;
|
|
6846
|
-
scrollAnchorHeight = this.viewState.heightMap.height;
|
|
6847
|
-
}
|
|
6848
|
-
else {
|
|
6849
|
-
let block = this.viewState.lineBlockAtHeight(scrollTop);
|
|
6850
|
-
scrollAnchorPos = block.from;
|
|
6851
|
-
scrollAnchorHeight = block.top;
|
|
6852
|
-
}
|
|
6853
|
-
}
|
|
6854
6856
|
try {
|
|
6855
6857
|
for (let i = 0;; i++) {
|
|
6858
|
+
if (scrollAnchorHeight < 0) {
|
|
6859
|
+
if (isScrolledToBottom(sDOM)) {
|
|
6860
|
+
scrollAnchorPos = -1;
|
|
6861
|
+
scrollAnchorHeight = this.viewState.heightMap.height;
|
|
6862
|
+
}
|
|
6863
|
+
else {
|
|
6864
|
+
let block = this.viewState.scrollAnchorAt(scrollTop);
|
|
6865
|
+
scrollAnchorPos = block.from;
|
|
6866
|
+
scrollAnchorHeight = block.top;
|
|
6867
|
+
}
|
|
6868
|
+
}
|
|
6856
6869
|
this.updateState = 1 /* Measuring */;
|
|
6857
|
-
let oldViewport = this.viewport;
|
|
6858
6870
|
let changed = this.viewState.measure(this);
|
|
6859
6871
|
if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
|
|
6860
6872
|
break;
|
|
@@ -6877,7 +6889,7 @@ class EditorView {
|
|
|
6877
6889
|
return BadMeasure;
|
|
6878
6890
|
}
|
|
6879
6891
|
});
|
|
6880
|
-
let update = ViewUpdate.create(this, this.state, []), redrawn = false
|
|
6892
|
+
let update = ViewUpdate.create(this, this.state, []), redrawn = false;
|
|
6881
6893
|
update.flags |= changed;
|
|
6882
6894
|
if (!updated)
|
|
6883
6895
|
updated = update;
|
|
@@ -6901,28 +6913,28 @@ class EditorView {
|
|
|
6901
6913
|
logException(this.state, e);
|
|
6902
6914
|
}
|
|
6903
6915
|
}
|
|
6904
|
-
if (this.viewState.editorHeight) {
|
|
6905
|
-
if (this.viewState.scrollTarget) {
|
|
6906
|
-
this.docView.scrollIntoView(this.viewState.scrollTarget);
|
|
6907
|
-
this.viewState.scrollTarget = null;
|
|
6908
|
-
scrolled = true;
|
|
6909
|
-
}
|
|
6910
|
-
else if (scrollAnchorHeight > -1) {
|
|
6911
|
-
let newAnchorHeight = scrollAnchorPos < 0 ? this.viewState.heightMap.height :
|
|
6912
|
-
this.viewState.lineBlockAt(scrollAnchorPos).top;
|
|
6913
|
-
let diff = newAnchorHeight - scrollAnchorHeight;
|
|
6914
|
-
if (diff > 1 || diff < -1) {
|
|
6915
|
-
sDOM.scrollTop = scrollTop + diff;
|
|
6916
|
-
scrolled = true;
|
|
6917
|
-
}
|
|
6918
|
-
}
|
|
6919
|
-
}
|
|
6920
6916
|
if (redrawn)
|
|
6921
6917
|
this.docView.updateSelection(true);
|
|
6922
|
-
if (
|
|
6923
|
-
|
|
6918
|
+
if (!update.viewportChanged && this.measureRequests.length == 0) {
|
|
6919
|
+
if (this.viewState.editorHeight) {
|
|
6920
|
+
if (this.viewState.scrollTarget) {
|
|
6921
|
+
this.docView.scrollIntoView(this.viewState.scrollTarget);
|
|
6922
|
+
this.viewState.scrollTarget = null;
|
|
6923
|
+
continue;
|
|
6924
|
+
}
|
|
6925
|
+
else {
|
|
6926
|
+
let newAnchorHeight = scrollAnchorPos < 0 ? this.viewState.heightMap.height :
|
|
6927
|
+
this.viewState.lineBlockAt(scrollAnchorPos).top;
|
|
6928
|
+
let diff = newAnchorHeight - scrollAnchorHeight;
|
|
6929
|
+
if (diff > 1 || diff < -1) {
|
|
6930
|
+
scrollTop = sDOM.scrollTop = scrollTop + diff;
|
|
6931
|
+
scrollAnchorHeight = -1;
|
|
6932
|
+
continue;
|
|
6933
|
+
}
|
|
6934
|
+
}
|
|
6935
|
+
}
|
|
6924
6936
|
break;
|
|
6925
|
-
|
|
6937
|
+
}
|
|
6926
6938
|
}
|
|
6927
6939
|
}
|
|
6928
6940
|
finally {
|
package/dist/index.d.cts
CHANGED
|
@@ -72,13 +72,18 @@ interface WidgetDecorationSpec {
|
|
|
72
72
|
values will determine their ordering—those with a lower value
|
|
73
73
|
come first. Defaults to 0. May not be more than 10000 or less
|
|
74
74
|
than -10000.
|
|
75
|
-
|
|
76
|
-
Block widgets are always drawn before inline widgets when side
|
|
77
|
-
is non-positive, and after them when side is positive,
|
|
78
|
-
regardless of the value of `side`.
|
|
79
75
|
*/
|
|
80
76
|
side?: number;
|
|
81
77
|
/**
|
|
78
|
+
By default, to avoid unintended mixing of block and inline
|
|
79
|
+
widgets, block widgets with a positive `side` are always drawn
|
|
80
|
+
after all inline widgets at that position, and those with a
|
|
81
|
+
non-positive side before inline widgets. Setting this option to
|
|
82
|
+
`true` for a block widget will turn this off and cause it to be
|
|
83
|
+
rendered between the inline widgets, ordered by `side`.
|
|
84
|
+
*/
|
|
85
|
+
inlineOrder?: boolean;
|
|
86
|
+
/**
|
|
82
87
|
Determines whether this is a block widgets, which will be drawn
|
|
83
88
|
between lines, or an inline widget (the default) which is drawn
|
|
84
89
|
between the surrounding text.
|
package/dist/index.d.ts
CHANGED
|
@@ -72,13 +72,18 @@ interface WidgetDecorationSpec {
|
|
|
72
72
|
values will determine their ordering—those with a lower value
|
|
73
73
|
come first. Defaults to 0. May not be more than 10000 or less
|
|
74
74
|
than -10000.
|
|
75
|
-
|
|
76
|
-
Block widgets are always drawn before inline widgets when side
|
|
77
|
-
is non-positive, and after them when side is positive,
|
|
78
|
-
regardless of the value of `side`.
|
|
79
75
|
*/
|
|
80
76
|
side?: number;
|
|
81
77
|
/**
|
|
78
|
+
By default, to avoid unintended mixing of block and inline
|
|
79
|
+
widgets, block widgets with a positive `side` are always drawn
|
|
80
|
+
after all inline widgets at that position, and those with a
|
|
81
|
+
non-positive side before inline widgets. Setting this option to
|
|
82
|
+
`true` for a block widget will turn this off and cause it to be
|
|
83
|
+
rendered between the inline widgets, ordered by `side`.
|
|
84
|
+
*/
|
|
85
|
+
inlineOrder?: boolean;
|
|
86
|
+
/**
|
|
82
87
|
Determines whether this is a block widgets, which will be drawn
|
|
83
88
|
between lines, or an inline widget (the default) which is drawn
|
|
84
89
|
between the surrounding text.
|
package/dist/index.js
CHANGED
|
@@ -316,6 +316,9 @@ function atElementStart(doc, selection) {
|
|
|
316
316
|
}
|
|
317
317
|
}
|
|
318
318
|
}
|
|
319
|
+
function isScrolledToBottom(elt) {
|
|
320
|
+
return elt.scrollTop > Math.max(1, elt.scrollHeight - elt.clientHeight - 4);
|
|
321
|
+
}
|
|
319
322
|
|
|
320
323
|
class DOMPos {
|
|
321
324
|
constructor(node, offset, precise = true) {
|
|
@@ -358,7 +361,7 @@ class ContentView {
|
|
|
358
361
|
let prev = null, next;
|
|
359
362
|
for (let child of this.children) {
|
|
360
363
|
if (child.dirty) {
|
|
361
|
-
if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild)) {
|
|
364
|
+
if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild) && next != view.docView.compositionNode) {
|
|
362
365
|
let contentView = ContentView.get(next);
|
|
363
366
|
if (!contentView || !contentView.parent && contentView.canReuseDOM(child))
|
|
364
367
|
child.reuseDOM(next);
|
|
@@ -1322,7 +1325,9 @@ class Decoration extends RangeValue {
|
|
|
1322
1325
|
*/
|
|
1323
1326
|
static widget(spec) {
|
|
1324
1327
|
let side = Math.max(-10000, Math.min(10000, spec.side || 0)), block = !!spec.block;
|
|
1325
|
-
side += block
|
|
1328
|
+
side += (block && !spec.inlineOrder)
|
|
1329
|
+
? (side > 0 ? 300000000 /* BlockAfter */ : -400000000 /* BlockBefore */)
|
|
1330
|
+
: (side > 0 ? 100000000 /* InlineAfter */ : -100000000 /* InlineBefore */);
|
|
1326
1331
|
return new PointDecoration(spec, side, side, block, spec.widget || null, false);
|
|
1327
1332
|
}
|
|
1328
1333
|
/**
|
|
@@ -2593,6 +2598,7 @@ class DocView extends ContentView {
|
|
|
2593
2598
|
super();
|
|
2594
2599
|
this.view = view;
|
|
2595
2600
|
this.compositionDeco = Decoration.none;
|
|
2601
|
+
this.compositionNode = null;
|
|
2596
2602
|
this.decorations = [];
|
|
2597
2603
|
this.dynamicDecorationMap = [];
|
|
2598
2604
|
// Track a minimum width for the editor. When measuring sizes in
|
|
@@ -2632,10 +2638,8 @@ class DocView extends ContentView {
|
|
|
2632
2638
|
this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
|
|
2633
2639
|
}
|
|
2634
2640
|
}
|
|
2635
|
-
|
|
2636
|
-
this.
|
|
2637
|
-
else if (update.transactions.length || this.dirty)
|
|
2638
|
-
this.compositionDeco = computeCompositionDeco(this.view, update.changes);
|
|
2641
|
+
({ deco: this.compositionDeco, node: this.compositionNode } =
|
|
2642
|
+
this.view.inputState.composing < 0 ? noComp : computeCompositionDeco(this.view, update.changes));
|
|
2639
2643
|
// When the DOM nodes around the selection are moved to another
|
|
2640
2644
|
// parent, Chrome sometimes reports a different selection through
|
|
2641
2645
|
// getSelection than the one that it actually shows to the user.
|
|
@@ -3023,10 +3027,11 @@ function compositionSurroundingNode(view) {
|
|
|
3023
3027
|
return { from, to: from + cView.length, node: cView.dom, text: textNode };
|
|
3024
3028
|
}
|
|
3025
3029
|
}
|
|
3030
|
+
const noComp = { deco: Decoration.none, node: null };
|
|
3026
3031
|
function computeCompositionDeco(view, changes) {
|
|
3027
3032
|
let surrounding = compositionSurroundingNode(view);
|
|
3028
3033
|
if (!surrounding)
|
|
3029
|
-
return
|
|
3034
|
+
return noComp;
|
|
3030
3035
|
let { from, to, node, text: textNode } = surrounding;
|
|
3031
3036
|
let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
|
|
3032
3037
|
let { state } = view, reader = new DOMReader([], state);
|
|
@@ -3036,25 +3041,26 @@ function computeCompositionDeco(view, changes) {
|
|
|
3036
3041
|
reader.readRange(node.firstChild, null);
|
|
3037
3042
|
let { text } = reader;
|
|
3038
3043
|
if (text.indexOf(LineBreakPlaceholder) > -1)
|
|
3039
|
-
return
|
|
3044
|
+
return noComp; // Don't try to preserve multi-line compositions
|
|
3040
3045
|
if (newTo - newFrom < text.length) {
|
|
3041
3046
|
if (state.doc.sliceString(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
|
|
3042
3047
|
newTo = newFrom + text.length;
|
|
3043
3048
|
else if (state.doc.sliceString(Math.max(0, newTo - text.length), newTo) == text)
|
|
3044
3049
|
newFrom = newTo - text.length;
|
|
3045
3050
|
else
|
|
3046
|
-
return
|
|
3051
|
+
return noComp;
|
|
3047
3052
|
}
|
|
3048
3053
|
else if (state.doc.sliceString(newFrom, newTo) != text) {
|
|
3049
|
-
return
|
|
3054
|
+
return noComp;
|
|
3050
3055
|
}
|
|
3051
3056
|
let topView = ContentView.get(node);
|
|
3052
3057
|
if (topView instanceof CompositionView)
|
|
3053
3058
|
topView = topView.widget.topView;
|
|
3054
3059
|
else if (topView)
|
|
3055
3060
|
topView.parent = null;
|
|
3056
|
-
|
|
3061
|
+
let deco = Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode, topView), inclusive: true })
|
|
3057
3062
|
.range(newFrom, newTo));
|
|
3063
|
+
return { deco, node };
|
|
3058
3064
|
}
|
|
3059
3065
|
class CompositionWidget extends WidgetType {
|
|
3060
3066
|
constructor(top, text, topView) {
|
|
@@ -3266,7 +3272,7 @@ function posAtCoords(view, coords, precise, bias = -1) {
|
|
|
3266
3272
|
if (yOffset > docHeight)
|
|
3267
3273
|
return view.state.doc.length;
|
|
3268
3274
|
// Scan for a text block near the queried y position
|
|
3269
|
-
for (let halfLine = view.
|
|
3275
|
+
for (let halfLine = view.viewState.heightOracle.textHeight / 2, bounced = false;;) {
|
|
3270
3276
|
block = view.elementAtHeight(yOffset);
|
|
3271
3277
|
if (block.type == BlockType.Text)
|
|
3272
3278
|
break;
|
|
@@ -3346,7 +3352,8 @@ function posAtCoords(view, coords, precise, bias = -1) {
|
|
|
3346
3352
|
function posAtCoordsImprecise(view, contentRect, block, x, y) {
|
|
3347
3353
|
let into = Math.round((x - contentRect.left) * view.defaultCharacterWidth);
|
|
3348
3354
|
if (view.lineWrapping && block.height > view.defaultLineHeight * 1.5) {
|
|
3349
|
-
let
|
|
3355
|
+
let textHeight = view.viewState.heightOracle.textHeight;
|
|
3356
|
+
let line = Math.floor((y - block.top - (view.defaultLineHeight - textHeight) * 0.5) / textHeight);
|
|
3350
3357
|
into += line * view.viewState.heightOracle.lineLength;
|
|
3351
3358
|
}
|
|
3352
3359
|
let content = view.state.sliceDoc(block.from, block.to);
|
|
@@ -3457,7 +3464,7 @@ function moveVertically(view, start, forward, distance) {
|
|
|
3457
3464
|
startY = (dir < 0 ? line.top : line.bottom) + docTop;
|
|
3458
3465
|
}
|
|
3459
3466
|
let resolvedGoal = rect.left + goal;
|
|
3460
|
-
let dist = distance !== null && distance !== void 0 ? distance : (view.
|
|
3467
|
+
let dist = distance !== null && distance !== void 0 ? distance : (view.viewState.heightOracle.textHeight >> 1);
|
|
3461
3468
|
for (let extra = 0;; extra += 10) {
|
|
3462
3469
|
let curY = startY + (dist + extra) * dir;
|
|
3463
3470
|
let pos = posAtCoords(view, { x: resolvedGoal, y: curY }, false, dir);
|
|
@@ -3716,6 +3723,7 @@ class InputState {
|
|
|
3716
3723
|
const PendingKeys = [
|
|
3717
3724
|
{ key: "Backspace", keyCode: 8, inputType: "deleteContentBackward" },
|
|
3718
3725
|
{ key: "Enter", keyCode: 13, inputType: "insertParagraph" },
|
|
3726
|
+
{ key: "Enter", keyCode: 13, inputType: "insertLineBreak" },
|
|
3719
3727
|
{ key: "Delete", keyCode: 46, inputType: "deleteContentForward" }
|
|
3720
3728
|
];
|
|
3721
3729
|
const EmacsyPendingKeys = "dthko";
|
|
@@ -3740,7 +3748,6 @@ class MouseSelection {
|
|
|
3740
3748
|
doc.addEventListener("mouseup", this.up = this.up.bind(this));
|
|
3741
3749
|
this.extend = startEvent.shiftKey;
|
|
3742
3750
|
this.multiple = view.state.facet(EditorState.allowMultipleSelections) && addsSelectionRange(view, startEvent);
|
|
3743
|
-
this.dragMove = dragMovesSelection(view, startEvent);
|
|
3744
3751
|
this.dragging = isInPrimarySelection(view, startEvent) && getClickType(startEvent) == 1 ? null : false;
|
|
3745
3752
|
}
|
|
3746
3753
|
start(event) {
|
|
@@ -3961,7 +3968,7 @@ handlers.mousedown = (view, event) => {
|
|
|
3961
3968
|
if (!style && event.button == 0)
|
|
3962
3969
|
style = basicMouseSelection(view, event);
|
|
3963
3970
|
if (style) {
|
|
3964
|
-
let mustFocus = view.
|
|
3971
|
+
let mustFocus = !view.hasFocus;
|
|
3965
3972
|
view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
|
|
3966
3973
|
if (mustFocus)
|
|
3967
3974
|
view.observer.ignore(() => focusPreventScroll(view.contentDOM));
|
|
@@ -4078,7 +4085,7 @@ function dropText(view, event, text, direct) {
|
|
|
4078
4085
|
let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
|
|
4079
4086
|
event.preventDefault();
|
|
4080
4087
|
let { mouseSelection } = view.inputState;
|
|
4081
|
-
let del = direct && mouseSelection && mouseSelection.dragging &&
|
|
4088
|
+
let del = direct && mouseSelection && mouseSelection.dragging && dragMovesSelection(view, event) ?
|
|
4082
4089
|
{ from: mouseSelection.dragging.from, to: mouseSelection.dragging.to } : null;
|
|
4083
4090
|
let ins = { from: dropPos, insert: text };
|
|
4084
4091
|
let changes = view.state.changes(del ? [del, ins] : ins);
|
|
@@ -5157,7 +5164,7 @@ class ViewState {
|
|
|
5157
5164
|
let contentChanges = update.changedRanges;
|
|
5158
5165
|
let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(prevDeco, this.stateDeco, update ? update.changes : ChangeSet.empty(this.state.doc.length)));
|
|
5159
5166
|
let prevHeight = this.heightMap.height;
|
|
5160
|
-
let scrollAnchor = this.scrolledToBottom ? null : this.
|
|
5167
|
+
let scrollAnchor = this.scrolledToBottom ? null : this.scrollAnchorAt(this.scrollTop);
|
|
5161
5168
|
this.heightMap = this.heightMap.applyChanges(this.stateDeco, update.startState.doc, this.heightOracle.setDoc(this.state.doc), heightChanges);
|
|
5162
5169
|
if (this.heightMap.height != prevHeight)
|
|
5163
5170
|
update.flags |= 2 /* Height */;
|
|
@@ -5217,7 +5224,7 @@ class ViewState {
|
|
|
5217
5224
|
this.scrollAnchorHeight = -1;
|
|
5218
5225
|
this.scrollTop = view.scrollDOM.scrollTop;
|
|
5219
5226
|
}
|
|
5220
|
-
this.scrolledToBottom =
|
|
5227
|
+
this.scrolledToBottom = isScrolledToBottom(view.scrollDOM);
|
|
5221
5228
|
// Pixel viewport
|
|
5222
5229
|
let pixelViewport = (this.printing ? fullPixelRange : visiblePixelRange)(dom, this.paddingTop);
|
|
5223
5230
|
let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
|
|
@@ -5460,6 +5467,10 @@ class ViewState {
|
|
|
5460
5467
|
lineBlockAtHeight(height) {
|
|
5461
5468
|
return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.heightOracle, 0, 0), this.scaler);
|
|
5462
5469
|
}
|
|
5470
|
+
scrollAnchorAt(scrollTop) {
|
|
5471
|
+
let block = this.lineBlockAtHeight(scrollTop + 8);
|
|
5472
|
+
return block.from >= this.viewport.from || this.viewportLines[0].top - scrollTop > 200 ? block : this.viewportLines[0];
|
|
5473
|
+
}
|
|
5463
5474
|
elementAtHeight(height) {
|
|
5464
5475
|
return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.heightOracle, 0, 0), this.scaler);
|
|
5465
5476
|
}
|
|
@@ -6832,22 +6843,23 @@ class EditorView {
|
|
|
6832
6843
|
let updated = null;
|
|
6833
6844
|
let sDOM = this.scrollDOM, { scrollTop } = sDOM;
|
|
6834
6845
|
let { scrollAnchorPos, scrollAnchorHeight } = this.viewState;
|
|
6846
|
+
if (scrollTop != this.viewState.scrollTop)
|
|
6847
|
+
scrollAnchorHeight = -1;
|
|
6835
6848
|
this.viewState.scrollAnchorHeight = -1;
|
|
6836
|
-
if (scrollAnchorHeight < 0 || scrollTop != this.viewState.scrollTop) {
|
|
6837
|
-
if (scrollTop > sDOM.scrollHeight - sDOM.clientHeight - 4) {
|
|
6838
|
-
scrollAnchorPos = -1;
|
|
6839
|
-
scrollAnchorHeight = this.viewState.heightMap.height;
|
|
6840
|
-
}
|
|
6841
|
-
else {
|
|
6842
|
-
let block = this.viewState.lineBlockAtHeight(scrollTop);
|
|
6843
|
-
scrollAnchorPos = block.from;
|
|
6844
|
-
scrollAnchorHeight = block.top;
|
|
6845
|
-
}
|
|
6846
|
-
}
|
|
6847
6849
|
try {
|
|
6848
6850
|
for (let i = 0;; i++) {
|
|
6851
|
+
if (scrollAnchorHeight < 0) {
|
|
6852
|
+
if (isScrolledToBottom(sDOM)) {
|
|
6853
|
+
scrollAnchorPos = -1;
|
|
6854
|
+
scrollAnchorHeight = this.viewState.heightMap.height;
|
|
6855
|
+
}
|
|
6856
|
+
else {
|
|
6857
|
+
let block = this.viewState.scrollAnchorAt(scrollTop);
|
|
6858
|
+
scrollAnchorPos = block.from;
|
|
6859
|
+
scrollAnchorHeight = block.top;
|
|
6860
|
+
}
|
|
6861
|
+
}
|
|
6849
6862
|
this.updateState = 1 /* Measuring */;
|
|
6850
|
-
let oldViewport = this.viewport;
|
|
6851
6863
|
let changed = this.viewState.measure(this);
|
|
6852
6864
|
if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
|
|
6853
6865
|
break;
|
|
@@ -6870,7 +6882,7 @@ class EditorView {
|
|
|
6870
6882
|
return BadMeasure;
|
|
6871
6883
|
}
|
|
6872
6884
|
});
|
|
6873
|
-
let update = ViewUpdate.create(this, this.state, []), redrawn = false
|
|
6885
|
+
let update = ViewUpdate.create(this, this.state, []), redrawn = false;
|
|
6874
6886
|
update.flags |= changed;
|
|
6875
6887
|
if (!updated)
|
|
6876
6888
|
updated = update;
|
|
@@ -6894,28 +6906,28 @@ class EditorView {
|
|
|
6894
6906
|
logException(this.state, e);
|
|
6895
6907
|
}
|
|
6896
6908
|
}
|
|
6897
|
-
if (this.viewState.editorHeight) {
|
|
6898
|
-
if (this.viewState.scrollTarget) {
|
|
6899
|
-
this.docView.scrollIntoView(this.viewState.scrollTarget);
|
|
6900
|
-
this.viewState.scrollTarget = null;
|
|
6901
|
-
scrolled = true;
|
|
6902
|
-
}
|
|
6903
|
-
else if (scrollAnchorHeight > -1) {
|
|
6904
|
-
let newAnchorHeight = scrollAnchorPos < 0 ? this.viewState.heightMap.height :
|
|
6905
|
-
this.viewState.lineBlockAt(scrollAnchorPos).top;
|
|
6906
|
-
let diff = newAnchorHeight - scrollAnchorHeight;
|
|
6907
|
-
if (diff > 1 || diff < -1) {
|
|
6908
|
-
sDOM.scrollTop = scrollTop + diff;
|
|
6909
|
-
scrolled = true;
|
|
6910
|
-
}
|
|
6911
|
-
}
|
|
6912
|
-
}
|
|
6913
6909
|
if (redrawn)
|
|
6914
6910
|
this.docView.updateSelection(true);
|
|
6915
|
-
if (
|
|
6916
|
-
|
|
6911
|
+
if (!update.viewportChanged && this.measureRequests.length == 0) {
|
|
6912
|
+
if (this.viewState.editorHeight) {
|
|
6913
|
+
if (this.viewState.scrollTarget) {
|
|
6914
|
+
this.docView.scrollIntoView(this.viewState.scrollTarget);
|
|
6915
|
+
this.viewState.scrollTarget = null;
|
|
6916
|
+
continue;
|
|
6917
|
+
}
|
|
6918
|
+
else {
|
|
6919
|
+
let newAnchorHeight = scrollAnchorPos < 0 ? this.viewState.heightMap.height :
|
|
6920
|
+
this.viewState.lineBlockAt(scrollAnchorPos).top;
|
|
6921
|
+
let diff = newAnchorHeight - scrollAnchorHeight;
|
|
6922
|
+
if (diff > 1 || diff < -1) {
|
|
6923
|
+
scrollTop = sDOM.scrollTop = scrollTop + diff;
|
|
6924
|
+
scrollAnchorHeight = -1;
|
|
6925
|
+
continue;
|
|
6926
|
+
}
|
|
6927
|
+
}
|
|
6928
|
+
}
|
|
6917
6929
|
break;
|
|
6918
|
-
|
|
6930
|
+
}
|
|
6919
6931
|
}
|
|
6920
6932
|
}
|
|
6921
6933
|
finally {
|