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; readonly opsByType: Map; 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; } }