line_widget.min.js (2831B)
1 import { runInOp } from "../display/operations.js" 2 import { addToScrollTop } from "../display/scrolling.js" 3 import { regLineChange } from "../display/view_tracking.js" 4 import { heightAtLine, lineIsHidden } from "../line/spans.js" 5 import { lineNo, updateLineHeight } from "../line/utils_line.js" 6 import { widgetHeight } from "../measurement/widgets.js" 7 import { changeLine } from "./changes.js" 8 import { eventMixin } from "../util/event.js" 9 import { signalLater } from "../util/operation_group.js" 10 11 // Line widgets are block elements displayed above or below a line. 12 13 export class LineWidget { 14 constructor(doc, node, options) { 15 if (options) for (let opt in options) if (options.hasOwnProperty(opt)) 16 this[opt] = options[opt] 17 this.doc = doc 18 this.node = node 19 } 20 21 clear() { 22 let cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line) 23 if (no == null || !ws) return 24 for (let i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1) 25 if (!ws.length) line.widgets = null 26 let height = widgetHeight(this) 27 updateLineHeight(line, Math.max(0, line.height - height)) 28 if (cm) { 29 runInOp(cm, () => { 30 adjustScrollWhenAboveVisible(cm, line, -height) 31 regLineChange(cm, no, "widget") 32 }) 33 signalLater(cm, "lineWidgetCleared", cm, this, no) 34 } 35 } 36 37 changed() { 38 let oldH = this.height, cm = this.doc.cm, line = this.line 39 this.height = null 40 let diff = widgetHeight(this) - oldH 41 if (!diff) return 42 updateLineHeight(line, line.height + diff) 43 if (cm) { 44 runInOp(cm, () => { 45 cm.curOp.forceUpdate = true 46 adjustScrollWhenAboveVisible(cm, line, diff) 47 signalLater(cm, "lineWidgetChanged", cm, this, lineNo(line)) 48 }) 49 } 50 } 51 } 52 eventMixin(LineWidget) 53 54 function adjustScrollWhenAboveVisible(cm, line, diff) { 55 if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) 56 addToScrollTop(cm, diff) 57 } 58 59 export function addLineWidget(doc, handle, node, options) { 60 let widget = new LineWidget(doc, node, options) 61 let cm = doc.cm 62 if (cm && widget.noHScroll) cm.display.alignWidgets = true 63 changeLine(doc, handle, "widget", line => { 64 let widgets = line.widgets || (line.widgets = []) 65 if (widget.insertAt == null) widgets.push(widget) 66 else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget) 67 widget.line = line 68 if (cm && !lineIsHidden(doc, line)) { 69 let aboveVisible = heightAtLine(line) < doc.scrollTop 70 updateLineHeight(line, line.height + widgetHeight(widget)) 71 if (aboveVisible) addToScrollTop(cm, widget.height) 72 cm.curOp.forceUpdate = true 73 } 74 return true 75 }) 76 signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)) 77 return widget 78 }