summaryrefslogtreecommitdiff
path: root/packages/web/src/components/grid/drawSelection.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/web/src/components/grid/drawSelection.ts')
-rw-r--r--packages/web/src/components/grid/drawSelection.ts97
1 files changed, 97 insertions, 0 deletions
diff --git a/packages/web/src/components/grid/drawSelection.ts b/packages/web/src/components/grid/drawSelection.ts
new file mode 100644
index 0000000..1b8c2ed
--- /dev/null
+++ b/packages/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);
+}