Skip to content

Commit

Permalink
Use resize observers to automatically reposition resized tooltips
Browse files Browse the repository at this point in the history
FIX: Automatically reposition tooltips when their size changes.

See https://discuss.codemirror.net/t/trigger-recalculation-of-autocompletion-tooltip-position/7748
  • Loading branch information
marijnh committed Jan 24, 2024
1 parent 76ffc1d commit dbd5ca4
Showing 1 changed file with 12 additions and 4 deletions.
16 changes: 12 additions & 4 deletions src/tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ class TooltipViewManager {
constructor(
view: EditorView,
private readonly facet: Facet<Tooltip | null>,
private readonly createTooltipView: (tooltip: Tooltip) => TooltipView
private readonly createTooltipView: (tooltip: Tooltip) => TooltipView,
private readonly removeTooltipView: (tooltipView: TooltipView) => void
) {
this.input = view.state.facet(facet)
this.tooltips = this.input.filter(t => t) as Tooltip[]
Expand Down Expand Up @@ -62,7 +63,7 @@ class TooltipViewManager {
}
}
for (let t of this.tooltipViews) if (tooltipViews.indexOf(t) < 0) {
t.dom.remove()
this.removeTooltipView(t)
t.destroy?.()
}
if (above) {
Expand Down Expand Up @@ -141,6 +142,7 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
container!: HTMLElement
classes: string
intersectionObserver: IntersectionObserver | null
resizeObserver: ResizeObserver | null
lastTransaction = 0
measureTimeout = -1

Expand All @@ -151,7 +153,11 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
this.classes = view.themeClasses
this.createContainer()
this.measureReq = {read: this.readMeasure.bind(this), write: this.writeMeasure.bind(this), key: this}
this.manager = new TooltipViewManager(view, showTooltip, t => this.createTooltip(t))
this.resizeObserver = typeof ResizeObserver == "function" ? new ResizeObserver(() => this.measureSoon()) : null
this.manager = new TooltipViewManager(view, showTooltip, t => this.createTooltip(t), t => {
if (this.resizeObserver) this.resizeObserver.unobserve(t.dom)
t.dom.remove()
})
this.above = this.manager.tooltips.map(t => !!t.above)
this.intersectionObserver = typeof IntersectionObserver == "function" ? new IntersectionObserver(entries => {
if (Date.now() > this.lastTransaction - 50 &&
Expand Down Expand Up @@ -225,6 +231,7 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
tooltipView.dom.style.left = "0px"
this.container.appendChild(tooltipView.dom)
if (tooltipView.mount) tooltipView.mount(this.view)
if (this.resizeObserver) this.resizeObserver.observe(tooltipView.dom)
return tooltipView
}

Expand All @@ -235,6 +242,7 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
tooltipView.destroy?.()
}
if (this.parent) this.container.remove()
this.resizeObserver?.disconnect()
this.intersectionObserver?.disconnect()
clearTimeout(this.measureTimeout)
}
Expand Down Expand Up @@ -509,7 +517,7 @@ class HoverTooltipHost implements TooltipView {
private constructor(readonly view: EditorView) {
this.dom = document.createElement("div")
this.dom.classList.add("cm-tooltip-hover")
this.manager = new TooltipViewManager(view, showHoverTooltip, t => this.createHostedView(t))
this.manager = new TooltipViewManager(view, showHoverTooltip, t => this.createHostedView(t), t => t.dom.remove())
}

createHostedView(tooltip: Tooltip) {
Expand Down

0 comments on commit dbd5ca4

Please sign in to comment.