@codemirror/view 6.26.1 → 6.26.2
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 +10 -0
- package/dist/index.cjs +26 -52
- package/dist/index.js +26 -52
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
## 6.26.2 (2024-04-09)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Improve behavior of `scrollPastEnd` in a scaled editor.
|
|
6
|
+
|
|
7
|
+
When available, use `Selection.getComposedRanges` on Safari to find the selection inside a shadow DOM.
|
|
8
|
+
|
|
9
|
+
Remove the workaround that avoided inappropriate styling on composed text after a decoration again, since it breaks the stock Android virtual keyboard.
|
|
10
|
+
|
|
1
11
|
## 6.26.1 (2024-03-28)
|
|
2
12
|
|
|
3
13
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -2704,11 +2704,10 @@ class DocView extends ContentView {
|
|
|
2704
2704
|
super();
|
|
2705
2705
|
this.view = view;
|
|
2706
2706
|
this.decorations = [];
|
|
2707
|
-
this.dynamicDecorationMap = [
|
|
2707
|
+
this.dynamicDecorationMap = [];
|
|
2708
2708
|
this.domChanged = null;
|
|
2709
2709
|
this.hasComposition = null;
|
|
2710
2710
|
this.markedForComposition = new Set;
|
|
2711
|
-
this.compositionBarrier = Decoration.none;
|
|
2712
2711
|
this.lastCompositionAfterCursor = false;
|
|
2713
2712
|
// Track a minimum width for the editor. When measuring sizes in
|
|
2714
2713
|
// measureVisibleLineHeights, this is updated to point at the width
|
|
@@ -2973,7 +2972,7 @@ class DocView extends ContentView {
|
|
|
2973
2972
|
// composition, avoid moving it across it and disrupting the
|
|
2974
2973
|
// composition.
|
|
2975
2974
|
suppressWidgetCursorChange(sel, cursor) {
|
|
2976
|
-
return this.hasComposition && cursor.empty &&
|
|
2975
|
+
return this.hasComposition && cursor.empty &&
|
|
2977
2976
|
isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset) &&
|
|
2978
2977
|
this.posFromDOM(sel.focusNode, sel.focusOffset) == cursor.head;
|
|
2979
2978
|
}
|
|
@@ -3181,7 +3180,7 @@ class DocView extends ContentView {
|
|
|
3181
3180
|
return Decoration.set(deco);
|
|
3182
3181
|
}
|
|
3183
3182
|
updateDeco() {
|
|
3184
|
-
let i =
|
|
3183
|
+
let i = 0;
|
|
3185
3184
|
let allDeco = this.view.state.facet(decorations).map(d => {
|
|
3186
3185
|
let dynamic = this.dynamicDecorationMap[i++] = typeof d == "function";
|
|
3187
3186
|
return dynamic ? d(this.view) : d;
|
|
@@ -3197,7 +3196,6 @@ class DocView extends ContentView {
|
|
|
3197
3196
|
allDeco.push(state.RangeSet.join(outerDeco));
|
|
3198
3197
|
}
|
|
3199
3198
|
this.decorations = [
|
|
3200
|
-
this.compositionBarrier,
|
|
3201
3199
|
...allDeco,
|
|
3202
3200
|
this.computeBlockGapDeco(),
|
|
3203
3201
|
this.view.viewState.lineGapDeco
|
|
@@ -3206,34 +3204,6 @@ class DocView extends ContentView {
|
|
|
3206
3204
|
this.dynamicDecorationMap[i++] = false;
|
|
3207
3205
|
return this.decorations;
|
|
3208
3206
|
}
|
|
3209
|
-
// Starting a composition will style the inserted text with the
|
|
3210
|
-
// style of the text before it, and this is only cleared when the
|
|
3211
|
-
// composition ends, because touching it before that will abort it.
|
|
3212
|
-
// This (called from compositionstart handler) tries to notice when
|
|
3213
|
-
// the cursor is after a non-inclusive mark, where the styling could
|
|
3214
|
-
// be jarring, and insert an ad-hoc widget before the cursor to
|
|
3215
|
-
// isolate it from the style before it.
|
|
3216
|
-
maybeCreateCompositionBarrier() {
|
|
3217
|
-
let { main: { head, empty } } = this.view.state.selection;
|
|
3218
|
-
if (!empty)
|
|
3219
|
-
return false;
|
|
3220
|
-
let found = null;
|
|
3221
|
-
for (let set of this.decorations) {
|
|
3222
|
-
set.between(head, head, (from, to, value) => {
|
|
3223
|
-
if (value.point)
|
|
3224
|
-
found = false;
|
|
3225
|
-
else if (value.endSide < 0 && from < head && to == head)
|
|
3226
|
-
found = true;
|
|
3227
|
-
});
|
|
3228
|
-
if (found === false)
|
|
3229
|
-
break;
|
|
3230
|
-
}
|
|
3231
|
-
this.compositionBarrier = found ? Decoration.set(compositionBarrierWidget.range(head)) : Decoration.none;
|
|
3232
|
-
return !!found;
|
|
3233
|
-
}
|
|
3234
|
-
clearCompositionBarrier() {
|
|
3235
|
-
this.compositionBarrier = Decoration.none;
|
|
3236
|
-
}
|
|
3237
3207
|
scrollIntoView(target) {
|
|
3238
3208
|
if (target.isSnapshot) {
|
|
3239
3209
|
let ref = this.view.viewState.lineBlockAt(target.range.head);
|
|
@@ -3266,7 +3236,6 @@ class DocView extends ContentView {
|
|
|
3266
3236
|
scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, Math.max(Math.min(target.xMargin, offsetWidth), -offsetWidth), Math.max(Math.min(target.yMargin, offsetHeight), -offsetHeight), this.view.textDirection == exports.Direction.LTR);
|
|
3267
3237
|
}
|
|
3268
3238
|
}
|
|
3269
|
-
const compositionBarrierWidget = Decoration.widget({ side: -1, widget: NullWidget.inline });
|
|
3270
3239
|
function betweenUneditable(pos) {
|
|
3271
3240
|
return pos.node.nodeType == 1 && pos.node.firstChild &&
|
|
3272
3241
|
(pos.offset == 0 || pos.node.childNodes[pos.offset - 1].contentEditable == "false") &&
|
|
@@ -4508,10 +4477,6 @@ observers.compositionstart = observers.compositionupdate = view => {
|
|
|
4508
4477
|
if (view.inputState.composing < 0) {
|
|
4509
4478
|
// FIXME possibly set a timeout to clear it again on Android
|
|
4510
4479
|
view.inputState.composing = 0;
|
|
4511
|
-
if (view.docView.maybeCreateCompositionBarrier()) {
|
|
4512
|
-
view.update([]);
|
|
4513
|
-
view.docView.clearCompositionBarrier();
|
|
4514
|
-
}
|
|
4515
4480
|
}
|
|
4516
4481
|
};
|
|
4517
4482
|
observers.compositionend = view => {
|
|
@@ -6711,9 +6676,12 @@ class DOMObserver {
|
|
|
6711
6676
|
let { view } = this;
|
|
6712
6677
|
// The Selection object is broken in shadow roots in Safari. See
|
|
6713
6678
|
// https://github.com/codemirror/dev/issues/414
|
|
6679
|
+
let selection = getSelection(view.root);
|
|
6680
|
+
if (!selection)
|
|
6681
|
+
return false;
|
|
6714
6682
|
let range = browser.safari && view.root.nodeType == 11 &&
|
|
6715
6683
|
deepActiveElement(this.dom.ownerDocument) == this.dom &&
|
|
6716
|
-
safariSelectionRangeHack(this.view) ||
|
|
6684
|
+
safariSelectionRangeHack(this.view, selection) || selection;
|
|
6717
6685
|
if (!range || this.selectionRange.eq(range))
|
|
6718
6686
|
return false;
|
|
6719
6687
|
let local = hasSelection(this.dom, range);
|
|
@@ -6985,8 +6953,24 @@ function findChild(cView, dom, dir) {
|
|
|
6985
6953
|
}
|
|
6986
6954
|
return null;
|
|
6987
6955
|
}
|
|
6956
|
+
function buildSelectionRangeFromRange(view, range) {
|
|
6957
|
+
let anchorNode = range.startContainer, anchorOffset = range.startOffset;
|
|
6958
|
+
let focusNode = range.endContainer, focusOffset = range.endOffset;
|
|
6959
|
+
let curAnchor = view.docView.domAtPos(view.state.selection.main.anchor);
|
|
6960
|
+
// Since such a range doesn't distinguish between anchor and head,
|
|
6961
|
+
// use a heuristic that flips it around if its end matches the
|
|
6962
|
+
// current anchor.
|
|
6963
|
+
if (isEquivalentPosition(curAnchor.node, curAnchor.offset, focusNode, focusOffset))
|
|
6964
|
+
[anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset];
|
|
6965
|
+
return { anchorNode, anchorOffset, focusNode, focusOffset };
|
|
6966
|
+
}
|
|
6988
6967
|
// Used to work around a Safari Selection/shadow DOM bug (#414)
|
|
6989
|
-
function safariSelectionRangeHack(view) {
|
|
6968
|
+
function safariSelectionRangeHack(view, selection) {
|
|
6969
|
+
if (selection.getComposedRanges) {
|
|
6970
|
+
let range = selection.getComposedRanges(view.root)[0];
|
|
6971
|
+
if (range)
|
|
6972
|
+
return buildSelectionRangeFromRange(view, range);
|
|
6973
|
+
}
|
|
6990
6974
|
let found = null;
|
|
6991
6975
|
// Because Safari (at least in 2018-2021) doesn't provide regular
|
|
6992
6976
|
// access to the selection inside a shadowroot, we have to perform a
|
|
@@ -7001,17 +6985,7 @@ function safariSelectionRangeHack(view) {
|
|
|
7001
6985
|
view.contentDOM.addEventListener("beforeinput", read, true);
|
|
7002
6986
|
view.dom.ownerDocument.execCommand("indent");
|
|
7003
6987
|
view.contentDOM.removeEventListener("beforeinput", read, true);
|
|
7004
|
-
|
|
7005
|
-
return null;
|
|
7006
|
-
let anchorNode = found.startContainer, anchorOffset = found.startOffset;
|
|
7007
|
-
let focusNode = found.endContainer, focusOffset = found.endOffset;
|
|
7008
|
-
let curAnchor = view.docView.domAtPos(view.state.selection.main.anchor);
|
|
7009
|
-
// Since such a range doesn't distinguish between anchor and head,
|
|
7010
|
-
// use a heuristic that flips it around if its end matches the
|
|
7011
|
-
// current anchor.
|
|
7012
|
-
if (isEquivalentPosition(curAnchor.node, curAnchor.offset, focusNode, focusOffset))
|
|
7013
|
-
[anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset];
|
|
7014
|
-
return { anchorNode, anchorOffset, focusNode, focusOffset };
|
|
6988
|
+
return found ? buildSelectionRangeFromRange(view, found) : null;
|
|
7015
6989
|
}
|
|
7016
6990
|
|
|
7017
6991
|
// The editor's update state machine looks something like this:
|
|
@@ -9102,7 +9076,7 @@ const plugin = ViewPlugin.fromClass(class {
|
|
|
9102
9076
|
}
|
|
9103
9077
|
update(update) {
|
|
9104
9078
|
let { view } = update;
|
|
9105
|
-
let height = view.viewState.editorHeight
|
|
9079
|
+
let height = view.viewState.editorHeight -
|
|
9106
9080
|
view.defaultLineHeight - view.documentPadding.top - 0.5;
|
|
9107
9081
|
if (height >= 0 && height != this.height) {
|
|
9108
9082
|
this.height = height;
|
package/dist/index.js
CHANGED
|
@@ -2700,11 +2700,10 @@ class DocView extends ContentView {
|
|
|
2700
2700
|
super();
|
|
2701
2701
|
this.view = view;
|
|
2702
2702
|
this.decorations = [];
|
|
2703
|
-
this.dynamicDecorationMap = [
|
|
2703
|
+
this.dynamicDecorationMap = [];
|
|
2704
2704
|
this.domChanged = null;
|
|
2705
2705
|
this.hasComposition = null;
|
|
2706
2706
|
this.markedForComposition = new Set;
|
|
2707
|
-
this.compositionBarrier = Decoration.none;
|
|
2708
2707
|
this.lastCompositionAfterCursor = false;
|
|
2709
2708
|
// Track a minimum width for the editor. When measuring sizes in
|
|
2710
2709
|
// measureVisibleLineHeights, this is updated to point at the width
|
|
@@ -2969,7 +2968,7 @@ class DocView extends ContentView {
|
|
|
2969
2968
|
// composition, avoid moving it across it and disrupting the
|
|
2970
2969
|
// composition.
|
|
2971
2970
|
suppressWidgetCursorChange(sel, cursor) {
|
|
2972
|
-
return this.hasComposition && cursor.empty &&
|
|
2971
|
+
return this.hasComposition && cursor.empty &&
|
|
2973
2972
|
isEquivalentPosition(sel.focusNode, sel.focusOffset, sel.anchorNode, sel.anchorOffset) &&
|
|
2974
2973
|
this.posFromDOM(sel.focusNode, sel.focusOffset) == cursor.head;
|
|
2975
2974
|
}
|
|
@@ -3177,7 +3176,7 @@ class DocView extends ContentView {
|
|
|
3177
3176
|
return Decoration.set(deco);
|
|
3178
3177
|
}
|
|
3179
3178
|
updateDeco() {
|
|
3180
|
-
let i =
|
|
3179
|
+
let i = 0;
|
|
3181
3180
|
let allDeco = this.view.state.facet(decorations).map(d => {
|
|
3182
3181
|
let dynamic = this.dynamicDecorationMap[i++] = typeof d == "function";
|
|
3183
3182
|
return dynamic ? d(this.view) : d;
|
|
@@ -3193,7 +3192,6 @@ class DocView extends ContentView {
|
|
|
3193
3192
|
allDeco.push(RangeSet.join(outerDeco));
|
|
3194
3193
|
}
|
|
3195
3194
|
this.decorations = [
|
|
3196
|
-
this.compositionBarrier,
|
|
3197
3195
|
...allDeco,
|
|
3198
3196
|
this.computeBlockGapDeco(),
|
|
3199
3197
|
this.view.viewState.lineGapDeco
|
|
@@ -3202,34 +3200,6 @@ class DocView extends ContentView {
|
|
|
3202
3200
|
this.dynamicDecorationMap[i++] = false;
|
|
3203
3201
|
return this.decorations;
|
|
3204
3202
|
}
|
|
3205
|
-
// Starting a composition will style the inserted text with the
|
|
3206
|
-
// style of the text before it, and this is only cleared when the
|
|
3207
|
-
// composition ends, because touching it before that will abort it.
|
|
3208
|
-
// This (called from compositionstart handler) tries to notice when
|
|
3209
|
-
// the cursor is after a non-inclusive mark, where the styling could
|
|
3210
|
-
// be jarring, and insert an ad-hoc widget before the cursor to
|
|
3211
|
-
// isolate it from the style before it.
|
|
3212
|
-
maybeCreateCompositionBarrier() {
|
|
3213
|
-
let { main: { head, empty } } = this.view.state.selection;
|
|
3214
|
-
if (!empty)
|
|
3215
|
-
return false;
|
|
3216
|
-
let found = null;
|
|
3217
|
-
for (let set of this.decorations) {
|
|
3218
|
-
set.between(head, head, (from, to, value) => {
|
|
3219
|
-
if (value.point)
|
|
3220
|
-
found = false;
|
|
3221
|
-
else if (value.endSide < 0 && from < head && to == head)
|
|
3222
|
-
found = true;
|
|
3223
|
-
});
|
|
3224
|
-
if (found === false)
|
|
3225
|
-
break;
|
|
3226
|
-
}
|
|
3227
|
-
this.compositionBarrier = found ? Decoration.set(compositionBarrierWidget.range(head)) : Decoration.none;
|
|
3228
|
-
return !!found;
|
|
3229
|
-
}
|
|
3230
|
-
clearCompositionBarrier() {
|
|
3231
|
-
this.compositionBarrier = Decoration.none;
|
|
3232
|
-
}
|
|
3233
3203
|
scrollIntoView(target) {
|
|
3234
3204
|
if (target.isSnapshot) {
|
|
3235
3205
|
let ref = this.view.viewState.lineBlockAt(target.range.head);
|
|
@@ -3262,7 +3232,6 @@ class DocView extends ContentView {
|
|
|
3262
3232
|
scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, Math.max(Math.min(target.xMargin, offsetWidth), -offsetWidth), Math.max(Math.min(target.yMargin, offsetHeight), -offsetHeight), this.view.textDirection == Direction.LTR);
|
|
3263
3233
|
}
|
|
3264
3234
|
}
|
|
3265
|
-
const compositionBarrierWidget = /*@__PURE__*/Decoration.widget({ side: -1, widget: NullWidget.inline });
|
|
3266
3235
|
function betweenUneditable(pos) {
|
|
3267
3236
|
return pos.node.nodeType == 1 && pos.node.firstChild &&
|
|
3268
3237
|
(pos.offset == 0 || pos.node.childNodes[pos.offset - 1].contentEditable == "false") &&
|
|
@@ -4504,10 +4473,6 @@ observers.compositionstart = observers.compositionupdate = view => {
|
|
|
4504
4473
|
if (view.inputState.composing < 0) {
|
|
4505
4474
|
// FIXME possibly set a timeout to clear it again on Android
|
|
4506
4475
|
view.inputState.composing = 0;
|
|
4507
|
-
if (view.docView.maybeCreateCompositionBarrier()) {
|
|
4508
|
-
view.update([]);
|
|
4509
|
-
view.docView.clearCompositionBarrier();
|
|
4510
|
-
}
|
|
4511
4476
|
}
|
|
4512
4477
|
};
|
|
4513
4478
|
observers.compositionend = view => {
|
|
@@ -6706,9 +6671,12 @@ class DOMObserver {
|
|
|
6706
6671
|
let { view } = this;
|
|
6707
6672
|
// The Selection object is broken in shadow roots in Safari. See
|
|
6708
6673
|
// https://github.com/codemirror/dev/issues/414
|
|
6674
|
+
let selection = getSelection(view.root);
|
|
6675
|
+
if (!selection)
|
|
6676
|
+
return false;
|
|
6709
6677
|
let range = browser.safari && view.root.nodeType == 11 &&
|
|
6710
6678
|
deepActiveElement(this.dom.ownerDocument) == this.dom &&
|
|
6711
|
-
safariSelectionRangeHack(this.view) ||
|
|
6679
|
+
safariSelectionRangeHack(this.view, selection) || selection;
|
|
6712
6680
|
if (!range || this.selectionRange.eq(range))
|
|
6713
6681
|
return false;
|
|
6714
6682
|
let local = hasSelection(this.dom, range);
|
|
@@ -6980,8 +6948,24 @@ function findChild(cView, dom, dir) {
|
|
|
6980
6948
|
}
|
|
6981
6949
|
return null;
|
|
6982
6950
|
}
|
|
6951
|
+
function buildSelectionRangeFromRange(view, range) {
|
|
6952
|
+
let anchorNode = range.startContainer, anchorOffset = range.startOffset;
|
|
6953
|
+
let focusNode = range.endContainer, focusOffset = range.endOffset;
|
|
6954
|
+
let curAnchor = view.docView.domAtPos(view.state.selection.main.anchor);
|
|
6955
|
+
// Since such a range doesn't distinguish between anchor and head,
|
|
6956
|
+
// use a heuristic that flips it around if its end matches the
|
|
6957
|
+
// current anchor.
|
|
6958
|
+
if (isEquivalentPosition(curAnchor.node, curAnchor.offset, focusNode, focusOffset))
|
|
6959
|
+
[anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset];
|
|
6960
|
+
return { anchorNode, anchorOffset, focusNode, focusOffset };
|
|
6961
|
+
}
|
|
6983
6962
|
// Used to work around a Safari Selection/shadow DOM bug (#414)
|
|
6984
|
-
function safariSelectionRangeHack(view) {
|
|
6963
|
+
function safariSelectionRangeHack(view, selection) {
|
|
6964
|
+
if (selection.getComposedRanges) {
|
|
6965
|
+
let range = selection.getComposedRanges(view.root)[0];
|
|
6966
|
+
if (range)
|
|
6967
|
+
return buildSelectionRangeFromRange(view, range);
|
|
6968
|
+
}
|
|
6985
6969
|
let found = null;
|
|
6986
6970
|
// Because Safari (at least in 2018-2021) doesn't provide regular
|
|
6987
6971
|
// access to the selection inside a shadowroot, we have to perform a
|
|
@@ -6996,17 +6980,7 @@ function safariSelectionRangeHack(view) {
|
|
|
6996
6980
|
view.contentDOM.addEventListener("beforeinput", read, true);
|
|
6997
6981
|
view.dom.ownerDocument.execCommand("indent");
|
|
6998
6982
|
view.contentDOM.removeEventListener("beforeinput", read, true);
|
|
6999
|
-
|
|
7000
|
-
return null;
|
|
7001
|
-
let anchorNode = found.startContainer, anchorOffset = found.startOffset;
|
|
7002
|
-
let focusNode = found.endContainer, focusOffset = found.endOffset;
|
|
7003
|
-
let curAnchor = view.docView.domAtPos(view.state.selection.main.anchor);
|
|
7004
|
-
// Since such a range doesn't distinguish between anchor and head,
|
|
7005
|
-
// use a heuristic that flips it around if its end matches the
|
|
7006
|
-
// current anchor.
|
|
7007
|
-
if (isEquivalentPosition(curAnchor.node, curAnchor.offset, focusNode, focusOffset))
|
|
7008
|
-
[anchorNode, anchorOffset, focusNode, focusOffset] = [focusNode, focusOffset, anchorNode, anchorOffset];
|
|
7009
|
-
return { anchorNode, anchorOffset, focusNode, focusOffset };
|
|
6983
|
+
return found ? buildSelectionRangeFromRange(view, found) : null;
|
|
7010
6984
|
}
|
|
7011
6985
|
|
|
7012
6986
|
// The editor's update state machine looks something like this:
|
|
@@ -9097,7 +9071,7 @@ const plugin = /*@__PURE__*/ViewPlugin.fromClass(class {
|
|
|
9097
9071
|
}
|
|
9098
9072
|
update(update) {
|
|
9099
9073
|
let { view } = update;
|
|
9100
|
-
let height = view.viewState.editorHeight
|
|
9074
|
+
let height = view.viewState.editorHeight -
|
|
9101
9075
|
view.defaultLineHeight - view.documentPadding.top - 0.5;
|
|
9102
9076
|
if (height >= 0 && height != this.height) {
|
|
9103
9077
|
this.height = height;
|