@codemirror/view 6.5.0 → 6.6.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 CHANGED
@@ -1,3 +1,17 @@
1
+ ## 6.6.0 (2022-11-24)
2
+
3
+ ### New features
4
+
5
+ The `layer` function can now be used to define extensions that draw DOM elements over or below the document text.
6
+
7
+ Tooltips that are bigger than the available vertical space for them will now have their height set so that they don't stick out of the window. The new `resize` property on `TooltipView` can be used to opt out of this behavior.
8
+
9
+ ## 6.5.1 (2022-11-15)
10
+
11
+ ### Bug fixes
12
+
13
+ Fix a bug that caused marked unnecessary splitting of mark decoration DOM elements in some cases.
14
+
1
15
  ## 6.5.0 (2022-11-14)
2
16
 
3
17
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -1669,7 +1669,7 @@ class ContentBuilder {
1669
1669
  }
1670
1670
  }
1671
1671
  let take = Math.min(this.text.length - this.textOff, length, 512 /* T.Chunk */);
1672
- this.flushBuffer(active.slice(0, openStart));
1672
+ this.flushBuffer(active.slice(active.length - openStart));
1673
1673
  this.getLine().append(wrapMarks(new TextView(this.text.slice(this.textOff, this.textOff + take)), active), openStart);
1674
1674
  this.atCursorPos = true;
1675
1675
  this.textOff += take;
