feat(engine): canvas panning, wheel zoom and link editing tools
Drag empty space to pan, mouse-wheel to zoom, and selecting a link now shows vertex/segment/arrowhead/remove tools so connectors can be rerouted and deleted interactively. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8779c88e66
commit
e43ace1853
@ -3,7 +3,7 @@
|
|||||||
* framework-agnostic API for the React shell: add nodes, edit properties,
|
* framework-agnostic API for the React shell: add nodes, edit properties,
|
||||||
* select/delete/copy, undo/redo, configure, save/load and run the mock flow.
|
* select/delete/copy, undo/redo, configure, save/load and run the mock flow.
|
||||||
*/
|
*/
|
||||||
import { dia, shapes, highlighters, g } from '@joint/core'
|
import { dia, shapes, highlighters, linkTools, g } from '@joint/core'
|
||||||
import {
|
import {
|
||||||
PipelineNode,
|
PipelineNode,
|
||||||
createNode,
|
createNode,
|
||||||
@ -113,10 +113,47 @@ export class DiagramController {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Pan the canvas by dragging empty space.
|
||||||
|
let pan: { tx: number; ty: number; x: number; y: number } | null = null
|
||||||
|
paper.on('blank:pointerdown', (evt: dia.Event) => {
|
||||||
|
const t = paper.translate()
|
||||||
|
pan = { tx: t.tx, ty: t.ty, x: evt.clientX ?? 0, y: evt.clientY ?? 0 }
|
||||||
|
})
|
||||||
|
paper.on('blank:pointermove', (evt: dia.Event) => {
|
||||||
|
if (!pan) return
|
||||||
|
paper.translate(pan.tx + ((evt.clientX ?? 0) - pan.x), pan.ty + ((evt.clientY ?? 0) - pan.y))
|
||||||
|
})
|
||||||
|
paper.on('blank:pointerup', () => (pan = null))
|
||||||
|
|
||||||
|
// Wheel to zoom toward the cursor.
|
||||||
|
paper.on('blank:mousewheel', (evt: dia.Event, _x, _y, delta: number) => {
|
||||||
|
evt.preventDefault?.()
|
||||||
|
this.zoom(delta > 0 ? 1.1 : 1 / 1.1)
|
||||||
|
})
|
||||||
|
paper.on('cell:mousewheel', (_v, evt: dia.Event, _x, _y, delta: number) => {
|
||||||
|
evt.preventDefault?.()
|
||||||
|
this.zoom(delta > 0 ? 1.1 : 1 / 1.1)
|
||||||
|
})
|
||||||
|
|
||||||
this.graph.on('remove', () => !this.restoring && this.fire('change'))
|
this.graph.on('remove', () => !this.restoring && this.fire('change'))
|
||||||
this.graph.on('add', () => !this.restoring && this.fire('change'))
|
this.graph.on('add', () => !this.restoring && this.fire('change'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private addLinkTools(link: dia.Link): void {
|
||||||
|
const view = this.paper?.findViewByModel(link)
|
||||||
|
if (!view) return
|
||||||
|
view.addTools(
|
||||||
|
new dia.ToolsView({
|
||||||
|
tools: [
|
||||||
|
new linkTools.Vertices(),
|
||||||
|
new linkTools.Segments(),
|
||||||
|
new linkTools.TargetArrowhead(),
|
||||||
|
new linkTools.Remove({ distance: 20 })
|
||||||
|
]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private validateConnection = (
|
private validateConnection = (
|
||||||
sourceView: dia.CellView,
|
sourceView: dia.CellView,
|
||||||
sourceMagnet: SVGElement | null,
|
sourceMagnet: SVGElement | null,
|
||||||
@ -197,6 +234,7 @@ export class DiagramController {
|
|||||||
padding: 4,
|
padding: 4,
|
||||||
attrs: { stroke: '#f0c000', 'stroke-width': 2 }
|
attrs: { stroke: '#f0c000', 'stroke-width': 2 }
|
||||||
})
|
})
|
||||||
|
if (cell.isLink()) this.addLinkTools(cell as dia.Link)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,7 +244,10 @@ export class DiagramController {
|
|||||||
if (!cell) continue
|
if (!cell) continue
|
||||||
if (!cell.isLink()) this.setPortsVisible(cell as dia.Element, false)
|
if (!cell.isLink()) this.setPortsVisible(cell as dia.Element, false)
|
||||||
const view = this.paper?.findViewByModel(cell)
|
const view = this.paper?.findViewByModel(cell)
|
||||||
if (view) highlighters.mask.remove(view, 'sel')
|
if (view) {
|
||||||
|
highlighters.mask.remove(view, 'sel')
|
||||||
|
if (cell.isLink()) view.removeTools()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user