task(G1): layout data model - LayoutDefinition type, Yjs layouts map, undo-tracked CRUD in CADDocument
This commit is contained in:
@@ -6,6 +6,7 @@ import * as Y from 'yjs';
|
||||
import type { CADElement, CADLayer, BlockDefinition } from '../types/cad.types';
|
||||
import type { ElementGroup } from '../tools/modification/GroupTool';
|
||||
import type { BackgroundConfig } from '../services/backgroundService';
|
||||
import type { LayoutDefinition } from '../types/cad.types';
|
||||
|
||||
export interface YjsDocumentData {
|
||||
elements: Y.Map<CADElement>;
|
||||
@@ -14,6 +15,7 @@ export interface YjsDocumentData {
|
||||
meta: Y.Map<unknown>;
|
||||
groups: Y.Map<ElementGroup>;
|
||||
bgConfig: Y.Map<BackgroundConfig>;
|
||||
layouts: Y.Map<LayoutDefinition>; // Task G1
|
||||
}
|
||||
|
||||
export class YjsDocument {
|
||||
@@ -29,6 +31,7 @@ export class YjsDocument {
|
||||
meta: this.doc.getMap<unknown>('meta'),
|
||||
groups: this.doc.getMap<ElementGroup>('groups'),
|
||||
bgConfig: this.doc.getMap<BackgroundConfig>('bgConfig'),
|
||||
layouts: this.doc.getMap<LayoutDefinition>('layouts'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -57,6 +60,11 @@ export class YjsDocument {
|
||||
return Array.from(this.data.bgConfig.values());
|
||||
}
|
||||
|
||||
/** Get all layouts as a plain array (Task G1) */
|
||||
getLayouts(): LayoutDefinition[] {
|
||||
return Array.from(this.data.layouts.values());
|
||||
}
|
||||
|
||||
/** Add or update a single element */
|
||||
setElement(el: CADElement): void {
|
||||
this.doc.transact(() => {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
*/
|
||||
import * as Y from 'yjs';
|
||||
import { YjsDocument } from '../../crdt/YjsDocument';
|
||||
import type { CADElement, CADLayer } from '../../types/cad.types';
|
||||
import type { CADElement, CADLayer, LayoutDefinition } from '../../types/cad.types';
|
||||
|
||||
export class CADDocument {
|
||||
private readonly ydoc: YjsDocument;
|
||||
@@ -22,10 +22,10 @@ export class CADDocument {
|
||||
constructor(ydoc: YjsDocument) {
|
||||
this.ydoc = ydoc;
|
||||
// Trackt alle Operationen auf Elements + Layers → gemeinsame Undo-Historie.
|
||||
// Blocks/Groups/Configs bewusst NICHT tracken: Werkzeuge ändern primär Elemente/Layer.
|
||||
// Blocks/Groups/Configs bewusst NICHT tracken; Layouts (G1) dazu seit Task G1.
|
||||
// captureTimeout 0: KEIN automatisches Mergen zeitnaher Transaktionen —
|
||||
// jede explizite transact() bleibt EIGENER Undo-Schritt (deterministisch für Tools).
|
||||
this.undoManager = new Y.UndoManager([this.ydoc.data.elements, this.ydoc.data.layers], {
|
||||
this.undoManager = new Y.UndoManager([this.ydoc.data.elements, this.ydoc.data.layers, this.ydoc.data.layouts], {
|
||||
captureTimeout: 0,
|
||||
});
|
||||
}
|
||||
@@ -52,6 +52,53 @@ export class CADDocument {
|
||||
return Array.from(this.ydoc.data.layers.values());
|
||||
}
|
||||
|
||||
// ── Layouts (Task G1) ────────────────────────────────────
|
||||
|
||||
/** Ein Layout per ID (oder undefined). */
|
||||
getLayout(id: string): LayoutDefinition | undefined {
|
||||
return this.ydoc.data.layouts.get(id);
|
||||
}
|
||||
|
||||
/** Alle Layouts als Array. */
|
||||
getAllLayouts(): LayoutDefinition[] {
|
||||
return Array.from(this.ydoc.data.layouts.values());
|
||||
}
|
||||
|
||||
/** Fügt ein Layout hinzu (eigener Undo-Schritt). */
|
||||
addLayout(layout: LayoutDefinition): void {
|
||||
this.transact(() => {
|
||||
this.ydoc.data.layouts.set(layout.id, layout);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiert ein Layout: Top-Level-Felder ersetzt, viewport SHALLOW gemergt
|
||||
* (center/scale einzeln änderbar). @throws wenn Layout fehlt.
|
||||
*/
|
||||
updateLayout(id: string, patch: Partial<Omit<LayoutDefinition, 'id'>>): void {
|
||||
const existing = this.ydoc.data.layouts.get(id);
|
||||
if (!existing) throw new Error(`updateLayout: Layout '${id}' nicht gefunden`);
|
||||
const merged: LayoutDefinition = {
|
||||
...existing,
|
||||
...patch,
|
||||
viewport: patch.viewport
|
||||
? { ...existing.viewport, ...patch.viewport, center: { ...existing.viewport.center, ...patch.viewport.center } }
|
||||
: existing.viewport,
|
||||
};
|
||||
this.transact(() => {
|
||||
this.ydoc.data.layouts.set(id, merged);
|
||||
});
|
||||
}
|
||||
|
||||
/** Entfernt ein Layout (still bei unbekannter ID, eigener Undo-Schritt). */
|
||||
deleteLayout(id: string): void {
|
||||
this.transact(() => {
|
||||
if (this.ydoc.data.layouts.has(id)) {
|
||||
this.ydoc.data.layouts.delete(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ── Schreiben ────────────────────────────────────────────
|
||||
|
||||
/** Fügt ein Element hinzu (einzelner Aufruf = eigener Undo-Schritt). */
|
||||
|
||||
@@ -72,6 +72,23 @@ export interface BlockDefinition {
|
||||
thumbnail?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Layout (Task G1): Papier-/Plot-Seite mit eigenem Viewport aufs Modell.
|
||||
* Persistiert in der Yjs-Map 'layouts' (key = layoutId).
|
||||
*/
|
||||
export interface LayoutViewport {
|
||||
center: { x: number; y: number };
|
||||
scale: number;
|
||||
}
|
||||
|
||||
export interface LayoutDefinition {
|
||||
id: string;
|
||||
name: string;
|
||||
viewport: LayoutViewport;
|
||||
/** Referenz auf den Titelblock-Block (frame block), optional. */
|
||||
frameBlockRef?: string;
|
||||
}
|
||||
|
||||
export interface BoundingBox {
|
||||
minX: number;
|
||||
minY: number;
|
||||
|
||||
Reference in New Issue
Block a user