import h, { type CreateElement } from "../../html"; import { CellRef } from "../../types"; import cellAtCoord from "./cellAtCoord"; import drawGrid, { getGridColors } from "./drawGrid"; import "./index.css"; class NotiveGridElement extends HTMLElement { #gridId!: string; get gridId() { return this.#gridId; } set gridId(val: string) { this.#gridId = val; this.setAttribute("grid-id", val); } get renderedGrid() { return window.notive.getGrid(this.#gridId)!; } canvasEl: HTMLCanvasElement = h.canvas(); connectedCallback() { if (!this.gridId) { throw new Error("ntv-grid requries gridId attribute"); } window.addEventListener("ntv:selection-changed", () => this.draw()); this.canvasEl.addEventListener( "mousedown", this.#canvasMouseDownCallback.bind(this), ); this.append(this.canvasEl); this.draw(); } draw() { const ctx = this.canvasEl.getContext("2d"); if (!ctx) throw new Error("Unable to get canvas context"); const grid = window.notive.getGrid(this.gridId); if (!grid) return; this.canvasEl.setAttribute("width", grid.rect.width + "px"); this.canvasEl.setAttribute("height", grid.rect.height + "px"); const colors = getGridColors(this); drawGrid( ctx, colors, grid, window.notive.selection, window.notive.pendingSelection, ); } #mouseEventCellRef( this: NotiveGridElement, event: MouseEvent, ): CellRef | undefined { const clientRect = this.canvasEl.getBoundingClientRect(); const x = event.x - clientRect.x; const y = event.y - clientRect.y; return cellAtCoord(this.renderedGrid, x, y); } #canvasMouseDownCallback(this: NotiveGridElement, event: MouseEvent) { const cellRef = this.#mouseEventCellRef(event); if (!cellRef) return; window.notive.startSelecting(this.gridId, cellRef); this.#selectionAbortController = new AbortController(); const { signal } = this.#selectionAbortController; window.addEventListener( "mousemove", this.#selectionMouseMoveCallback.bind(this), { signal }, ); window.addEventListener( "mouseup", this.#selectionMouseUpCallback.bind(this), { signal }, ); } #selectionAbortController?: AbortController; #selectionMouseMoveCallback(this: NotiveGridElement, event: MouseEvent) { const cellRef = this.#mouseEventCellRef(event); if (!cellRef) return; window.notive.extendSelection(cellRef); } #selectionMouseUpCallback(this: NotiveGridElement, event: MouseEvent) { this.#selectionAbortController?.abort(); this.#selectionAbortController = undefined; window.notive.finishSelecting(); } } customElements.define("ntv-grid", NotiveGridElement); export default ((...args: any[]): NotiveGridElement => (h as any)["ntv-grid"](...args)) as CreateElement;