101 lines
3.8 KiB
TypeScript
101 lines
3.8 KiB
TypeScript
/**
|
|
* CAD-14 Phase 3: selectTool V2 — Auswahl direkt auf dem V2-Dokument.
|
|
* Klick = hitTest via SelectionBridge, Leerklick leert, Shift additiv,
|
|
* Drag = Box-Select (Elemente mit Zentrum in BBox).
|
|
*/
|
|
import { describe, it, expect, beforeEach } from 'vitest';
|
|
import { selectTool } from '../src/plugins/builtin/core-drawing';
|
|
import { CADDocument } from '../src/kernel/document/CADDocument';
|
|
import { YjsDocument } from '../src/crdt/YjsDocument';
|
|
import type { CADElement, Pt } from '../src/types/cad.types';
|
|
|
|
function pe(x: number, y: number, shift = false): { world: Pt; shift: boolean } {
|
|
return { world: { x, y }, shift } as never;
|
|
}
|
|
|
|
function makeCtx(ids: string[], hit: CADElement | null) {
|
|
const selected: string[][] = [];
|
|
const onChangeCalls: string[][] = [];
|
|
const realDoc = new CADDocument(new YjsDocument());
|
|
let currentIds = [...ids];
|
|
const ctx: any = {
|
|
options: {},
|
|
selection: {
|
|
getIds: () => currentIds,
|
|
setIds: (next: string[], additive?: boolean) => {
|
|
currentIds = additive ? [...currentIds, ...next] : next;
|
|
selected.push(currentIds);
|
|
},
|
|
hitTest: (_x: number, _y: number, _tol: number) => hit,
|
|
onChange: (ids2: string[]) => onChangeCalls.push(ids2),
|
|
},
|
|
doc: realDoc,
|
|
setStatus: () => {},
|
|
setPreview: () => {},
|
|
};
|
|
return { ctx, selected, onChangeCalls, realDoc, getCurrentIds: () => currentIds };
|
|
}
|
|
|
|
function lineEl(id: string, x1: number, y1: number, x2: number, y2: number): CADElement {
|
|
return {
|
|
id, type: 'line', layerId: 'l', x: (x1 + x2) / 2, y: (y1 + y2) / 2,
|
|
width: Math.abs(x2 - x1), height: Math.abs(y2 - y1),
|
|
properties: { x1, y1, x2, y2 },
|
|
} as unknown as CADElement;
|
|
}
|
|
|
|
describe('CAD-14 Phase 3: selectTool V2', () => {
|
|
beforeEach(() => {
|
|
selectTool.handlers.cancel?.({ setStatus: () => {}, setPreview: () => {} } as never);
|
|
});
|
|
|
|
it('Manifest: id=select, basic-tag (immer sichtbar)', () => {
|
|
expect(selectTool.manifest.id).toBe('select');
|
|
expect((selectTool.manifest.tags ?? []).includes('basic')).toBe(true);
|
|
});
|
|
|
|
it('Klick auf Element: hitTest-Treffer wird selektiert (setIds mit ID)', () => {
|
|
const el = lineEl('e1', 0, 0, 100, 100);
|
|
const { ctx, selected } = makeCtx([], el);
|
|
selectTool.handlers.down!(pe(50, 50), ctx);
|
|
expect(selected.length).toBe(1);
|
|
expect(selected[0]).toEqual(['e1']);
|
|
});
|
|
|
|
it('Leerklick (hitTest null): Auswahl geleert (setIds mit [])', () => {
|
|
const { ctx, selected } = makeCtx(['e9'], null);
|
|
selectTool.handlers.down!(pe(500, 500), ctx);
|
|
expect(selected.length).toBe(1);
|
|
expect(selected[0]).toEqual([]);
|
|
});
|
|
|
|
it('Shift-Klick additiv: setIds mit additive=true', () => {
|
|
const el = lineEl('e1', 0, 0, 100, 100);
|
|
const { ctx, selected } = makeCtx(['e9'], el);
|
|
selectTool.handlers.down!(pe(50, 50, true), ctx);
|
|
expect(selected.length).toBe(1);
|
|
// additive-Variante: bestaehige IDs + neue ID
|
|
expect(selected[0]).toEqual(['e9', 'e1']);
|
|
});
|
|
|
|
it('Box-Select: Element mit Zentrum in BBox wird selektiert', () => {
|
|
const { ctx, realDoc, getCurrentIds } = makeCtx([], null);
|
|
const el = lineEl('e1', 0, 0, 100, 100);
|
|
realDoc.addElement(el);
|
|
// down in leerer Gegend startet Box (Leerklick leert zuerst — korrekt),
|
|
// move zeigt Vorschau, up schliesst BBox um Element.
|
|
selectTool.handlers.down!(pe(-10, -10), ctx);
|
|
selectTool.handlers.move!(pe(200, 200), ctx);
|
|
selectTool.handlers.up!(pe(200, 200), ctx);
|
|
expect(getCurrentIds()).toEqual(['e1']);
|
|
});
|
|
|
|
it('onChange feuert nach Klick-Auswahl (App-Kanal)', () => {
|
|
const el = lineEl('e1', 0, 0, 100, 100);
|
|
const { ctx, onChangeCalls } = makeCtx([], el);
|
|
selectTool.handlers.down!(pe(50, 50), ctx);
|
|
expect(onChangeCalls.length).toBe(1);
|
|
expect(onChangeCalls[0]).toEqual(['e1']);
|
|
});
|
|
});
|