@@ -5313,14 +5313,13 @@ const baseTheme$1 = buildTheme("." + baseThemeID, {
5313
5313
  "&dark .cm-content": { caretColor: "white" },
5314
5314
  ".cm-line": {
5315
5315
  display: "block",
5316
- padding: "0 2px 0 4px"
5316
+ padding: "0 2px 0 6px"
5317
5317
  },
5318
- ".cm-selectionLayer": {
5319
- zIndex: -1,
5320
- contain: "size style"
5321
- },
5322
- ".cm-selectionBackground": {
5323
- position: "absolute",
5318
+ ".cm-layer": {
5319
+ contain: "size style",
5320
+ "& > *": {
5321
+ position: "absolute"
5322
+ }
5324
5323
  },
5325
5324
  "&light .cm-selectionBackground": {
5326
5325
  background: "#d9d9d9"
@@ -5335,8 +5334,6 @@ const baseTheme$1 = buildTheme("." + baseThemeID, {
5335
5334
  background: "#233"
5336
5335
  },
5337
5336
  ".cm-cursorLayer": {
5338
- zIndex: 100,
5339
- contain: "size style",
5340
5337
  pointerEvents: "none"
5341
5338
  },
5342
5339
  "&.cm-focused .cm-cursorLayer": {
@@ -5348,7 +5345,6 @@ const baseTheme$1 = buildTheme("." + baseThemeID, {
5348
5345
  "@keyframes cm-blink": { "0%": {}, "50%": { opacity: 0 }, "100%": {} },
5349
5346
  "@keyframes cm-blink2": { "0%": {}, "50%": { opacity: 0 }, "100%": {} },
5350
5347
  ".cm-cursor, .cm-dropCursor": {
5351
- position: "absolute",
5352
5348
  borderLeft: "1.2px solid black",
5353
5349
  marginLeft: "-0.6px",
5354
5350
  pointerEvents: "none",
@@ -7294,6 +7290,117 @@ function runHandlers(map, event, view, scope) {
7294
7290
  return fallthrough;
7295
7291
  }
7296
7292
 
7293
+ /**
7294
+ Implementation of [`LayerMarker`](https://codemirror.net/6/docs/ref/#view.LayerMarker) that creates
7295
+ a rectangle at a given set of coordinates.
7296
+ */
7297
+ class RectangleMarker {
7298
+ /**
7299
+ Create a marker with the given class and dimensions.
7300
+ */
7301
+ constructor(className, left, top, width, height) {
7302
+ this.className = className;
7303
+ this.left = left;
7304
+ this.top = top;
7305
+ this.width = width;
7306
+ this.height = height;
7307
+ }
7308
+ draw() {
7309
+ let elt = document.createElement("div");
7310
+ elt.className = this.className;
7311
+ this.adjust(elt);
7312
+ return elt;
7313
+ }
7314
+ update(elt, prev) {
7315
+ if (prev.className != this.className)
7316
+ return false;
7317
+ this.adjust(elt);
7318
+ return true;
7319
+ }
7320
+ adjust(elt) {
7321
+ elt.style.left = this.left + "px";
7322
+ elt.style.top = this.top + "px";
7323
+ if (this.width >= 0)
7324
+ elt.style.width = this.width + "px";
7325
+ elt.style.height = this.height + "px";
7326
+ }
7327
+ eq(p) {
7328
+ return this.left == p.left && this.top == p.top && this.width == p.width && this.height == p.height &&
7329
+ this.className == p.className;
7330
+ }
7331
+ }
7332
+ function sameMarker(a, b) {
7333
+ return a.constructor == b.constructor && a.eq(b);
7334
+ }
7335
+ class LayerView {
7336
+ constructor(view, layer) {
7337
+ this.view = view;
7338
+ this.layer = layer;
7339
+ this.drawn = [];
7340
+ this.measureReq = { read: this.measure.bind(this), write: this.draw.bind(this) };
7341
+ this.dom = view.scrollDOM.appendChild(document.createElement("div"));
7342
+ this.dom.classList.add("cm-layer");
7343
+ if (layer.above)
7344
+ this.dom.classList.add("cm-layer-above");
7345
+ if (layer.class)
7346
+ this.dom.classList.add(layer.class);
7347
+ this.dom.setAttribute("aria-hidden", "true");
7348
+ this.setOrder(view.state);
7349
+ view.requestMeasure(this.measureReq);
7350
+ if (layer.mount)
7351
+ layer.mount(this.dom, view);
7352
+ }
7353
+ update(update) {
7354
+ if (update.startState.facet(layerOrder) != update.state.facet(layerOrder))
7355
+ this.setOrder(update.state);
7356
+ if (this.layer.update(update, this.dom) || update.geometryChanged)
7357
+ update.view.requestMeasure(this.measureReq);
7358
+ }
7359
+ setOrder(state) {
7360
+ let pos = 0, order = state.facet(layerOrder);
7361
+ while (pos < order.length && order[pos] != this.layer)
7362
+ pos++;
7363
+ this.dom.style.zIndex = String((this.layer.above ? 150 : -1) - pos);
7364
+ }
7365
+ measure() {
7366
+ return this.layer.markers(this.view);
7367
+ }
7368
+ draw(markers) {
7369
+ if (markers.length != this.drawn.length || markers.some((p, i) => !sameMarker(p, this.drawn[i]))) {
7370
+ let old = this.dom.firstChild, oldI = 0;
7371
+ for (let marker of markers) {
7372
+ if (marker.update && old && marker.constructor && this.drawn[oldI].constructor &&
7373
+ marker.update(old, this.drawn[oldI])) {
7374
+ old = old.nextSibling;
7375
+ oldI++;
7376
+ }
7377
+ else {
7378
+ this.dom.insertBefore(marker.draw(), old);
7379
+ }
7380
+ }
7381
+ while (old) {
7382
+ let next = old.nextSibling;
7383
+ old.remove();
7384
+ old = next;
7385
+ }
7386
+ this.drawn = markers;
7387
+ }
7388
+ }
7389
+ destroy() {
7390
+ this.dom.remove();
7391
+ }
7392
+ }
7393
+ const layerOrder = state.Facet.define();
7394
+ /**
7395
+ Define a layer.
7396
+ */
7397
+ function layer(config) {
7398
+ return [
7399
+ ViewPlugin.define(v => new LayerView(v, config)),
7400
+ layerOrder.of(config)
7401
+ ];
7402
+ }
7403
+
7297
7404
  const CanHidePrimary = !browser.ios; // FIXME test IE
7298
7405
  const selectionConfig = state.Facet.define({
7299
7406
  combine(configs) {
@@ -7327,102 +7434,55 @@ content).
7327
7434
  function drawSelection(config = {}) {
7328
7435
  return [
7329
7436
  selectionConfig.of(config),
7330
- drawSelectionPlugin,
7437
+ cursorLayer,
7438
+ selectionLayer,
7331
7439
  hideNativeSelection,
7332
7440
  nativeSelectionHidden.of(true)
7333
7441
  ];
7334
7442
  }
7335
- class Piece {
7336
- constructor(left, top, width, height, className) {
7337
- this.left = left;
7338
- this.top = top;
7339
- this.width = width;
7340
- this.height = height;
7341
- this.className = className;
7342
- }
7343
- draw() {
7344
- let elt = document.createElement("div");
7345
- elt.className = this.className;
7346
- this.adjust(elt);
7347
- return elt;
7348
- }
7349
- adjust(elt) {
7350
- elt.style.left = this.left + "px";
7351
- elt.style.top = this.top + "px";
7352
- if (this.width >= 0)
7353
- elt.style.width = this.width + "px";
7354
- elt.style.height = this.height + "px";
7355
- }
7356
- eq(p) {
7357
- return this.left == p.left && this.top == p.top && this.width == p.width && this.height == p.height &&
7358
- this.className == p.className;
7359
- }
7443
+ function configChanged(update) {
7444
+ return update.startState.facet(selectionConfig) != update.startState.facet(selectionConfig);
7360
7445
  }
7361
- const drawSelectionPlugin = ViewPlugin.fromClass(class {
7362
- constructor(view) {
7363
- this.view = view;
7364
- this.rangePieces = [];
7365
- this.cursors = [];
7366
- this.measureReq = { read: this.readPos.bind(this), write: this.drawSel.bind(this) };
7367
- this.selectionLayer = view.scrollDOM.appendChild(document.createElement("div"));
7368
- this.selectionLayer.className = "cm-selectionLayer";
7369
- this.selectionLayer.setAttribute("aria-hidden", "true");
7370
- this.cursorLayer = view.scrollDOM.appendChild(document.createElement("div"));
7371
- this.cursorLayer.className = "cm-cursorLayer";
7372
- this.cursorLayer.setAttribute("aria-hidden", "true");
7373
- view.requestMeasure(this.measureReq);
7374
- this.setBlinkRate();
7375
- }
7376
- setBlinkRate() {
7377
- this.cursorLayer.style.animationDuration = this.view.state.facet(selectionConfig).cursorBlinkRate + "ms";
7378
- }
7379
- update(update) {
7380
- let confChanged = update.startState.facet(selectionConfig) != update.state.facet(selectionConfig);
7381
- if (confChanged || update.selectionSet || update.geometryChanged || update.viewportChanged)
7382
- this.view.requestMeasure(this.measureReq);
7383
- if (update.transactions.some(tr => tr.scrollIntoView))
7384
- this.cursorLayer.style.animationName = this.cursorLayer.style.animationName == "cm-blink" ? "cm-blink2" : "cm-blink";
7385
- if (confChanged)
7386
- this.setBlinkRate();
7387
- }
7388
- readPos() {
7389
- let { state } = this.view, conf = state.facet(selectionConfig);
7390
- let rangePieces = state.selection.ranges.map(r => r.empty ? [] : measureRange(this.view, r)).reduce((a, b) => a.concat(b));
7446
+ const cursorLayer = layer({
7447
+ above: true,
7448
+ markers(view) {
7449
+ let { state } = view, conf = state.facet(selectionConfig);
7391
7450
  let cursors = [];
7392
7451
  for (let r of state.selection.ranges) {
7393
7452
  let prim = r == state.selection.main;
7394
7453
  if (r.empty ? !prim || CanHidePrimary : conf.drawRangeCursor) {
7395
- let piece = measureCursor(this.view, r, prim);
7454
+ let piece = measureCursor(view, r, prim);
7396
7455
  if (piece)
7397
7456
  cursors.push(piece);
7398
7457
  }
7399
7458
  }
7400
- return { rangePieces, cursors };
7401
- }
7402
- drawSel({ rangePieces, cursors }) {
7403
- if (rangePieces.length != this.rangePieces.length || rangePieces.some((p, i) => !p.eq(this.rangePieces[i]))) {
7404
- this.selectionLayer.textContent = "";
7405
- for (let p of rangePieces)
7406
- this.selectionLayer.appendChild(p.draw());
7407
- this.rangePieces = rangePieces;
7408
- }
7409
- if (cursors.length != this.cursors.length || cursors.some((c, i) => !c.eq(this.cursors[i]))) {
7410
- let oldCursors = this.cursorLayer.children;
7411
- if (oldCursors.length !== cursors.length) {
7412
- this.cursorLayer.textContent = "";
7413
- for (const c of cursors)
7414
- this.cursorLayer.appendChild(c.draw());
7415
- }
7416
- else {
7417
- cursors.forEach((c, idx) => c.adjust(oldCursors[idx]));
7418
- }
7419
- this.cursors = cursors;
7420
- }
7421
- }
7422
- destroy() {
7423
- this.selectionLayer.remove();
7424
- this.cursorLayer.remove();
7425
- }
7459
+ return cursors;
7460
+ },
7461
+ update(update, dom) {
7462
+ if (update.transactions.some(tr => tr.scrollIntoView))
7463
+ dom.style.animationName = dom.style.animationName == "cm-blink" ? "cm-blink2" : "cm-blink";
7464
+ let confChange = configChanged(update);
7465
+ if (confChange)
7466
+ setBlinkRate(update.state, dom);
7467
+ return update.docChanged || update.selectionSet || confChange;
7468
+ },
7469
+ mount(dom, view) {
7470
+ setBlinkRate(view.state, dom);
7471
+ },
7472
+ class: "cm-cursorLayer"
7473
+ });
7474
+ function setBlinkRate(state, dom) {
7475
+ dom.style.animationDuration = state.facet(selectionConfig).cursorBlinkRate + "ms";
7476
+ }
7477
+ const selectionLayer = layer({
7478
+ above: false,
7479
+ markers(view) {
7480
+ return view.state.selection.ranges.map(r => r.empty ? [] : measureRange(view, r)).reduce((a, b) => a.concat(b));
7481
+ },
7482
+ update(update, dom) {
7483
+ return update.docChanged || update.selectionSet || update.viewportChanged || configChanged(update);
7484
+ },
7485
+ class: "cm-selectionLayer"
7426
7486
  });
7427
7487
  const themeSpec = {
7428
7488
  ".cm-line": {
@@ -7485,7 +7545,7 @@ function measureRange(view, range) {
7485
7545
  return pieces(top).concat(between).concat(pieces(bottom));
7486
7546
  }
7487
7547
  function piece(left, top, right, bottom) {
7488
- return new Piece(left - base.left, top - base.top - 0.01 /* C.Epsilon */, right - left, bottom - top + 0.01 /* C.Epsilon */, "cm-selectionBackground");
7548
+ return new RectangleMarker("cm-selectionBackground", left - base.left, top - base.top - 0.01 /* C.Epsilon */, right - left, bottom - top + 0.01 /* C.Epsilon */);
7489
7549
  }
7490
7550
  function pieces({ top, bottom, horizontal }) {
7491
7551
  let pieces = [];
@@ -7542,7 +7602,7 @@ function measureCursor(view, cursor, primary) {
7542
7602
  if (!pos)
7543
7603
  return null;
7544
7604
  let base = getBase(view);
7545
- return new Piece(pos.left - base.left, pos.top - base.top, -1, pos.bottom - pos.top, primary ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary");
7605
+ return new RectangleMarker(primary ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary", pos.left - base.left, pos.top - base.top, -1, pos.bottom - pos.top);
7546
7606
  }
7547
7607
 
7548
7608
  const setDropCursorPos = state.StateEffect.define({
@@ -8336,6 +8396,17 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
8336
8396
  : pos.bottom + (size.bottom - size.top) + offset.y > space.bottom) &&
8337
8397
  above == (space.bottom - pos.bottom > pos.top - space.top))
8338
8398
  above = !above;
8399
+ let spaceVert = (above ? pos.top - space.top : space.bottom - pos.bottom) - arrowHeight;
8400
+ if (spaceVert < height && tView.resize !== false) {
8401
+ if (spaceVert < this.view.defaultLineHeight) {
8402
+ dom.style.top = Outside;
8403
+ continue;
8404
+ }
8405
+ dom.style.height = (height = spaceVert) + "px";
8406
+ }
8407
+ else if (dom.style.height) {
8408
+ dom.style.height = "";
8409
+ }
8339
8410
  let top = above ? pos.top - height - arrowHeight - offset.y : pos.bottom + arrowHeight + offset.y;
8340
8411
  let right = left + width;
8341
8412
  if (tView.overlap !== true)
@@ -8379,7 +8450,8 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
8379
8450
  });
8380
8451
  const baseTheme = EditorView.baseTheme({
8381
8452
  ".cm-tooltip": {
8382
- zIndex: 100
8453
+ zIndex: 100,
8454
+ boxSizing: "border-box"
8383
8455
  },
8384
8456
  "&light .cm-tooltip": {
8385
8457
  border: "1px solid #bbb",
@@ -9309,6 +9381,7 @@ exports.Decoration = Decoration;
9309
9381
  exports.EditorView = EditorView;
9310
9382
  exports.GutterMarker = GutterMarker;
9311
9383
  exports.MatchDecorator = MatchDecorator;
9384
+ exports.RectangleMarker = RectangleMarker;
9312
9385
  exports.ViewPlugin = ViewPlugin;
9313
9386
  exports.ViewUpdate = ViewUpdate;
9314
9387
  exports.WidgetType = WidgetType;
@@ -9328,6 +9401,7 @@ exports.highlightActiveLineGutter = highlightActiveLineGutter;
9328
9401
  exports.highlightSpecialChars = highlightSpecialChars;
9329
9402
  exports.hoverTooltip = hoverTooltip;
9330
9403
  exports.keymap = keymap;
9404
+ exports.layer = layer;
9331
9405
  exports.lineNumberMarkers = lineNumberMarkers;
9332
9406
  exports.lineNumbers = lineNumbers;
9333
9407
  exports.logException = logException;
package/dist/index.d.ts CHANGED
@@ -1366,6 +1366,80 @@ to show when the editor is empty.
1366
1366
  */
1367
1367
  declare function placeholder(content: string | HTMLElement): Extension;
1368
1368
 
1369
+ /**
1370
+ Markers shown in a [layer](https://codemirror.net/6/docs/ref/#view.layer) must conform to this
1371
+ interface. They are created in a measuring phase, and have to
1372
+ contain all their positioning information, so that they can be
1373
+ drawn without further DOM layout reading.
1374
+
1375
+ Markers are automatically absolutely positioned. Their parent
1376
+ element has the same top-left corner as the document, so they
1377
+ should be positioned relative to the document.
1378
+ */
1379
+ interface LayerMarker {
1380
+ /**
1381
+ Compare this marker to a marker of the same type. Used to avoid
1382
+ unnecessary redraws.
1383
+ */
1384
+ eq(other: LayerMarker): boolean;
1385
+ /**
1386
+ Draw the marker to the DOM.
1387
+ */
1388
+ draw(): HTMLElement;
1389
+ /**
1390
+ Update an existing marker of this type to this marker.
1391
+ */
1392
+ update?(dom: HTMLElement, oldMarker: LayerMarker): boolean;
1393
+ }
1394
+ /**
1395
+ Implementation of [`LayerMarker`](https://codemirror.net/6/docs/ref/#view.LayerMarker) that creates
1396
+ a rectangle at a given set of coordinates.
1397
+ */
1398
+ declare class RectangleMarker implements LayerMarker {
1399
+ private className;
1400
+ private left;
1401
+ private top;
1402
+ private width;
1403
+ private height;
1404
+ /**
1405
+ Create a marker with the given class and dimensions.
1406
+ */
1407
+ constructor(className: string, left: number, top: number, width: number, height: number);
1408
+ draw(): HTMLDivElement;
1409
+ update(elt: HTMLElement, prev: RectangleMarker): boolean;
1410
+ private adjust;
1411
+ eq(p: RectangleMarker): boolean;
1412
+ }
1413
+ interface LayerConfig {
1414
+ /**
1415
+ Determines whether this layer is shown above or below the text.
1416
+ */
1417
+ above: boolean;
1418
+ /**
1419
+ When given, this class is added to the DOM element that will
1420
+ wrap the markers.
1421
+ */
1422
+ class?: string;
1423
+ /**
1424
+ Called on every view update. Returning true triggers a marker
1425
+ update (a call to `markers` and drawing of those markers).
1426
+ */
1427
+ update(update: ViewUpdate, layer: HTMLElement): boolean;
1428
+ /**
1429
+ Build a set of markers for this layer, and measure their
1430
+ dimensions.
1431
+ */
1432
+ markers(view: EditorView): readonly LayerMarker[];
1433
+ /**
1434
+ If given, this is called when the layer is created.
1435
+ */
1436
+ mount?(layer: HTMLElement, view: EditorView): void;
1437
+ }
1438
+ /**
1439
+ Define a layer.
1440
+ */
1441
+ declare function layer(config: LayerConfig): Extension;
1442
+
1369
1443
  /**
1370
1444
  Helper class used to make it easier to maintain decorations on
1371
1445
  visible code that matches a given regular expression. To be used
@@ -1584,6 +1658,12 @@ interface TooltipView {
1584
1658
  tooltip.
1585
1659
  */
1586
1660
  positioned?(space: Rect): void;
1661
+ /**
1662
+ By default, the library will restrict the size of tooltips so
1663
+ that they don't stick out of the available space. Set this to
1664
+ false to disable that.
1665
+ */
1666
+ resize?: boolean;
1587
1667
  }
1588
1668
  /**
1589
1669
  Facet to which an extension can add a value to show a tooltip.
@@ -1820,4 +1900,4 @@ line](https://codemirror.net/6/docs/ref/#view.highlightActiveLine).
1820
1900
  */
1821
1901
  declare function highlightActiveLineGutter(): Extension;
1822
1902
 
1823
- export { BidiSpan, BlockInfo, BlockType, Command, DOMEventHandlers, DOMEventMap, Decoration, DecorationSet, Direction, EditorView, EditorViewConfig, GutterMarker, KeyBinding, MatchDecorator, MouseSelectionStyle, Panel, PanelConstructor, PluginSpec, PluginValue, Rect, Tooltip, TooltipView, ViewPlugin, ViewUpdate, WidgetType, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getPanel, getTooltip, gutter, gutterLineClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, hoverTooltip, keymap, lineNumberMarkers, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showPanel, showTooltip, tooltips };
1903
+ export { BidiSpan, BlockInfo, BlockType, Command, DOMEventHandlers, DOMEventMap, Decoration, DecorationSet, Direction, EditorView, EditorViewConfig, GutterMarker, KeyBinding, LayerMarker, MatchDecorator, MouseSelectionStyle, Panel, PanelConstructor, PluginSpec, PluginValue, Rect, RectangleMarker, Tooltip, TooltipView, ViewPlugin, ViewUpdate, WidgetType, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getPanel, getTooltip, gutter, gutterLineClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, hoverTooltip, keymap, layer, lineNumberMarkers, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showPanel, showTooltip, tooltips };
package/dist/index.js CHANGED
@@ -1664,7 +1664,7 @@ class ContentBuilder {
1664
1664
  }
1665
1665
  }
1666
1666
  let take = Math.min(this.text.length - this.textOff, length, 512 /* T.Chunk */);
1667
- this.flushBuffer(active.slice(0, openStart));
1667
+ this.flushBuffer(active.slice(active.length - openStart));
1668
1668
  this.getLine().append(wrapMarks(new TextView(this.text.slice(this.textOff, this.textOff + take)), active), openStart);
1669
1669
  this.atCursorPos = true;
1670
1670
  this.textOff += take;
@@ -5306,14 +5306,13 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
5306
5306
  "&dark .cm-content": { caretColor: "white" },
5307
5307
  ".cm-line": {
5308
5308
  display: "block",
5309
- padding: "0 2px 0 4px"
5309
+ padding: "0 2px 0 6px"
5310
5310
  },
5311
- ".cm-selectionLayer": {
5312
- zIndex: -1,
5313
- contain: "size style"
5314
- },
5315
- ".cm-selectionBackground": {
5316
- position: "absolute",
5311
+ ".cm-layer": {
5312
+ contain: "size style",
5313
+ "& > *": {
5314
+ position: "absolute"
5315
+ }
5317
5316
  },
5318
5317
  "&light .cm-selectionBackground": {
5319
5318
  background: "#d9d9d9"
@@ -5328,8 +5327,6 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
5328
5327
  background: "#233"
5329
5328
  },
5330
5329
  ".cm-cursorLayer": {
5331
- zIndex: 100,
5332
- contain: "size style",
5333
5330
  pointerEvents: "none"
5334
5331
  },
5335
5332
  "&.cm-focused .cm-cursorLayer": {
@@ -5341,7 +5338,6 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
5341
5338
  "@keyframes cm-blink": { "0%": {}, "50%": { opacity: 0 }, "100%": {} },
5342
5339
  "@keyframes cm-blink2": { "0%": {}, "50%": { opacity: 0 }, "100%": {} },
5343
5340
  ".cm-cursor, .cm-dropCursor": {
5344
- position: "absolute",
5345
5341
  borderLeft: "1.2px solid black",
5346
5342
  marginLeft: "-0.6px",
5347
5343
  pointerEvents: "none",
@@ -7287,6 +7283,117 @@ function runHandlers(map, event, view, scope) {
7287
7283
  return fallthrough;
7288
7284
  }
7289
7285
 
7286
+ /**
7287
+ Implementation of [`LayerMarker`](https://codemirror.net/6/docs/ref/#view.LayerMarker) that creates
7288
+ a rectangle at a given set of coordinates.
7289
+ */
7290
+ class RectangleMarker {
7291
+ /**
7292
+ Create a marker with the given class and dimensions.
7293
+ */
7294
+ constructor(className, left, top, width, height) {
7295
+ this.className = className;
7296
+ this.left = left;
7297
+ this.top = top;
7298
+ this.width = width;
7299
+ this.height = height;
7300
+ }
7301
+ draw() {
7302
+ let elt = document.createElement("div");
7303
+ elt.className = this.className;
7304
+ this.adjust(elt);
7305
+ return elt;
7306
+ }
7307
+ update(elt, prev) {
7308
+ if (prev.className != this.className)
7309
+ return false;
7310
+ this.adjust(elt);
7311
+ return true;
7312
+ }
7313
+ adjust(elt) {
7314
+ elt.style.left = this.left + "px";
7315
+ elt.style.top = this.top + "px";
7316
+ if (this.width >= 0)
7317
+ elt.style.width = this.width + "px";
7318
+ elt.style.height = this.height + "px";
7319
+ }
7320
+ eq(p) {
7321
+ return this.left == p.left && this.top == p.top && this.width == p.width && this.height == p.height &&
7322
+ this.className == p.className;
7323
+ }
7324
+ }
7325
+ function sameMarker(a, b) {
7326
+ return a.constructor == b.constructor && a.eq(b);
7327
+ }
7328
+ class LayerView {
7329
+ constructor(view, layer) {
7330
+ this.view = view;
7331
+ this.layer = layer;
7332
+ this.drawn = [];
7333
+ this.measureReq = { read: this.measure.bind(this), write: this.draw.bind(this) };
7334
+ this.dom = view.scrollDOM.appendChild(document.createElement("div"));
7335
+ this.dom.classList.add("cm-layer");
7336
+ if (layer.above)
7337
+ this.dom.classList.add("cm-layer-above");
7338
+ if (layer.class)
7339
+ this.dom.classList.add(layer.class);
7340
+ this.dom.setAttribute("aria-hidden", "true");
7341
+ this.setOrder(view.state);
7342
+ view.requestMeasure(this.measureReq);
7343
+ if (layer.mount)
7344
+ layer.mount(this.dom, view);
7345
+ }
7346
+ update(update) {
7347
+ if (update.startState.facet(layerOrder) != update.state.facet(layerOrder))
7348
+ this.setOrder(update.state);
7349
+ if (this.layer.update(update, this.dom) || update.geometryChanged)
7350
+ update.view.requestMeasure(this.measureReq);
7351
+ }
7352
+ setOrder(state) {
7353
+ let pos = 0, order = state.facet(layerOrder);
7354
+ while (pos < order.length && order[pos] != this.layer)
7355
+ pos++;
7356
+ this.dom.style.zIndex = String((this.layer.above ? 150 : -1) - pos);
7357
+ }
7358
+ measure() {
7359
+ return this.layer.markers(this.view);
7360
+ }
7361
+ draw(markers) {
7362
+ if (markers.length != this.drawn.length || markers.some((p, i) => !sameMarker(p, this.drawn[i]))) {
7363
+ let old = this.dom.firstChild, oldI = 0;
7364
+ for (let marker of markers) {
7365
+ if (marker.update && old && marker.constructor && this.drawn[oldI].constructor &&
7366
+ marker.update(old, this.drawn[oldI])) {
7367
+ old = old.nextSibling;
7368
+ oldI++;
7369
+ }
7370
+ else {
7371
+ this.dom.insertBefore(marker.draw(), old);
7372
+ }
7373
+ }
7374
+ while (old) {
7375
+ let next = old.nextSibling;
7376
+ old.remove();
7377
+ old = next;
7378
+ }
7379
+ this.drawn = markers;
7380
+ }
7381
+ }
7382
+ destroy() {
7383
+ this.dom.remove();
7384
+ }
7385
+ }
7386
+ const layerOrder = /*@__PURE__*/Facet.define();
7387
+ /**
7388
+ Define a layer.
7389
+ */
7390
+ function layer(config) {
7391
+ return [
7392
+ ViewPlugin.define(v => new LayerView(v, config)),
7393
+ layerOrder.of(config)
7394
+ ];
7395
+ }
7396
+
7290
7397
  const CanHidePrimary = !browser.ios; // FIXME test IE
7291
7398
  const selectionConfig = /*@__PURE__*/Facet.define({
7292
7399
  combine(configs) {
@@ -7320,102 +7427,55 @@ content).
7320
7427
  function drawSelection(config = {}) {
7321
7428
  return [
7322
7429
  selectionConfig.of(config),
7323
- drawSelectionPlugin,
7430
+ cursorLayer,
7431
+ selectionLayer,
7324
7432
  hideNativeSelection,
7325
7433
  nativeSelectionHidden.of(true)
7326
7434
  ];
7327
7435
  }
7328
- class Piece {
7329
- constructor(left, top, width, height, className) {
7330
- this.left = left;
7331
- this.top = top;
7332
- this.width = width;
7333
- this.height = height;
7334
- this.className = className;
7335
- }
7336
- draw() {
7337
- let elt = document.createElement("div");
7338
- elt.className = this.className;
7339
- this.adjust(elt);
7340
- return elt;
7341
- }
7342
- adjust(elt) {
7343
- elt.style.left = this.left + "px";
7344
- elt.style.top = this.top + "px";
7345
- if (this.width >= 0)
7346
- elt.style.width = this.width + "px";
7347
- elt.style.height = this.height + "px";
7348
- }
7349
- eq(p) {
7350
- return this.left == p.left && this.top == p.top && this.width == p.width && this.height == p.height &&
7351
- this.className == p.className;
7352
- }
7436
+ function configChanged(update) {
7437
+ return update.startState.facet(selectionConfig) != update.startState.facet(selectionConfig);
7353
7438
  }
7354
- const drawSelectionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
7355
- constructor(view) {
7356
- this.view = view;
7357
- this.rangePieces = [];
7358
- this.cursors = [];
7359
- this.measureReq = { read: this.readPos.bind(this), write: this.drawSel.bind(this) };
7360
- this.selectionLayer = view.scrollDOM.appendChild(document.createElement("div"));
7361
- this.selectionLayer.className = "cm-selectionLayer";
7362
- this.selectionLayer.setAttribute("aria-hidden", "true");
7363
- this.cursorLayer = view.scrollDOM.appendChild(document.createElement("div"));
7364
- this.cursorLayer.className = "cm-cursorLayer";
7365
- this.cursorLayer.setAttribute("aria-hidden", "true");
7366
- view.requestMeasure(this.measureReq);
7367
- this.setBlinkRate();
7368
- }
7369
- setBlinkRate() {
7370
- this.cursorLayer.style.animationDuration = this.view.state.facet(selectionConfig).cursorBlinkRate + "ms";
7371
- }
7372
- update(update) {
7373
- let confChanged = update.startState.facet(selectionConfig) != update.state.facet(selectionConfig);
7374
- if (confChanged || update.selectionSet || update.geometryChanged || update.viewportChanged)
7375
- this.view.requestMeasure(this.measureReq);
7376
- if (update.transactions.some(tr => tr.scrollIntoView))
7377
- this.cursorLayer.style.animationName = this.cursorLayer.style.animationName == "cm-blink" ? "cm-blink2" : "cm-blink";
7378
- if (confChanged)
7379
- this.setBlinkRate();
7380
- }
7381
- readPos() {
7382
- let { state } = this.view, conf = state.facet(selectionConfig);
7383
- let rangePieces = state.selection.ranges.map(r => r.empty ? [] : measureRange(this.view, r)).reduce((a, b) => a.concat(b));
7439
+ const cursorLayer = /*@__PURE__*/layer({
7440
+ above: true,
7441
+ markers(view) {
7442
+ let { state } = view, conf = state.facet(selectionConfig);
7384
7443
  let cursors = [];
7385
7444
  for (let r of state.selection.ranges) {
7386
7445
  let prim = r == state.selection.main;
7387
7446
  if (r.empty ? !prim || CanHidePrimary : conf.drawRangeCursor) {
7388
- let piece = measureCursor(this.view, r, prim);
7447
+ let piece = measureCursor(view, r, prim);
7389
7448
  if (piece)
7390
7449
  cursors.push(piece);
7391
7450
  }
7392
7451
  }
7393
- return { rangePieces, cursors };
7394
- }
7395
- drawSel({ rangePieces, cursors }) {
7396
- if (rangePieces.length != this.rangePieces.length || rangePieces.some((p, i) => !p.eq(this.rangePieces[i]))) {
7397
- this.selectionLayer.textContent = "";
7398
- for (let p of rangePieces)
7399
- this.selectionLayer.appendChild(p.draw());
7400
- this.rangePieces = rangePieces;
7401
- }
7402
- if (cursors.length != this.cursors.length || cursors.some((c, i) => !c.eq(this.cursors[i]))) {
7403
- let oldCursors = this.cursorLayer.children;
7404
- if (oldCursors.length !== cursors.length) {
7405
- this.cursorLayer.textContent = "";
7406
- for (const c of cursors)
7407
- this.cursorLayer.appendChild(c.draw());
7408
- }
7409
- else {
7410
- cursors.forEach((c, idx) => c.adjust(oldCursors[idx]));
7411
- }
7412
- this.cursors = cursors;
7413
- }
7414
- }
7415
- destroy() {
7416
- this.selectionLayer.remove();
7417
- this.cursorLayer.remove();
7418
- }
7452
+ return cursors;
7453
+ },
7454
+ update(update, dom) {
7455
+ if (update.transactions.some(tr => tr.scrollIntoView))
7456
+ dom.style.animationName = dom.style.animationName == "cm-blink" ? "cm-blink2" : "cm-blink";
7457
+ let confChange = configChanged(update);
7458
+ if (confChange)
7459
+ setBlinkRate(update.state, dom);
7460
+ return update.docChanged || update.selectionSet || confChange;
7461
+ },
7462
+ mount(dom, view) {
7463
+ setBlinkRate(view.state, dom);
7464
+ },
7465
+ class: "cm-cursorLayer"
7466
+ });
7467
+ function setBlinkRate(state, dom) {
7468
+ dom.style.animationDuration = state.facet(selectionConfig).cursorBlinkRate + "ms";
7469
+ }
7470
+ const selectionLayer = /*@__PURE__*/layer({
7471
+ above: false,
7472
+ markers(view) {
7473
+ return view.state.selection.ranges.map(r => r.empty ? [] : measureRange(view, r)).reduce((a, b) => a.concat(b));
7474
+ },
7475
+ update(update, dom) {
7476
+ return update.docChanged || update.selectionSet || update.viewportChanged || configChanged(update);
7477
+ },
7478
+ class: "cm-selectionLayer"
7419
7479
  });
7420
7480
  const themeSpec = {
7421
7481
  ".cm-line": {
@@ -7478,7 +7538,7 @@ function measureRange(view, range) {
7478
7538
  return pieces(top).concat(between).concat(pieces(bottom));
7479
7539
  }
7480
7540
  function piece(left, top, right, bottom) {
7481
- return new Piece(left - base.left, top - base.top - 0.01 /* C.Epsilon */, right - left, bottom - top + 0.01 /* C.Epsilon */, "cm-selectionBackground");
7541
+ return new RectangleMarker("cm-selectionBackground", left - base.left, top - base.top - 0.01 /* C.Epsilon */, right - left, bottom - top + 0.01 /* C.Epsilon */);
7482
7542
  }
7483
7543
  function pieces({ top, bottom, horizontal }) {
7484
7544
  let pieces = [];
@@ -7535,7 +7595,7 @@ function measureCursor(view, cursor, primary) {
7535
7595
  if (!pos)
7536
7596
  return null;
7537
7597
  let base = getBase(view);
7538
- return new Piece(pos.left - base.left, pos.top - base.top, -1, pos.bottom - pos.top, primary ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary");
7598
+ return new RectangleMarker(primary ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary", pos.left - base.left, pos.top - base.top, -1, pos.bottom - pos.top);
7539
7599
  }
7540
7600
 
7541
7601
  const setDropCursorPos = /*@__PURE__*/StateEffect.define({
@@ -8329,6 +8389,17 @@ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
8329
8389
  : pos.bottom + (size.bottom - size.top) + offset.y > space.bottom) &&
8330
8390
  above == (space.bottom - pos.bottom > pos.top - space.top))
8331
8391
  above = !above;
8392
+ let spaceVert = (above ? pos.top - space.top : space.bottom - pos.bottom) - arrowHeight;
8393
+ if (spaceVert < height && tView.resize !== false) {
8394
+ if (spaceVert < this.view.defaultLineHeight) {
8395
+ dom.style.top = Outside;
8396
+ continue;
8397
+ }
8398
+ dom.style.height = (height = spaceVert) + "px";
8399
+ }
8400
+ else if (dom.style.height) {
8401
+ dom.style.height = "";
8402
+ }
8332
8403
  let top = above ? pos.top - height - arrowHeight - offset.y : pos.bottom + arrowHeight + offset.y;
8333
8404
  let right = left + width;
8334
8405
  if (tView.overlap !== true)
@@ -8372,7 +8443,8 @@ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
8372
8443
  });
8373
8444
  const baseTheme = /*@__PURE__*/EditorView.baseTheme({
8374
8445
  ".cm-tooltip": {
8375
- zIndex: 100
8446
+ zIndex: 100,
8447
+ boxSizing: "border-box"
8376
8448
  },
8377
8449
  "&light .cm-tooltip": {
8378
8450
  border: "1px solid #bbb",
@@ -9296,4 +9368,4 @@ function highlightActiveLineGutter() {
9296
9368
  */
9297
9369
  const __test = { HeightMap, HeightOracle, MeasuredHeights, QueryType, ChangedRange, computeOrder, moveVisually };
9298
9370
 
9299
- export { BidiSpan, BlockInfo, BlockType, Decoration, Direction, EditorView, GutterMarker, MatchDecorator, ViewPlugin, ViewUpdate, WidgetType, __test, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getPanel, getTooltip, gutter, gutterLineClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, hoverTooltip, keymap, lineNumberMarkers, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showPanel, showTooltip, tooltips };
9371
+ export { BidiSpan, BlockInfo, BlockType, Decoration, Direction, EditorView, GutterMarker, MatchDecorator, RectangleMarker, ViewPlugin, ViewUpdate, WidgetType, __test, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getPanel, getTooltip, gutter, gutterLineClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, hoverTooltip, keymap, layer, lineNumberMarkers, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showPanel, showTooltip, tooltips };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.5.0",
3
+ "version": "6.6.0",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",
@@ -26,7 +26,7 @@
26
26
  "sideEffects": false,
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
- "@codemirror/state": "^6.0.0",
29
+ "@codemirror/state": "^6.1.4",
30
30
  "style-mod": "^4.0.0",
31
31
  "w3c-keyname": "^2.2.4"
32
32
  },