@codemirror/view 0.19.45 → 0.19.46

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 CHANGED
@@ -1,3 +1,13 @@
1
+ ## 0.19.46 (2022-03-03)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix a bug where block widgets on the edges of viewports could cause the positioning of content to misalign with the gutter and height computations.
6
+
7
+ Improve cursor height next to widgets.
8
+
9
+ Fix a bug where mapping positions to screen coordinates could return incorred coordinates during composition.
10
+
1
11
  ## 0.19.45 (2022-02-23)
2
12
 
3
13
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -22,7 +22,7 @@ function getSelection(root) {
22
22
  return target.getSelection();
23
23
  }
24
24
  function contains(dom, node) {
25
- return node ? dom.contains(node.nodeType != 1 ? node.parentNode : node) : false;
25
+ return node ? dom == node || dom.contains(node.nodeType != 1 ? node.parentNode : node) : false;
26
26
  }
27
27
  function deepActiveElement() {
28
28
  let elt = document.activeElement;
@@ -856,18 +856,26 @@ class WidgetView extends ContentView {
856
856
  }
857
857
  class CompositionView extends WidgetView {
858
858
  domAtPos(pos) {
859
- let { topView } = this.widget;
859
+ let { topView, text } = this.widget;
860
860
  if (!topView)
861
- return new DOMPos(this.widget.text, Math.min(pos, this.widget.text.nodeValue.length));
862
- return posInCompositionTree(pos, topView, this.widget.text);
861
+ return new DOMPos(text, Math.min(pos, text.nodeValue.length));
862
+ return scanCompositionTree(pos, 0, topView, text, (v, p) => v.domAtPos(p), p => new DOMPos(text, Math.min(p, text.nodeValue.length)));
863
863
  }
864
864
  sync() { this.setDOM(this.widget.toDOM()); }
865
865
  localPosFromDOM(node, offset) {
866
- return !offset ? 0 : node.nodeType == 3 ? Math.min(offset, this.length) : this.length;
866
+ let { topView, text } = this.widget;
867
+ if (!topView)
868
+ return Math.min(offset, this.length);
869
+ return posFromDOMInCompositionTree(node, offset, topView, text);
867
870
  }
868
871
  ignoreMutation() { return false; }
869
872
  get overrideDOMText() { return null; }
870
- coordsAt(pos, side) { return textCoords(this.widget.text, pos, side); }
873
+ coordsAt(pos, side) {
874
+ let { topView, text } = this.widget;
875
+ if (!topView)
876
+ return textCoords(text, pos, side);
877
+ return scanCompositionTree(pos, side, topView, text, (v, pos, side) => v.coordsAt(pos, side), (pos, side) => textCoords(text, pos, side));
878
+ }
871
879
  destroy() {
872
880
  var _a;
873
881
  super.destroy();
@@ -878,23 +886,37 @@ class CompositionView extends WidgetView {
878
886
  // Uses the old structure of a chunk of content view frozen for
879
887
  // composition to try and find a reasonable DOM location for the given
880
888
  // offset.
881
- function posInCompositionTree(pos, view, text) {
889
+ function scanCompositionTree(pos, side, view, text, enterView, fromText) {
882
890
  if (view instanceof MarkView) {
883
891
  for (let child of view.children) {
884
- let hasComp = child.dom == text || child.dom.contains(text.parentNode);
892
+ let hasComp = contains(child.dom, text);
885
893
  let len = hasComp ? text.nodeValue.length : child.length;
886
894
  if (pos < len || pos == len && child.getSide() <= 0)
887
- return hasComp ? posInCompositionTree(pos, child, text) : child.domAtPos(pos);
895
+ return hasComp ? scanCompositionTree(pos, side, child, text, enterView, fromText) : enterView(child, pos, side);
888
896
  pos -= len;
889
897
  }
890
- return view.domAtPos(view.length);
898
+ return enterView(view, view.length, -1);
891
899
  }
892
900
  else if (view.dom == text) {
893
- return new DOMPos(text, Math.min(pos, text.nodeValue.length));
901
+ return fromText(pos, side);
894
902
  }
895
903
  else {
896
- return view.domAtPos(pos);
904
+ return enterView(view, pos, side);
905
+ }
906
+ }
907
+ function posFromDOMInCompositionTree(node, offset, view, text) {
908
+ if (view instanceof MarkView) {
909
+ for (let child of view.children) {
910
+ let pos = 0, hasComp = contains(child.dom, text);
911
+ if (contains(child.dom, node))
912
+ return pos + (hasComp ? posFromDOMInCompositionTree(node, offset, child, text) : child.localPosFromDOM(node, offset));
913
+ pos += hasComp ? text.nodeValue.length : child.length;
914
+ }
897
915
  }
916
+ else if (view.dom == text) {
917
+ return Math.min(offset, text.nodeValue.length);
918
+ }
919
+ return view.localPosFromDOM(node, offset);
898
920
  }
899
921
  // These are drawn around uneditable widgets to avoid a number of
900
922
  // browser bugs that show up when the cursor is directly next to
@@ -922,13 +944,43 @@ class WidgetBufferView extends ContentView {
922
944
  localPosFromDOM() { return 0; }
923
945
  domBoundsAround() { return null; }
924
946
  coordsAt(pos) {
925
- return this.dom.getBoundingClientRect();
947
+ let imgRect = this.dom.getBoundingClientRect();
948
+ // Since the <img> height doesn't correspond to text height, try
949
+ // to borrow the height from some sibling node.
950
+ let siblingRect = inlineSiblingRect(this, this.side > 0 ? -1 : 1);
951
+ return siblingRect && siblingRect.top < imgRect.bottom && siblingRect.bottom > imgRect.top
952
+ ? { left: imgRect.left, right: imgRect.right, top: siblingRect.top, bottom: siblingRect.bottom } : imgRect;
926
953
  }
927
954
  get overrideDOMText() {
928
955
  return text.Text.empty;
929
956
  }
930
957
  }
931
958
  TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
959
+ function inlineSiblingRect(view, side) {
960
+ let parent = view.parent, index = parent ? parent.children.indexOf(view) : -1;
961
+ while (parent && index >= 0) {
962
+ if (side < 0 ? index > 0 : index < parent.children.length) {
963
+ let next = parent.children[index + side];
964
+ if (next instanceof TextView) {
965
+ let nextRect = next.coordsAt(side < 0 ? next.length : 0, side);
966
+ if (nextRect)
967
+ return nextRect;
968
+ }
969
+ index += side;
970
+ }
971
+ else if (parent instanceof MarkView && parent.parent) {
972
+ index = parent.parent.children.indexOf(parent) + (side < 0 ? 0 : 1);
973
+ parent = parent.parent;
974
+ }
975
+ else {
976
+ let last = parent.dom.lastChild;
977
+ if (last && last.nodeName == "BR")
978
+ return last.getClientRects()[0];
979
+ break;
980
+ }
981
+ }
982
+ return undefined;
983
+ }
932
984
  function inlineDOMAtPos(dom, children, pos) {
933
985
  let i = 0;
934
986
  for (let off = 0; i < children.length; i++) {
@@ -1154,7 +1206,7 @@ class Decoration extends rangeset.RangeValue {
1154
1206
  */
1155
1207
  static widget(spec) {
1156
1208
  let side = spec.side || 0, block = !!spec.block;
1157
- side += block ? (side > 0 ? 300000000 /* BlockAfter */ : -400000000 /* BlockBefore */) : (side > 0 ? 100000000 /* InlineAfter */ : -100000000 /* InlineBefore */);
1209
+ side += block ? (side > 0 ? 400000000 /* BlockAfter */ : -500000000 /* BlockBefore */) : (side > 0 ? 100000000 /* InlineAfter */ : -100000000 /* InlineBefore */);
1158
1210
  return new PointDecoration(spec, side, side, block, spec.widget || null, false);
1159
1211
  }
1160
1212
  /**
@@ -1162,10 +1214,16 @@ class Decoration extends rangeset.RangeValue {
1162
1214
  a widget, or simply hides it.
1163
1215
  */
1164
1216
  static replace(spec) {
1165
- let block = !!spec.block;
1166
- let { start, end } = getInclusive(spec, block);
1167
- let startSide = (start ? (block ? -300000000 /* BlockIncStart */ : -1 /* InlineIncStart */) : 400000000 /* NonIncStart */) - 1;
1168
- let endSide = (end ? (block ? 200000000 /* BlockIncEnd */ : 1 /* InlineIncEnd */) : -500000000 /* NonIncEnd */) + 1;
1217
+ let block = !!spec.block, startSide, endSide;
1218
+ if (spec.isBlockGap) {
1219
+ startSide = -400000000 /* GapStart */;
1220
+ endSide = 300000000 /* GapEnd */;
1221
+ }
1222
+ else {
1223
+ let { start, end } = getInclusive(spec, block);
1224
+ startSide = (start ? (block ? -300000000 /* BlockIncStart */ : -1 /* InlineIncStart */) : 500000000 /* NonIncStart */) - 1;
1225
+ endSide = (end ? (block ? 200000000 /* BlockIncEnd */ : 1 /* InlineIncEnd */) : -600000000 /* NonIncEnd */) + 1;
1226
+ }
1169
1227
  return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
1170
1228
  }
1171
1229
  /**
@@ -1195,7 +1253,7 @@ Decoration.none = rangeset.RangeSet.empty;
1195
1253
  class MarkDecoration extends Decoration {
1196
1254
  constructor(spec) {
1197
1255
  let { start, end } = getInclusive(spec);
1198
- super(start ? -1 /* InlineIncStart */ : 400000000 /* NonIncStart */, end ? 1 /* InlineIncEnd */ : -500000000 /* NonIncEnd */, null, spec);
1256
+ super(start ? -1 /* InlineIncStart */ : 500000000 /* NonIncStart */, end ? 1 /* InlineIncEnd */ : -600000000 /* NonIncEnd */, null, spec);
1199
1257
  this.tagName = spec.tagName || "span";
1200
1258
  this.class = spec.class || "";
1201
1259
  this.attrs = spec.attributes || null;
@@ -2774,7 +2832,12 @@ class DocView extends ContentView {
2774
2832
  let end = next ? next.from - 1 : this.length;
2775
2833
  if (end > pos) {
2776
2834
  let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
2777
- deco.push(Decoration.replace({ widget: new BlockGapWidget(height), block: true, inclusive: true }).range(pos, end));
2835
+ deco.push(Decoration.replace({
2836
+ widget: new BlockGapWidget(height),
2837
+ block: true,
2838
+ inclusive: true,
2839
+ isBlockGap: true,
2840
+ }).range(pos, end));
2778
2841
  }
2779
2842
  if (!next)
2780
2843
  break;
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ function getSelection(root) {
19
19
  return target.getSelection();
20
20
  }
21
21
  function contains(dom, node) {
22
- return node ? dom.contains(node.nodeType != 1 ? node.parentNode : node) : false;
22
+ return node ? dom == node || dom.contains(node.nodeType != 1 ? node.parentNode : node) : false;
23
23
  }
24
24
  function deepActiveElement() {
25
25
  let elt = document.activeElement;
@@ -853,18 +853,26 @@ class WidgetView extends ContentView {
853
853
  }
854
854
  class CompositionView extends WidgetView {
855
855
  domAtPos(pos) {
856
- let { topView } = this.widget;
856
+ let { topView, text } = this.widget;
857
857
  if (!topView)
858
- return new DOMPos(this.widget.text, Math.min(pos, this.widget.text.nodeValue.length));
859
- return posInCompositionTree(pos, topView, this.widget.text);
858
+ return new DOMPos(text, Math.min(pos, text.nodeValue.length));
859
+ return scanCompositionTree(pos, 0, topView, text, (v, p) => v.domAtPos(p), p => new DOMPos(text, Math.min(p, text.nodeValue.length)));
860
860
  }
861
861
  sync() { this.setDOM(this.widget.toDOM()); }
862
862
  localPosFromDOM(node, offset) {
863
- return !offset ? 0 : node.nodeType == 3 ? Math.min(offset, this.length) : this.length;
863
+ let { topView, text } = this.widget;
864
+ if (!topView)
865
+ return Math.min(offset, this.length);
866
+ return posFromDOMInCompositionTree(node, offset, topView, text);
864
867
  }
865
868
  ignoreMutation() { return false; }
866
869
  get overrideDOMText() { return null; }
867
- coordsAt(pos, side) { return textCoords(this.widget.text, pos, side); }
870
+ coordsAt(pos, side) {
871
+ let { topView, text } = this.widget;
872
+ if (!topView)
873
+ return textCoords(text, pos, side);
874
+ return scanCompositionTree(pos, side, topView, text, (v, pos, side) => v.coordsAt(pos, side), (pos, side) => textCoords(text, pos, side));
875
+ }
868
876
  destroy() {
869
877
  var _a;
870
878
  super.destroy();
@@ -875,23 +883,37 @@ class CompositionView extends WidgetView {
875
883
  // Uses the old structure of a chunk of content view frozen for
876
884
  // composition to try and find a reasonable DOM location for the given
877
885
  // offset.
878
- function posInCompositionTree(pos, view, text) {
886
+ function scanCompositionTree(pos, side, view, text, enterView, fromText) {
879
887
  if (view instanceof MarkView) {
880
888
  for (let child of view.children) {
881
- let hasComp = child.dom == text || child.dom.contains(text.parentNode);
889
+ let hasComp = contains(child.dom, text);
882
890
  let len = hasComp ? text.nodeValue.length : child.length;
883
891
  if (pos < len || pos == len && child.getSide() <= 0)
884
- return hasComp ? posInCompositionTree(pos, child, text) : child.domAtPos(pos);
892
+ return hasComp ? scanCompositionTree(pos, side, child, text, enterView, fromText) : enterView(child, pos, side);
885
893
  pos -= len;
886
894
  }
887
- return view.domAtPos(view.length);
895
+ return enterView(view, view.length, -1);
888
896
  }
889
897
  else if (view.dom == text) {
890
- return new DOMPos(text, Math.min(pos, text.nodeValue.length));
898
+ return fromText(pos, side);
891
899
  }
892
900
  else {
893
- return view.domAtPos(pos);
901
+ return enterView(view, pos, side);
902
+ }
903
+ }
904
+ function posFromDOMInCompositionTree(node, offset, view, text) {
905
+ if (view instanceof MarkView) {
906
+ for (let child of view.children) {
907
+ let pos = 0, hasComp = contains(child.dom, text);
908
+ if (contains(child.dom, node))
909
+ return pos + (hasComp ? posFromDOMInCompositionTree(node, offset, child, text) : child.localPosFromDOM(node, offset));
910
+ pos += hasComp ? text.nodeValue.length : child.length;
911
+ }
894
912
  }
913
+ else if (view.dom == text) {
914
+ return Math.min(offset, text.nodeValue.length);
915
+ }
916
+ return view.localPosFromDOM(node, offset);
895
917
  }
896
918
  // These are drawn around uneditable widgets to avoid a number of
897
919
  // browser bugs that show up when the cursor is directly next to
@@ -919,13 +941,43 @@ class WidgetBufferView extends ContentView {
919
941
  localPosFromDOM() { return 0; }
920
942
  domBoundsAround() { return null; }
921
943
  coordsAt(pos) {
922
- return this.dom.getBoundingClientRect();
944
+ let imgRect = this.dom.getBoundingClientRect();
945
+ // Since the <img> height doesn't correspond to text height, try
946
+ // to borrow the height from some sibling node.
947
+ let siblingRect = inlineSiblingRect(this, this.side > 0 ? -1 : 1);
948
+ return siblingRect && siblingRect.top < imgRect.bottom && siblingRect.bottom > imgRect.top
949
+ ? { left: imgRect.left, right: imgRect.right, top: siblingRect.top, bottom: siblingRect.bottom } : imgRect;
923
950
  }
924
951
  get overrideDOMText() {
925
952
  return Text.empty;
926
953
  }
927
954
  }
928
955
  TextView.prototype.children = WidgetView.prototype.children = WidgetBufferView.prototype.children = noChildren;
956
+ function inlineSiblingRect(view, side) {
957
+ let parent = view.parent, index = parent ? parent.children.indexOf(view) : -1;
958
+ while (parent && index >= 0) {
959
+ if (side < 0 ? index > 0 : index < parent.children.length) {
960
+ let next = parent.children[index + side];
961
+ if (next instanceof TextView) {
962
+ let nextRect = next.coordsAt(side < 0 ? next.length : 0, side);
963
+ if (nextRect)
964
+ return nextRect;
965
+ }
966
+ index += side;
967
+ }
968
+ else if (parent instanceof MarkView && parent.parent) {
969
+ index = parent.parent.children.indexOf(parent) + (side < 0 ? 0 : 1);
970
+ parent = parent.parent;
971
+ }
972
+ else {
973
+ let last = parent.dom.lastChild;
974
+ if (last && last.nodeName == "BR")
975
+ return last.getClientRects()[0];
976
+ break;
977
+ }
978
+ }
979
+ return undefined;
980
+ }
929
981
  function inlineDOMAtPos(dom, children, pos) {
930
982
  let i = 0;
931
983
  for (let off = 0; i < children.length; i++) {
@@ -1150,7 +1202,7 @@ class Decoration extends RangeValue {
1150
1202
  */
1151
1203
  static widget(spec) {
1152
1204
  let side = spec.side || 0, block = !!spec.block;
1153
- side += block ? (side > 0 ? 300000000 /* BlockAfter */ : -400000000 /* BlockBefore */) : (side > 0 ? 100000000 /* InlineAfter */ : -100000000 /* InlineBefore */);
1205
+ side += block ? (side > 0 ? 400000000 /* BlockAfter */ : -500000000 /* BlockBefore */) : (side > 0 ? 100000000 /* InlineAfter */ : -100000000 /* InlineBefore */);
1154
1206
  return new PointDecoration(spec, side, side, block, spec.widget || null, false);
1155
1207
  }
1156
1208
  /**
@@ -1158,10 +1210,16 @@ class Decoration extends RangeValue {
1158
1210
  a widget, or simply hides it.
1159
1211
  */
1160
1212
  static replace(spec) {
1161
- let block = !!spec.block;
1162
- let { start, end } = getInclusive(spec, block);
1163
- let startSide = (start ? (block ? -300000000 /* BlockIncStart */ : -1 /* InlineIncStart */) : 400000000 /* NonIncStart */) - 1;
1164
- let endSide = (end ? (block ? 200000000 /* BlockIncEnd */ : 1 /* InlineIncEnd */) : -500000000 /* NonIncEnd */) + 1;
1213
+ let block = !!spec.block, startSide, endSide;
1214
+ if (spec.isBlockGap) {
1215
+ startSide = -400000000 /* GapStart */;
1216
+ endSide = 300000000 /* GapEnd */;
1217
+ }
1218
+ else {
1219
+ let { start, end } = getInclusive(spec, block);
1220
+ startSide = (start ? (block ? -300000000 /* BlockIncStart */ : -1 /* InlineIncStart */) : 500000000 /* NonIncStart */) - 1;
1221
+ endSide = (end ? (block ? 200000000 /* BlockIncEnd */ : 1 /* InlineIncEnd */) : -600000000 /* NonIncEnd */) + 1;
1222
+ }
1165
1223
  return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
1166
1224
  }
1167
1225
  /**
@@ -1191,7 +1249,7 @@ Decoration.none = RangeSet.empty;
1191
1249
  class MarkDecoration extends Decoration {
1192
1250
  constructor(spec) {
1193
1251
  let { start, end } = getInclusive(spec);
1194
- super(start ? -1 /* InlineIncStart */ : 400000000 /* NonIncStart */, end ? 1 /* InlineIncEnd */ : -500000000 /* NonIncEnd */, null, spec);
1252
+ super(start ? -1 /* InlineIncStart */ : 500000000 /* NonIncStart */, end ? 1 /* InlineIncEnd */ : -600000000 /* NonIncEnd */, null, spec);
1195
1253
  this.tagName = spec.tagName || "span";
1196
1254
  this.class = spec.class || "";
1197
1255
  this.attrs = spec.attributes || null;
@@ -2769,7 +2827,12 @@ class DocView extends ContentView {
2769
2827
  let end = next ? next.from - 1 : this.length;
2770
2828
  if (end > pos) {
2771
2829
  let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
2772
- deco.push(Decoration.replace({ widget: new BlockGapWidget(height), block: true, inclusive: true }).range(pos, end));
2830
+ deco.push(Decoration.replace({
2831
+ widget: new BlockGapWidget(height),
2832
+ block: true,
2833
+ inclusive: true,
2834
+ isBlockGap: true,
2835
+ }).range(pos, end));
2773
2836
  }
2774
2837
  if (!next)
2775
2838
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "0.19.45",
3
+ "version": "0.19.46",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",