Moving a device, moving or resizing a documentation item went into the shared document at every pointer move: 59 updates a second, each leaving about 18 bytes of history (M4 on test-filius). Now the editor keeps the drag while it lasts, shows it itself and tells the others through the awareness (moving, at most every 50 ms, the end at once), and writes it once when it is let go. A drag the browser cancels or a second finger turns into a pinch is written where it is; WebKit sends a second pointerdown for the first finger when another comes, which had lost the drag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
67 lines
2.9 KiB
TypeScript
67 lines
2.9 KiB
TypeScript
// A drag (moving.ts, M4): shown over the document while it lasts — a device
|
|
// at its dragged place, an item in the frame the document will hold — and
|
|
// written into the document once, as one change, when it is let go.
|
|
|
|
import { randomUUID } from 'node:crypto';
|
|
import { describe, expect, it } from 'vitest';
|
|
import * as Y from 'yjs';
|
|
import { addDevice, addDocumentation, emptyProject, topologyView, writeProject } from '@mdg-filius/doc';
|
|
import { commitMove, movedView } from '../src/moving.js';
|
|
|
|
function project() {
|
|
const doc = new Y.Doc();
|
|
writeProject(doc, emptyProject());
|
|
addDevice(doc, { kind: 'pc', id: 'pc', name: 'Rechner', x: 100, y: 100, newId: randomUUID }, 'me');
|
|
addDevice(doc, { kind: 'notebook', id: 'nb', name: 'Notebook', x: 300, y: 100, newId: randomUUID }, 'me');
|
|
addDocumentation(doc, { id: 'box', kind: 'rectangle', x: 50, y: 50 }, 'me');
|
|
return doc;
|
|
}
|
|
|
|
describe('a drag over the document', () => {
|
|
it('is the view itself without a drag', () => {
|
|
const view = topologyView(project());
|
|
expect(movedView(view, [])).toBe(view);
|
|
});
|
|
|
|
it('shows a dragged device where it is dragged and leaves the others where they are', () => {
|
|
const view = movedView(topologyView(project()), [{ kind: 'device', id: 'pc', x: 140.5, y: 260 }]);
|
|
expect(view.nodes.map((node) => [node.id, node.x, node.y])).toEqual([
|
|
['pc', 140.5, 260],
|
|
['nb', 300, 100],
|
|
]);
|
|
});
|
|
|
|
it('shows the later of two drags of one device (this editor puts its own last)', () => {
|
|
const view = movedView(topologyView(project()), [
|
|
{ kind: 'device', id: 'pc', x: 1, y: 1 },
|
|
{ kind: 'device', id: 'pc', x: 2, y: 2 },
|
|
]);
|
|
expect(view.nodes.find((node) => node.id === 'pc')).toMatchObject({ x: 2, y: 2 });
|
|
});
|
|
|
|
it('shows a dragged item in the frame the document will hold, and passes over what it does not know', () => {
|
|
const doc = project();
|
|
const before = topologyView(doc);
|
|
const view = movedView(before, [
|
|
{ kind: 'doc', id: 'box', x: 200, y: 120, width: -80, height: 40 },
|
|
{ kind: 'device', id: 'gone', x: 1, y: 1 },
|
|
]);
|
|
const box = view.docs.find((item) => item.id === 'box')!;
|
|
expect([box.x, box.y, box.width, box.height]).toEqual([120, 120, 80, 40]);
|
|
expect(view.nodes).toEqual(before.nodes);
|
|
commitMove(doc, { kind: 'doc', id: 'box', x: 200, y: 120, width: -80, height: 40 }, 'me');
|
|
const stored = topologyView(doc).docs.find((item) => item.id === 'box')!;
|
|
expect([stored.x, stored.y, stored.width, stored.height]).toEqual([box.x, box.y, box.width, box.height]);
|
|
});
|
|
|
|
it('writes a drag into the document as one change, rounded as Java holds places', () => {
|
|
const doc = project();
|
|
let changes = 0;
|
|
doc.on('update', () => (changes += 1));
|
|
commitMove(doc, { kind: 'device', id: 'pc', x: 140.6, y: 259.4 }, 'me');
|
|
expect(changes).toBe(1);
|
|
const pc = topologyView(doc).nodes.find((node) => node.id === 'pc')!;
|
|
expect([pc.x, pc.y]).toEqual([141, 259]);
|
|
});
|
|
});
|