diff options
Diffstat (limited to 'web/src/components/grid')
| -rw-r--r-- | web/src/components/grid/cellAtCoord.ts | 40 | ||||
| -rw-r--r-- | web/src/components/grid/drawGrid.ts | 95 | ||||
| -rw-r--r-- | web/src/components/grid/index.css | 1 | ||||
| -rw-r--r-- | web/src/components/grid/index.ts | 36 | ||||
| -rw-r--r-- | web/src/components/grid/renderGrid.ts | 2 |
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; |
