summaryrefslogtreecommitdiff
path: root/web/src/components/grid
diff options
context:
space:
mode:
Diffstat (limited to 'web/src/components/grid')
-rw-r--r--web/src/components/grid/cellAtCoord.ts40
-rw-r--r--web/src/components/grid/drawGrid.ts95
-rw-r--r--web/src/components/grid/index.css1
-rw-r--r--web/src/components/grid/index.ts36
-rw-r--r--web/src/components/grid/renderGrid.ts2
5 files changed, 158 insertions, 16 deletions
diff --git a/web/src/components/grid/cellAtCoord.ts b/web/src/components/grid/cellAtCoord.ts
new file mode 100644
index 0000000..dd594a4
--- /dev/null
+++ b/web/src/components/grid/cellAtCoord.ts
@@ -0,0 +1,40 @@
+import Coord from "../../math/Coord";
+import { CellRef } from "../../types";
+import { RenderedGrid, RenderedRow } from "./renderGrid";
+
+function rowAtCoord(grid: RenderedGrid, coord: Coord): RenderedRow | undefined {
+ if (coord.y <= grid.rect.topLeft.y) {
+ return grid.renderedRows[0];
+ }
+
+ if (coord.y >= grid.rect.bottomRight.y) {
+ return grid.renderedRows.at(-1);
+ }
+
+ return grid.renderedRows.find((row) =>
+ row.rect.verticallyContainsCoord(coord),
+ );
+}
+
+export default function cellAtCoord(
+ grid: RenderedGrid,
+ x: number,
+ y: number,
+): CellRef | undefined {
+ const coord = new Coord(x, y);
+ const row = rowAtCoord(grid, coord);
+
+ if (!row) return;
+
+ if (x <= row.rect.topLeft.x) {
+ return row.renderedCells[0]?.cellRef;
+ }
+
+ if (x >= row.rect.bottomRight.x) {
+ return row.renderedCells.at(-1)?.cellRef;
+ }
+
+ return row.renderedCells.find((cell) =>
+ cell.rect.horizontallyContainsCoord(coord),
+ )?.cellRef;
+}
diff --git a/web/src/components/grid/drawGrid.ts b/web/src/components/grid/drawGrid.ts
index 6284693..01240b5 100644
--- a/web/src/components/grid/drawGrid.ts
+++ b/web/src/components/grid/drawGrid.ts
@@ -1,16 +1,21 @@
-import { RenderedGrid } from "./renderGrid";
-import colors from "open-color";
+import colors from "tailwindcss/colors";
+import { PendingSelection, Selection } from "../../selection";
+import { CellRef } from "../../types";
+import { RenderedCell, RenderedGrid } from "./renderGrid";
-export default function drawGrid(
- ctx: CanvasRenderingContext2D,
- grid: RenderedGrid,
-) {
+function fillBackground(ctx: CanvasRenderingContext2D, grid: RenderedGrid) {
ctx.clearRect(0, 0, grid.rect.width, grid.rect.height);
-
- ctx.fillStyle = colors.gray[8];
+ ctx.fillStyle = colors.neutral[800];
ctx.fillRect(0, 0, grid.rect.width, grid.rect.height);
+}
+
+function strokeGrid(ctx: CanvasRenderingContext2D, grid: RenderedGrid) {
+ ctx.strokeStyle = colors.neutral[700];
+ ctx.strokeRect(0.5, 0.5, grid.rect.width - 1, grid.rect.height - 1);
+}
- ctx.strokeStyle = colors.gray[7];
+function strokeGridLines(ctx: CanvasRenderingContext2D, grid: RenderedGrid) {
+ ctx.strokeStyle = colors.neutral[700];
grid.renderedRows.forEach((row, renderedRowIndex) => {
const isLastRow = renderedRowIndex === grid.renderedRows.length - 1;
@@ -28,3 +33,75 @@ export default function drawGrid(
});
});
}
+
+function getRenderedCell(
+ grid: RenderedGrid,
+ cellRef: CellRef,
+): RenderedCell | undefined {
+ const rowsPerPart = grid.renderedRows.length / grid.parts.length;
+ const renderedRowIndex = cellRef.partIndex * rowsPerPart + cellRef.rowIndex;
+ return grid.renderedRows[renderedRowIndex]?.renderedCells[cellRef.cellIndex];
+}
+
+function drawPendingSelection(
+ ctx: CanvasRenderingContext2D,
+ grid: RenderedGrid,
+ selection: PendingSelection,
+) {}
+
+function drawSelection(
+ ctx: CanvasRenderingContext2D,
+ grid: RenderedGrid,
+ selection: Selection,
+) {
+ if (selection.gridId !== grid.id) return;
+
+ const cell = getRenderedCell(grid, selection.activeCellRef);
+
+ if (!cell) return;
+
+ const isLastCell = cell.rect.bottomRight.x === grid.rect.bottomRight.x;
+ const isLastRow = cell.rect.bottomRight.y === grid.rect.bottomRight.y;
+
+ // ctx.fillStyle = colors.green[4] + "30";
+
+ // ctx.fillRect(
+ // cell.rect.topLeft.x + 1,
+ // cell.rect.topLeft.y + 1,
+ // cell.rect.width - 1,
+ // cell.rect.height - 1,
+ // );
+
+ ctx.strokeStyle = colors.green[600];
+ ctx.lineWidth = 2;
+
+ ctx.strokeRect(
+ cell.rect.topLeft.x + 1,
+ cell.rect.topLeft.y + 1,
+ isLastCell ? cell.rect.width - 2 : cell.rect.width - 1,
+ isLastRow ? cell.rect.height - 2 : cell.rect.height - 1,
+ );
+}
+
+export default function drawGrid(
+ ctx: CanvasRenderingContext2D,
+ grid: RenderedGrid,
+ selection?: Selection,
+ pendingSelection?: PendingSelection,
+) {
+ const excursion = (f: () => void) => {
+ ctx.save();
+ f();
+ ctx.restore();
+ };
+
+ excursion(() => fillBackground(ctx, grid));
+ excursion(() => strokeGridLines(ctx, grid));
+ excursion(() => strokeGrid(ctx, grid));
+
+ if (pendingSelection) {
+ excursion(() => drawPendingSelection(ctx, grid, pendingSelection));
+ } else if (selection) {
+ excursion(() => drawSelection(ctx, grid, selection));
+ }
+}
diff --git a/web/src/components/grid/index.css b/web/src/components/grid/index.css
index 0fad720..a733015 100644
--- a/web/src/components/grid/index.css
+++ b/web/src/components/grid/index.css
@@ -1,6 +1,5 @@
ntv-grid {
display: block;
- padding: 1.5rem;
}
ntv-grid > canvas {
diff --git a/web/src/components/grid/index.ts b/web/src/components/grid/index.ts
index 829a511..0acace4 100644
--- a/web/src/components/grid/index.ts
+++ b/web/src/components/grid/index.ts
@@ -1,5 +1,5 @@
import h, { type CreateElement } from "../../html";
-import renderGrid from "./renderGrid";
+import cellAtCoord from "./cellAtCoord";
import drawGrid from "./drawGrid";
import "./index.css";
@@ -15,6 +15,10 @@ class NotiveGridElement extends HTMLElement {
this.setAttribute("grid-id", val);
}
+ get renderedGrid() {
+ return window.notive.getGrid(this.#gridId)!;
+ }
+
canvasEl: HTMLCanvasElement = h.canvas();
connectedCallback() {
@@ -22,19 +26,41 @@ class NotiveGridElement extends HTMLElement {
throw new Error("ntv-grid requries gridId attribute");
}
+ this.canvasEl.addEventListener("mousedown", (event) => {
+ const clientRect = this.canvasEl.getBoundingClientRect();
+ const x = event.x - clientRect.x;
+ const y = event.y - clientRect.y;
+ const cellRef = cellAtCoord(this.renderedGrid, x, y);
+ if (!cellRef) return;
+ window.notive.selectCell(this.#gridId, cellRef);
+ });
+
+ window.addEventListener("ntv:selection-changed", () => {
+ this.draw();
+ });
+
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;
- const renderedGrid = renderGrid(grid);
- this.canvasEl.setAttribute("width", renderedGrid.rect.width + "px");
- this.canvasEl.setAttribute("height", renderedGrid.rect.height + "px");
- drawGrid(ctx, renderedGrid);
+
+ this.canvasEl.setAttribute("width", grid.rect.width + "px");
+ this.canvasEl.setAttribute("height", grid.rect.height + "px");
+
+ drawGrid(
+ ctx,
+ grid,
+ window.notive.selection,
+ window.notive.pendingSelection,
+ );
}
}
diff --git a/web/src/components/grid/renderGrid.ts b/web/src/components/grid/renderGrid.ts
index 5666f66..7ef8813 100644
--- a/web/src/components/grid/renderGrid.ts
+++ b/web/src/components/grid/renderGrid.ts
@@ -1,6 +1,6 @@
import Ratio from "../../math/Ratio";
import Rect from "../../math/Rect";
-import { Cell, CellRef, Grid, Row, RowRef } from "./types";
+import { Cell, CellRef, Grid, Row, RowRef } from "../../types";
export interface RenderedCell extends Cell {
cellRef: CellRef;