From d724cc0bf6ff6d351319e6fb00f5184a04e16ac0 Mon Sep 17 00:00:00 2001 From: Josh Kingsley Date: Mon, 24 Nov 2025 15:46:22 +0200 Subject: chore: improve dev tasks --- apps/web/src/components/grid/drawSelection.ts | 97 +++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 apps/web/src/components/grid/drawSelection.ts (limited to 'apps/web/src/components/grid/drawSelection.ts') diff --git a/apps/web/src/components/grid/drawSelection.ts b/apps/web/src/components/grid/drawSelection.ts new file mode 100644 index 0000000..1b8c2ed --- /dev/null +++ b/apps/web/src/components/grid/drawSelection.ts @@ -0,0 +1,97 @@ +import { CellRef } from "../../types"; +import excursion from "./excursion"; +import { getRenderedCell, RenderedCell, RenderedGrid } from "./renderGrid"; +import { GridSelection } from "./selection"; + +export interface SelectionStyles { + activeCellStroke: string; + selectionRangeFill: string; + selectionRangeStroke: string; +} + +function strokeActiveCell( + ctx: CanvasRenderingContext2D, + styles: SelectionStyles, + grid: RenderedGrid, + cell: RenderedCell, +) { + excursion(ctx, () => { + const isLastCell = cell.rect.bottomRight.x === grid.rect.bottomRight.x; + const isLastRow = cell.rect.bottomRight.y === grid.rect.bottomRight.y; + + ctx.strokeStyle = styles.activeCellStroke; + 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, + ); + }); +} + +function drawCellRange( + ctx: CanvasRenderingContext2D, + styles: SelectionStyles, + grid: RenderedGrid, + start: CellRef, + end: CellRef, + { stroke }: { stroke: boolean }, +) { + excursion(ctx, () => { + const startCell = getRenderedCell(grid, start); + const endCell = getRenderedCell(grid, end); + + if (!startCell || !endCell) return; + + const rect = startCell.rect.extend(endCell.rect); + + const isRightEdge = rect.bottomRight.x === grid.rect.bottomRight.x; + const isBottomEdge = rect.bottomRight.y === grid.rect.bottomRight.y; + + ctx.fillStyle = styles.selectionRangeFill; + + ctx.fillRect( + rect.topLeft.x + 1, + rect.topLeft.y + 1, + isRightEdge ? rect.width - 2 : rect.width - 1, + isBottomEdge ? rect.height - 2 : rect.height - 1, + ); + + if (!stroke) return; + + ctx.strokeStyle = styles.selectionRangeStroke; + + ctx.strokeRect( + rect.topLeft.x + 0.5, + rect.topLeft.y + 0.5, + isRightEdge ? rect.width - 1 : rect.width, + isBottomEdge ? rect.height - 1 : rect.height, + ); + }); +} + +export default function drawSelection( + ctx: CanvasRenderingContext2D, + styles: SelectionStyles, + grid: RenderedGrid, + selection: GridSelection | undefined, + { pending }: { pending: boolean }, +) { + ctx.clearRect(0, 0, grid.rect.width, grid.rect.height); + + if (!selection) return; + + const activeCell = getRenderedCell(grid, selection.activeCellRef); + + if (!activeCell) return; + + if (selection.range) { + drawCellRange(ctx, styles, grid, selection.range[0], selection.range[1], { + stroke: !pending, + }); + } + + strokeActiveCell(ctx, styles, grid, activeCell); +} -- cgit v1.2.3