summaryrefslogtreecommitdiff
path: root/web/src/components
diff options
context:
space:
mode:
authorJosh Kingsley <josh@joshkingsley.me>2025-10-29 18:26:41 +0200
committerJosh Kingsley <josh@joshkingsley.me>2025-10-29 18:26:41 +0200
commit7ef8366bfc43775bf26e71e77bddf31af829dfde (patch)
tree38f2551d3676838df5e35c97e5678f89fd75a56f /web/src/components
parent986e65f9ab7122995ae1d647df23d817cecf6816 (diff)
refactor(web): add decorators
Diffstat (limited to 'web/src/components')
-rw-r--r--web/src/components/app/index.ts9
-rw-r--r--web/src/components/grid/index.css4
-rw-r--r--web/src/components/grid/index.ts99
-rw-r--r--web/src/components/toolbar/index.ts10
4 files changed, 60 insertions, 62 deletions
diff --git a/web/src/components/app/index.ts b/web/src/components/app/index.ts
index 195011f..c30249f 100644
--- a/web/src/components/app/index.ts
+++ b/web/src/components/app/index.ts
@@ -1,4 +1,5 @@
import defaultDoc from "../../defaultDoc";
+import NotiveElement, { customElement } from "../../element";
import { Selection } from "../../selection";
import { Doc } from "../../types";
import ntvGrid, { NotiveGridElement } from "../grid";
@@ -6,7 +7,8 @@ import renderGrid from "../grid/renderGrid";
import ntvToolbar from "../toolbar";
import "./index.css";
-export class NotiveAppElement extends HTMLElement {
+@customElement("ntv-app")
+export class NotiveAppElement extends NotiveElement {
doc: Doc = defaultDoc();
#selection?: Selection;
@@ -37,10 +39,11 @@ export class NotiveAppElement extends HTMLElement {
ongridselectionchange: (event) => {
this.selection = event.selection;
},
+ oncellchange: (event) => {
+ console.log(event);
+ },
}),
),
);
}
}
-
-customElements.define("ntv-app", NotiveAppElement);
diff --git a/web/src/components/grid/index.css b/web/src/components/grid/index.css
index 64153ed..c29f55d 100644
--- a/web/src/components/grid/index.css
+++ b/web/src/components/grid/index.css
@@ -33,7 +33,7 @@
display: none;
}
- ntv-grid input[data-edit-cell] {
+ ntv-grid input[data-edit] {
position: absolute;
vertical-align: baseline;
background: var(--color-neutral-800);
@@ -43,7 +43,7 @@
text-align: center;
}
- ntv-grid input[data-edit-cell]:focus-visible {
+ ntv-grid input[data-edit]:focus-visible {
outline: none;
}
}
diff --git a/web/src/components/grid/index.ts b/web/src/components/grid/index.ts
index 2c00eb8..6c7f735 100644
--- a/web/src/components/grid/index.ts
+++ b/web/src/components/grid/index.ts
@@ -1,4 +1,5 @@
-import h, { type CreateElement } from "../../html";
+import NotiveElement, { customElement, eventHandler } from "../../element";
+import h from "../../html";
import { ActiveCellSelection, Selection } from "../../selection";
import { CellRef } from "../../types";
import cellAtCoord from "./cellAtCoord";
@@ -7,7 +8,8 @@ import drawSelection, { SelectionStyles } from "./drawSelection";
import "./index.css";
import { getRenderedCell, RenderedGrid } from "./renderGrid";
-export class NotiveGridElement extends HTMLElement {
+@customElement("ntv-grid")
+export class NotiveGridElement extends NotiveElement {
#internals: ElementInternals = this.attachInternals();
grid?: RenderedGrid;
@@ -23,28 +25,11 @@ export class NotiveGridElement extends HTMLElement {
this.drawSelection();
}
- #ongridselectionchange?: ((event: GridSelectionEvent) => any) | undefined;
+ @eventHandler("ntv:grid:selectionchange")
+ ongridselectionchange?: (event: GridSelectionEvent) => any;
- get ongridselectionchange() {
- return this.#ongridselectionchange;
- }
-
- set ongridselectionchange(
- handler: ((event: GridSelectionEvent) => any) | undefined,
- ) {
- if (this.#ongridselectionchange) {
- this.removeEventListener(
- "ntv:grid:selectionchange",
- this.#ongridselectionchange,
- );
- }
-
- this.#ongridselectionchange = handler;
-
- if (handler) {
- this.addEventListener("ntv:grid:selectionchange", handler);
- }
- }
+ @eventHandler("ntv:grid:cellchange")
+ oncellchange?: (event: GridCellChangeEvent) => any;
canvas: HTMLCanvasElement = h.canvas({
onmousedown: (event) => {
@@ -53,6 +38,12 @@ export class NotiveGridElement extends HTMLElement {
if (!cellRef) return;
this.startSelecting(cellRef);
},
+ ondblclick: (event) => {
+ if (!this.grid) return;
+ const cellRef = this.#mouseEventCellRef(event);
+ if (!cellRef) return;
+ this.startEditing(cellRef);
+ },
});
selectionCanvas: HTMLCanvasElement = h.canvas({
@@ -188,11 +179,9 @@ export class NotiveGridElement extends HTMLElement {
#editingCellRef?: CellRef;
- #editInputEl: HTMLInputElement = h.input({
- dataset: { editCell: "true" },
- onblur: () => {
- this.#finishEditing();
- },
+ #editInput: HTMLInputElement = h.input({
+ dataset: { edit: "true" },
+ onblur: () => this.#finishEditing(),
onkeydown: (event) => {
switch (event.key) {
case "Enter":
@@ -206,50 +195,45 @@ export class NotiveGridElement extends HTMLElement {
},
});
- #canvasDoubleClickCallback(this: NotiveGridElement, event: MouseEvent) {
+ startEditing(cellRef: CellRef) {
if (!this.grid) return;
- const cellRef = this.#mouseEventCellRef(event);
-
- if (!cellRef) return;
-
const cell = getRenderedCell(this.grid, cellRef);
if (!cell) return;
this.#editingCellRef = cellRef;
- this.append(this.#editInputEl);
+ this.append(this.#editInput);
- this.#editInputEl.value = cell.value || "";
- this.#editInputEl.style.left = cell.rect.topLeft.x + 2 + "px";
- this.#editInputEl.style.top = cell.rect.topLeft.y + 2 + "px";
- this.#editInputEl.style.width = cell.rect.width - 3 + "px";
- this.#editInputEl.style.height = cell.rect.height - 3 + "px";
- this.#editInputEl.focus();
+ this.#editInput.value = cell.value || "";
+
+ Object.assign(this.#editInput.style, {
+ left: cell.rect.topLeft.x + 2 + "px",
+ top: cell.rect.topLeft.y + 2 + "px",
+ width: cell.rect.width - 3 + "px",
+ height: cell.rect.height - 3 + "px",
+ });
+
+ this.#editInput.focus();
}
#cancelEditing() {
- this.#editInputEl.remove();
+ this.#editInput.remove();
}
#finishEditing() {
- this.#editInputEl.remove();
+ this.#editInput.remove();
- if (!this.grid) return;
+ if (!this.grid || !this.#editingCellRef) return;
- window.notive.setCellValue(
- this.grid.id,
- this.#editingCellRef!,
- this.#editInputEl.value,
+ this.dispatchEvent(
+ new GridCellChangeEvent(this.#editingCellRef, this.#editInput.value),
);
}
}
-customElements.define("ntv-grid", NotiveGridElement);
-
-export default ((...args: any[]): NotiveGridElement =>
- (h as any)["ntv-grid"](...args)) as CreateElement<NotiveGridElement>;
+export default NotiveGridElement.makeFactory();
export class GridSelectionEvent extends Event {
selection: Selection;
@@ -260,8 +244,21 @@ export class GridSelectionEvent extends Event {
}
}
+export class GridCellChangeEvent extends Event {
+ cellRef: CellRef;
+ value?: string;
+
+ constructor(cellRef: CellRef, value: string | undefined) {
+ super("ntv:grid:cellchange");
+
+ this.cellRef = cellRef;
+ this.value = value;
+ }
+}
+
declare global {
interface HTMLElementEventMap {
"ntv:grid:selectionchange": GridSelectionEvent;
+ "ntv:grid:cellchange": GridCellChangeEvent;
}
}
diff --git a/web/src/components/toolbar/index.ts b/web/src/components/toolbar/index.ts
index 1400174..84f6a65 100644
--- a/web/src/components/toolbar/index.ts
+++ b/web/src/components/toolbar/index.ts
@@ -1,6 +1,6 @@
+import NotiveElement, { customElement } from "../../element";
import h, { CreateElement } from "../../html";
import { ActiveCellSelection, RangeSelection } from "../../selection";
-import { RenderedGrid } from "../grid/renderGrid";
import "./index.css";
function getSelectedSubdivisionsCount(): number | undefined {
@@ -23,7 +23,8 @@ function getSelectedSubdivisionsCount(): number | undefined {
return Math.min(...selectedCells.map((cells) => cells.length));
}
-class NotiveToolbarElement extends HTMLElement {
+@customElement("ntv-toolbar")
+class NotiveToolbarElement extends NotiveElement {
#subdivisionsInputEl: HTMLInputElement = h.input({
title: "Subdivisions",
placeholder: "-",
@@ -127,7 +128,4 @@ class NotiveToolbarElement extends HTMLElement {
}
}
-customElements.define("ntv-toolbar", NotiveToolbarElement);
-
-export default ((...args: any[]): NotiveToolbarElement =>
- (h as any)["ntv-toolbar"](...args)) as CreateElement<NotiveToolbarElement>;
+export default NotiveToolbarElement.makeFactory();