summaryrefslogtreecommitdiff
path: root/web/src/components/grid/index.ts
diff options
context:
space:
mode:
authorJosh Kingsley <josh@joshkingsley.me>2025-10-30 13:25:02 +0200
committerJosh Kingsley <josh@joshkingsley.me>2025-10-30 13:25:02 +0200
commit715996aceb4d4dc96410464f60727b98a289be08 (patch)
tree876d97664a22246ebecfc25cad1f4b96dee031a0 /web/src/components/grid/index.ts
parent7ef8366bfc43775bf26e71e77bddf31af829dfde (diff)
refactor(web): move selection code
Diffstat (limited to 'web/src/components/grid/index.ts')
-rw-r--r--web/src/components/grid/index.ts87
1 files changed, 45 insertions, 42 deletions
diff --git a/web/src/components/grid/index.ts b/web/src/components/grid/index.ts
index 6c7f735..78bb14e 100644
--- a/web/src/components/grid/index.ts
+++ b/web/src/components/grid/index.ts
@@ -1,12 +1,12 @@
import NotiveElement, { customElement, eventHandler } from "../../element";
import h from "../../html";
-import { ActiveCellSelection, Selection } from "../../selection";
import { CellRef } from "../../types";
import cellAtCoord from "./cellAtCoord";
import drawGrid, { GridStyles } from "./drawGrid";
import drawSelection, { SelectionStyles } from "./drawSelection";
import "./index.css";
import { getRenderedCell, RenderedGrid } from "./renderGrid";
+import { extendSelection, GridSelection } from "./selection";
@customElement("ntv-grid")
export class NotiveGridElement extends NotiveElement {
@@ -14,25 +14,26 @@ export class NotiveGridElement extends NotiveElement {
grid?: RenderedGrid;
- #selection?: Selection;
+ #selection?: GridSelection;
get selection() {
return this.#selection;
}
- set selection(selection: Selection | undefined) {
+ set selection(selection: GridSelection | undefined) {
this.#selection = selection;
this.drawSelection();
}
@eventHandler("ntv:grid:selectionchange")
- ongridselectionchange?: (event: GridSelectionEvent) => any;
+ ongridselectionchange?: (event: GridSelectionChangeEvent) => any;
@eventHandler("ntv:grid:cellchange")
oncellchange?: (event: GridCellChangeEvent) => any;
canvas: HTMLCanvasElement = h.canvas({
onmousedown: (event) => {
+ if (event.button !== 0) return;
if (!this.grid) return;
const cellRef = this.#mouseEventCellRef(event);
if (!cellRef) return;
@@ -114,7 +115,7 @@ export class NotiveGridElement extends NotiveElement {
};
}
- #pendingSelection?: Selection;
+ #pendingSelection?: GridSelection;
#selectionAbortController?: AbortController;
startSelecting(cellRef: CellRef) {
@@ -123,12 +124,6 @@ export class NotiveGridElement extends NotiveElement {
this.#internals.states.add("selecting");
this.#selectionAbortController = new AbortController();
-
- this.#selectionAbortController.signal.addEventListener("abort", () => {
- this.#internals.states.delete("selecting");
- this.#selectionAbortController = undefined;
- });
-
const { signal } = this.#selectionAbortController;
window.addEventListener(
@@ -136,33 +131,43 @@ export class NotiveGridElement extends NotiveElement {
(event) => {
const cellRef = this.#mouseEventCellRef(event);
if (!cellRef) return;
- this.#pendingSelection = this.#pendingSelection?.extend(cellRef);
+ this.#pendingSelection = extendSelection(
+ this.#pendingSelection,
+ cellRef,
+ );
this.drawSelection();
},
{ signal },
);
- window.addEventListener(
- "mouseup",
- () => {
- this.#selectionAbortController?.abort();
-
- if (!this.#pendingSelection) return;
-
- this.dispatchEvent(
- new GridSelectionEvent(
- "ntv:grid:selectionchange",
- this.#pendingSelection,
- ),
- );
+ window.addEventListener("mouseup", () => this.#finishSelecting(), {
+ signal,
+ });
- this.#pendingSelection = undefined;
- this.drawSelection();
+ window.addEventListener(
+ "keydown",
+ (event) => {
+ event.preventDefault();
+ if (event.key === "Escape") {
+ this.#pendingSelection = undefined;
+ this.#finishSelecting();
+ }
},
{ signal },
);
- this.#pendingSelection = new ActiveCellSelection(this.grid.id, cellRef);
+ this.#pendingSelection = extendSelection(undefined, cellRef);
+ this.drawSelection();
+ }
+
+ #finishSelecting() {
+ this.#selectionAbortController?.abort();
+ this.#selectionAbortController = undefined;
+ this.#internals.states.delete("selecting");
+ if (this.#pendingSelection) {
+ this.dispatchEvent(new GridSelectionChangeEvent(this.#pendingSelection));
+ }
+ this.#pendingSelection = undefined;
this.drawSelection();
}
@@ -235,30 +240,28 @@ export class NotiveGridElement extends NotiveElement {
export default NotiveGridElement.makeFactory();
-export class GridSelectionEvent extends Event {
- selection: Selection;
+export class GridSelectionChangeEvent extends Event {
+ static readonly TYPE = "ntv:grid:selectionchange";
- constructor(type: string, selection: Selection) {
- super(type);
- this.selection = selection;
+ constructor(public selection: GridSelection) {
+ super(GridSelectionChangeEvent.TYPE);
}
}
export class GridCellChangeEvent extends Event {
- cellRef: CellRef;
- value?: string;
-
- constructor(cellRef: CellRef, value: string | undefined) {
- super("ntv:grid:cellchange");
+ static readonly TYPE = "ntv:grid:cellchange";
- this.cellRef = cellRef;
- this.value = value;
+ constructor(
+ public cellRef: CellRef,
+ public value: string | undefined,
+ ) {
+ super(GridCellChangeEvent.TYPE);
}
}
declare global {
interface HTMLElementEventMap {
- "ntv:grid:selectionchange": GridSelectionEvent;
- "ntv:grid:cellchange": GridCellChangeEvent;
+ [GridSelectionChangeEvent.TYPE]: GridSelectionChangeEvent;
+ [GridCellChangeEvent.TYPE]: GridCellChangeEvent;
}
}