summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--web/package.json2
-rw-r--r--web/src/components/app/index.ts4
-rw-r--r--web/src/components/grid/drawGrid.ts30
-rw-r--r--web/src/components/grid/index.css2
-rw-r--r--web/src/components/grid/index.ts26
-rw-r--r--web/src/components/grid/renderGrid.ts127
-rw-r--r--web/src/index.css1
-rw-r--r--web/src/index.ts24
-rw-r--r--web/src/renderGrid.ts64
-rw-r--r--web/src/types.ts7
10 files changed, 208 insertions, 79 deletions
diff --git a/web/package.json b/web/package.json
index a84c562..e15cd1e 100644
--- a/web/package.json
+++ b/web/package.json
@@ -2,7 +2,7 @@
"name": "@notive/web",
"private": true,
"scripts": {
- "dev": "CI=true vite --clearScreen false"
+ "dev": "vite --clearScreen false"
},
"dependencies": {
"open-color": "^1.9.1"
diff --git a/web/src/components/app/index.ts b/web/src/components/app/index.ts
index 5967a46..2782e22 100644
--- a/web/src/components/app/index.ts
+++ b/web/src/components/app/index.ts
@@ -4,8 +4,8 @@ import "./index.css";
class NotiveAppElement extends HTMLElement {
connectedCallback() {
this.append(
- ...window.notive.doc.grids.map((_grid) => {
- return ntvGrid();
+ ...window.notive.doc.grids.map((grid) => {
+ return ntvGrid({ gridId: grid.id });
}),
);
}
diff --git a/web/src/components/grid/drawGrid.ts b/web/src/components/grid/drawGrid.ts
new file mode 100644
index 0000000..6284693
--- /dev/null
+++ b/web/src/components/grid/drawGrid.ts
@@ -0,0 +1,30 @@
+import { RenderedGrid } from "./renderGrid";
+import colors from "open-color";
+
+export default function drawGrid(
+ ctx: CanvasRenderingContext2D,
+ grid: RenderedGrid,
+) {
+ ctx.clearRect(0, 0, grid.rect.width, grid.rect.height);
+
+ ctx.fillStyle = colors.gray[8];
+ ctx.fillRect(0, 0, grid.rect.width, grid.rect.height);
+
+ ctx.strokeStyle = colors.gray[7];
+
+ grid.renderedRows.forEach((row, renderedRowIndex) => {
+ const isLastRow = renderedRowIndex === grid.renderedRows.length - 1;
+
+ row.renderedCells.forEach((cell, cellIndex) => {
+ const { topLeft, width, height } = cell.rect;
+ const isLastCell = cellIndex === row.renderedCells.length - 1;
+
+ ctx.strokeRect(
+ topLeft.x + 0.5,
+ topLeft.y + 0.5,
+ isLastCell ? width - 1 : width,
+ isLastRow ? height - 1 : height,
+ );
+ });
+ });
+}
diff --git a/web/src/components/grid/index.css b/web/src/components/grid/index.css
index 296c155..0fad720 100644
--- a/web/src/components/grid/index.css
+++ b/web/src/components/grid/index.css
@@ -1,8 +1,8 @@
ntv-grid {
display: block;
+ padding: 1.5rem;
}
ntv-grid > canvas {
display: block;
- width: 100%;
}
diff --git a/web/src/components/grid/index.ts b/web/src/components/grid/index.ts
index 18bf75a..829a511 100644
--- a/web/src/components/grid/index.ts
+++ b/web/src/components/grid/index.ts
@@ -1,11 +1,27 @@
import h, { type CreateElement } from "../../html";
+import renderGrid from "./renderGrid";
+import drawGrid from "./drawGrid";
import "./index.css";
-import colors from "open-color";
class NotiveGridElement extends HTMLElement {
+ #gridId!: string;
+
+ get gridId() {
+ return this.#gridId;
+ }
+
+ set gridId(val: string) {
+ this.#gridId = val;
+ this.setAttribute("grid-id", val);
+ }
+
canvasEl: HTMLCanvasElement = h.canvas();
connectedCallback() {
+ if (!this.gridId) {
+ throw new Error("ntv-grid requries gridId attribute");
+ }
+
this.append(this.canvasEl);
this.draw();
}
@@ -13,8 +29,12 @@ class NotiveGridElement extends HTMLElement {
draw() {
const ctx = this.canvasEl.getContext("2d");
if (!ctx) throw new Error("Unable to get canvas context");
- ctx.fillStyle = colors.gray[8];
- ctx.fillRect(0, 0, this.canvasEl.width, this.canvasEl.height);
+ 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);
}
}
diff --git a/web/src/components/grid/renderGrid.ts b/web/src/components/grid/renderGrid.ts
new file mode 100644
index 0000000..5666f66
--- /dev/null
+++ b/web/src/components/grid/renderGrid.ts
@@ -0,0 +1,127 @@
+import Ratio from "../../math/Ratio";
+import Rect from "../../math/Rect";
+import { Cell, CellRef, Grid, Row, RowRef } from "./types";
+
+export interface RenderedCell extends Cell {
+ cellRef: CellRef;
+ renderedRowIndex: number;
+ rect: Rect;
+}
+
+export interface RenderedRow {
+ rowRef: RowRef;
+ rect: Rect;
+ renderedCells: RenderedCell[];
+}
+
+export interface RenderedGrid extends Grid {
+ rect: Rect;
+ renderedRows: RenderedRow[];
+}
+
+function renderCell(
+ grid: Grid,
+ cell: Cell,
+ cellRef: CellRef,
+ renderedRowIndex: number,
+ topLeftX: number,
+ topLeftY: number,
+): RenderedCell {
+ const width = cell.widthRatio
+ .divideRatio(grid.baseCellWidthRatio)
+ .multiplyRatio(Ratio.fromInteger(grid.baseCellSize))
+ .toNumber();
+
+ const rect = new Rect(topLeftX, topLeftY, width, grid.baseCellSize);
+
+ return { ...cell, cellRef, rect, renderedRowIndex };
+}
+
+function renderRow(
+ grid: Grid,
+ row: Row,
+ rowRef: RowRef,
+ renderedRowIndex: number,
+ topLeftY: number,
+): RenderedRow {
+ if (row.cells.length === 0) {
+ return {
+ ...row,
+ rowRef,
+ rect: new Rect(0, topLeftY, 0, 0),
+ renderedCells: [],
+ };
+ }
+
+ let topLeftX = 0;
+
+ const renderedCells = row.cells.map((cell, cellIndex) => {
+ const cellRef = { ...rowRef, cellIndex };
+
+ const renderedCell = renderCell(
+ grid,
+ cell,
+ cellRef,
+ renderedRowIndex,
+ topLeftX,
+ topLeftY,
+ );
+
+ topLeftX = renderedCell.rect.bottomRight.x;
+
+ return renderedCell;
+ });
+
+ const { topLeft } = renderedCells[0].rect;
+ const { bottomRight } = renderedCells.at(-1)!.rect;
+
+ const rect = new Rect(
+ topLeft.x,
+ topLeft.y,
+ bottomRight.x - topLeft.x,
+ bottomRight.y - topLeft.y,
+ );
+
+ return { ...row, renderedCells, rect, rowRef };
+}
+
+function renderRows(grid: Grid): RenderedRow[] {
+ const renderedRows: RenderedRow[] = [];
+
+ let partIndex = 0;
+ let rowIndex = 0;
+ let topLeftY = 0;
+ let renderedRowIndex = 0;
+
+ while (true) {
+ if (!grid.parts[partIndex]?.rows[rowIndex]) break;
+
+ const row = grid.parts[partIndex].rows[rowIndex];
+ const rowRef = { partIndex, rowIndex };
+ const renderedRow = renderRow(
+ grid,
+ row,
+ rowRef,
+ renderedRowIndex,
+ topLeftY,
+ );
+
+ topLeftY = renderedRow.rect.bottomRight.y;
+ renderedRows.push(renderedRow);
+
+ if (!grid.parts[++partIndex]) {
+ partIndex = 0;
+ rowIndex++;
+ }
+
+ renderedRowIndex++;
+ }
+
+ return renderedRows;
+}
+
+export default function renderGrid(grid: Grid) {
+ const renderedRows = renderRows(grid);
+ const rect = renderedRows[0].rect.extend(renderedRows.at(-1)!.rect);
+ return { ...grid, rect, renderedRows };
+}
diff --git a/web/src/index.css b/web/src/index.css
index f578562..ba2a6a7 100644
--- a/web/src/index.css
+++ b/web/src/index.css
@@ -1,6 +1,7 @@
@import "open-color";
body {
+ margin: 0;
background: var(--oc-gray-9);
color: var(--oc-white);
font-weight: normal;
diff --git a/web/src/index.ts b/web/src/index.ts
index a32aaf1..fbbf37c 100644
--- a/web/src/index.ts
+++ b/web/src/index.ts
@@ -1,13 +1,24 @@
-import { Doc } from "./types";
+import Ratio from "./math/Ratio";
+import { Cell, Doc, Grid } from "./types";
function defaultDoc(): Doc {
- const defaultCells = Array(16).map(() => ({ value: "1" }));
+ const defaultCells: Cell[] = Array.from({ length: 16 }, () => ({
+ widthRatio: new Ratio(1, 16),
+ }));
return {
grids: [
{
+ id: window.crypto.randomUUID(),
baseCellSize: 48,
- parts: [{ rows: Array(4).map(() => ({ cells: [...defaultCells] })) }],
+ baseCellWidthRatio: new Ratio(1, 16),
+ parts: [
+ {
+ rows: Array.from({ length: 4 }, () => ({
+ cells: [...defaultCells],
+ })),
+ },
+ ],
},
],
};
@@ -15,8 +26,11 @@ function defaultDoc(): Doc {
export default class Notive {
doc: Doc = defaultDoc();
+ gridsById = Object.fromEntries(this.doc.grids.map((grid) => [grid.id, grid]));
+
+ getGrid(id: string): Grid | undefined {
+ return this.gridsById[id];
+ }
}
window.notive = new Notive();
-
-window.dispatchEvent(new CustomEvent("ntv:initialized"));
diff --git a/web/src/renderGrid.ts b/web/src/renderGrid.ts
deleted file mode 100644
index 476876b..0000000
--- a/web/src/renderGrid.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-import Rect from "./math/Rect";
-import { Cell, CellRef, Grid, Row, RowRef } from "./types";
-
-export interface RenderedCell extends Cell {
- cellRef: CellRef;
- rect: Rect;
-}
-
-export interface RenderedRow {
- rowRef: RowRef;
- rect: Rect;
- renderedCells: RenderedCell[];
-}
-
-export interface RenderedGrid extends Grid {
- rect: Rect;
- renderedRows: RenderedRow[];
-}
-
-function renderCell(grid: Grid, row: Row, cell: Cell): RenderedCell {}
-
-function renderRow(grid: Grid, row: Row): RenderedRow {
- let topLeftX = 0;
-
- const renderedCells = row.cells.map((cell, cellIndex) => {
- const renderedCell = renderCell(grid, row, cell);
- topLeftX = renderedCell.rect.bottomRight.y;
- return renderedCell;
- });
-}
-
-function renderRows(grid: Grid): RenderedRow[] {
- const renderedRows: RenderedRow[] = [];
-
- let partIndex = 0;
- let rowIndex = 0;
- let topLeftY = 0;
- let renderedRowIndex = 0;
-
- while (true) {
- if (!grid.parts[partIndex]?.rows[rowIndex]) break;
-
- const row = grid.parts[partIndex].rows[rowIndex];
- const renderedRow = renderRow(grid, row);
-
- topLeftY = renderedRow.rect.bottomRight.y;
- renderedRows.push(renderedRow);
-
- if (!grid.parts[++partIndex]) {
- partIndex = 0;
- rowIndex++;
- }
-
- renderedRowIndex++;
- }
-
- return renderedRows;
-}
-
-export default function renderGrid(grid: Grid) {
- const renderedRows = renderRows(grid);
- const rect = renderedRows[0].rect.extend(renderedRows.at(-1)!.rect);
- return { ...grid, rect, renderedRows };
-}
diff --git a/web/src/types.ts b/web/src/types.ts
index df421d7..008d1ee 100644
--- a/web/src/types.ts
+++ b/web/src/types.ts
@@ -2,22 +2,23 @@ import Ratio from "./math/Ratio";
export interface Cell {
value?: string;
+ widthRatio: Ratio;
}
export interface Row {
- cells: [Cell, ...Cell[]];
+ cells: Cell[];
}
export interface Part {
title?: string;
- rows: [Row, ...Row[]];
+ rows: Row[];
}
export interface Grid {
id: string;
baseCellSize: number;
baseCellWidthRatio: Ratio;
- parts: [Part, ...Part[]];
+ parts: Part[];
}
export interface Doc {