summaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
Diffstat (limited to 'web')
-rw-r--r--web/src/doc/index.test.ts15
-rw-r--r--web/src/doc/index.ts110
2 files changed, 125 insertions, 0 deletions
diff --git a/web/src/doc/index.test.ts b/web/src/doc/index.test.ts
new file mode 100644
index 0000000..331d0a5
--- /dev/null
+++ b/web/src/doc/index.test.ts
@@ -0,0 +1,15 @@
+import { describe, expect, test } from "vitest";
+import Doc from ".";
+
+describe(Doc, () => {
+ describe(Doc.default, () => {
+ const doc = Doc.default();
+
+ test("produces valid grid data", () => {
+ expect(doc.grids.length).toBe(1);
+ expect(doc.grids[0].doc).toBe(doc);
+ expect(doc.grids[0].rows.length).toBe(4);
+ expect(doc.grids[0].rows[0].cells.length).toBe(16);
+ });
+ });
+});
diff --git a/web/src/doc/index.ts b/web/src/doc/index.ts
new file mode 100644
index 0000000..a58faa6
--- /dev/null
+++ b/web/src/doc/index.ts
@@ -0,0 +1,110 @@
+export abstract class DocOp {
+ abstract key: string;
+}
+
+export class CreateGridOp extends DocOp {
+ gridId: string = crypto.randomUUID();
+ rows = 4;
+ key = this.gridId;
+ baseCellsPerRow = 16;
+}
+
+export default class Doc {
+ readonly ops: DocOp[];
+
+ readonly opsByKey: Map<string, readonly DocOp[]>;
+ readonly opsByType: Map<Function, readonly DocOp[]>;
+
+ constructor(ops: DocOp[]) {
+ this.ops = ops;
+
+ const opsByKey = new Map();
+ const opsByType = new Map();
+
+ for (const op of ops) {
+ const maybeKeyOps = opsByKey.get(op.key);
+
+ if (maybeKeyOps) maybeKeyOps.push(op);
+ else opsByKey.set(op.key, [op]);
+
+ const maybeTypeOps = opsByType.get(op.constructor);
+
+ if (maybeTypeOps) maybeTypeOps.push(op);
+ else opsByType.set(op.constructor, [op]);
+ }
+
+ this.opsByKey = opsByKey;
+ this.opsByType = opsByType;
+ }
+
+ static default(): Doc {
+ return new Doc([new CreateGridOp()]);
+ }
+
+ get grids(): Grid[] {
+ const ops = this.opsByType.get(CreateGridOp) ?? [];
+ return ops.map((op) => new Grid(this, op as CreateGridOp));
+ }
+}
+
+export class Grid {
+ readonly doc: Doc;
+ readonly id: string;
+ readonly rows: readonly Row[];
+
+ constructor(doc: Doc, createOp: CreateGridOp) {
+ this.doc = doc;
+ this.id = createOp.gridId;
+
+ const rows: Row[] = [];
+
+ for (let rowIndex = 0; rowIndex < createOp.rows; rowIndex++) {
+ rows.push(new Row(doc, this.id, rowIndex));
+ }
+
+ this.rows = rows;
+ }
+}
+
+export class Row {
+ readonly doc: Doc;
+ readonly gridId: string;
+ readonly index: number;
+ readonly cells: readonly Cell[];
+
+ constructor(doc: Doc, gridId: string, index: number) {
+ this.doc = doc;
+ this.gridId = gridId;
+ this.index = index;
+
+ const createGridOp = doc.opsByType
+ .get(CreateGridOp)
+ ?.find((op) => op.key === gridId)! as CreateGridOp;
+
+ const cells: Cell[] = [];
+
+ for (
+ let cellIndex = 0;
+ cellIndex < createGridOp.baseCellsPerRow;
+ cellIndex++
+ ) {
+ cells.push(new Cell(doc, gridId, index, cellIndex));
+ }
+
+ this.cells = cells;
+ }
+}
+
+export class Cell {
+ readonly doc: Doc;
+ readonly gridId: string;
+ readonly rowIndex: number;
+ readonly index: number;
+
+ constructor(doc: Doc, gridId: string, rowIndex: number, index: number) {
+ this.doc = doc;
+ this.gridId = gridId;
+ this.rowIndex = rowIndex;
+ this.index = index;
+ }
+}