client/src/cqlInput/popover/ErrorPopover.ts (64 lines of code) (raw):

import { EditorView } from "prosemirror-view"; import { Popover } from "./Popover"; import { CLASS_ERROR, CLASS_VISIBLE, CqlError } from "../editor/plugin"; export class ErrorPopover extends Popover { private debugContainer: HTMLElement | undefined; private contentEl: HTMLElement; private visibilityTimeout: ReturnType<typeof setTimeout> | undefined; public constructor( protected view: EditorView, protected popoverEl: HTMLElement, debugEl?: HTMLElement, private debounceTime = 500 ) { super(popoverEl); this.contentEl = document.createElement("div"); popoverEl.appendChild(this.contentEl); if (debugEl) { this.debugContainer = document.createElement("div"); debugEl.appendChild(this.debugContainer); } this.hide(); } public updateErrorMessage = async (error: CqlError | undefined) => { if (!error) { this.hideErrorMessages(); return; } this.updateDebugContainer(error); this.debouncedShowErrorMessages(error); }; private updateDebugContainer = (error: CqlError) => { if (this.debugContainer) { this.debugContainer.innerHTML = ` <h2>Error</h2> <div>Position: ${error.position ?? "No position given"}</div> <div>Message: ${error.message}</div> `; } }; public hideErrorMessages = () => { clearTimeout(this.visibilityTimeout); this.contentEl.innerHTML = ""; this.popoverEl.classList.remove(CLASS_VISIBLE); this.hide(); }; private debouncedShowErrorMessages = (error: CqlError) => { this.visibilityTimeout = setTimeout(() => { if (error.position !== undefined) { this.popoverEl.classList.add(CLASS_VISIBLE); const referenceEl = this.view.dom.getElementsByClassName(CLASS_ERROR)?.[0]; if (!referenceEl) { console.warn( `Attempt to render element popover at position ${error.position}, but no position widget found in document` ); return; } const xOffset = 0; const yOffset = -25; this.show(referenceEl, xOffset, yOffset); } }, this.debounceTime); }; }