Multi-Select
A single SelectionController wires up click-to-select, shift-click to toggle, drag-an-empty-
area marquee selection, and resize/rotate handles for a single selection — all as a
screen-space overlay. Read the selected set from controller.selection.nodes, and react to
changes with onChange.
import { Stage, History, SelectionController, Rect, Circle } from '@veyrajs/core'
const stage = new Stage({ container: document.getElementById('app')!, width: 800, height: 480,})const layer = stage.createLayer()layer.add( new Rect({ x: 80, y: 90, width: 130, height: 90, fill: '#38bdf8' }), new Rect({ x: 260, y: 150, width: 130, height: 90, fill: '#f472b6' }), new Circle({ x: 540, y: 150, radius: 56, fill: '#a78bfa' }),)
// Passing a History makes every move/resize/rotate undoable.const history = new History()const controller = new SelectionController(stage, { history })
// React whenever the selection changes (e.g. to update a properties panel).const unsubscribe = controller.selection.onChange((nodes) => { console.log(`${nodes.length} selected`)})
// Read the current selection at any time:function logSelection() { const selected = controller.selection.nodes // readonly view, in selection order console.log(selected.map((n) => n.type))}
// Teardown:// unsubscribe()// controller.destroy()SelectionController listens to stage pointer events in the capture phase, so it’s
authoritative over node-level handlers. Click a shape to select it, shift-click to add or
remove, or drag from empty canvas to draw a marquee that selects every shape whose world
bounds it intersects. The managed SelectionManager (controller.selection) is pure state —
beyond reading .nodes you can drive it directly with select, add, toggle, and clear.