feat(canvas): blank-drag panning and Ctrl/Cmd+wheel zoom-to-cursor
- Pan the viewport by dragging empty canvas - Ctrl/Cmd + mouse wheel zooms toward the pointer; handler cleaned up on destroy - Make PropertiesPanel's selectionRev dependency explicit for the linter Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
701ed61a82
commit
94026754b7
@ -200,15 +200,36 @@ export class CanvasController {
|
|||||||
this.selectCell(link, false);
|
this.selectCell(link, false);
|
||||||
this.commit();
|
this.commit();
|
||||||
});
|
});
|
||||||
p.on("blank:pointerdown", () => {
|
// Blank drag pans the canvas.
|
||||||
|
p.on("blank:pointerdown", (evt: dia.Event) => {
|
||||||
this.clearSelection();
|
this.clearSelection();
|
||||||
|
const t = this.paper.translate();
|
||||||
|
this.panStart = { x: (evt.clientX ?? 0) - t.tx, y: (evt.clientY ?? 0) - t.ty };
|
||||||
|
});
|
||||||
|
p.on("blank:pointermove", (evt: dia.Event) => {
|
||||||
|
if (!this.panStart) return;
|
||||||
|
this.paper.translate((evt.clientX ?? 0) - this.panStart.x, (evt.clientY ?? 0) - this.panStart.y);
|
||||||
|
});
|
||||||
|
p.on("blank:pointerup", () => {
|
||||||
|
this.panStart = null;
|
||||||
});
|
});
|
||||||
p.on("cell:pointerdblclick", (view) => {
|
p.on("cell:pointerdblclick", (view) => {
|
||||||
// double-click focuses without toggling multi-select
|
// double-click focuses without toggling multi-select
|
||||||
this.selectCell(view.model, false);
|
this.selectCell(view.model, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Ctrl/⌘ + wheel zooms toward the cursor.
|
||||||
|
this.wheelHandler = (e: WheelEvent) => {
|
||||||
|
if (!(e.ctrlKey || e.metaKey)) return;
|
||||||
|
e.preventDefault();
|
||||||
|
this.zoom(e.deltaY < 0 ? 1.1 : 1 / 1.1, { x: e.clientX, y: e.clientY });
|
||||||
|
};
|
||||||
|
this.paper.el.addEventListener("wheel", this.wheelHandler, { passive: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private panStart: { x: number; y: number } | null = null;
|
||||||
|
private wheelHandler: ((e: WheelEvent) => void) | null = null;
|
||||||
|
|
||||||
private snapElement(el: dia.Element): void {
|
private snapElement(el: dia.Element): void {
|
||||||
const g = this.config.gridSize;
|
const g = this.config.gridSize;
|
||||||
const { x, y } = el.position();
|
const { x, y } = el.position();
|
||||||
@ -720,6 +741,7 @@ export class CanvasController {
|
|||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
this.stopFlow();
|
this.stopFlow();
|
||||||
|
if (this.wheelHandler) this.paper.el.removeEventListener("wheel", this.wheelHandler);
|
||||||
this.paper.remove();
|
this.paper.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,6 +29,7 @@ export function PropertiesPanel() {
|
|||||||
const cell = selection && controller ? controller.getPrimary() : null;
|
const cell = selection && controller ? controller.getPrimary() : null;
|
||||||
|
|
||||||
const model = useMemo(() => {
|
const model = useMemo(() => {
|
||||||
|
void rev; // recompute when the selected cell's props change externally
|
||||||
if (!cell || !selection) return null;
|
if (!cell || !selection) return null;
|
||||||
const props = (cell.prop("props") ?? {}) as Record<string, PropertyValue>;
|
const props = (cell.prop("props") ?? {}) as Record<string, PropertyValue>;
|
||||||
if (selection.kind === "node") {
|
if (selection.kind === "node") {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user