feat(flowchart): node resizing (C10)

v4.13.0 — nodes can be resized by dragging the bottom-right handle.

  - New setNodeWidth(id, width) on the store. Clamps to [60, 600] so
    shapes stay readable. Throws on unknown id; pushes an undo
    snapshot like every other mutator.
  - New nodeWidth(node) helper on the canvas uses node.width with a
    fallback to DEFAULT_WIDTH for graphs loaded from older JSON
    that didn't have the field.
  - Canvas render uses the per-node width for shapeSvg + label
    positioning, and edge boundaryPoint() uses it too so connection
    geometry adapts to the new shape size.
  - Selected node renders a bottom-right resize handle (10px square).
    Clicking the handle starts a 'resize' dragState that calls
    setNodeWidth on every pointermove with the screen-to-SVG delta
    applied to the start width.
  - The handle is detected before the generic node click handler so
    it doesn't accidentally start a move drag.

3 new tests cover setNodeWidth update, clamp to [60, 600], and
unknown-id throw. The canvas change reuses the existing 14 tests
(which check rendering and pointer events) — they still pass.

Amit Haridas
This commit is contained in:
Amit Haridas
2026-09-30 21:42:35 +05:30
parent 22bc4dcf56
commit ded17d0b6b
3 changed files with 99 additions and 5 deletions
+21
View File
@@ -101,6 +101,27 @@ describe('flowchart-store: node operations', () => {
expect(() => store.setNodeColor('nope', '#ff0000')).toThrow(/nope/);
});
test('setNodeWidth updates the width of an existing node', () => {
const store = create(makeIO());
const node = store.addNode({ kind: 'process', x: 0, y: 0, label: '' });
store.setNodeWidth(node.id, 220);
expect(store.getGraph().nodes[0].width).toBe(220);
});
test('setNodeWidth clamps to [60, 600]', () => {
const store = create(makeIO());
const node = store.addNode({ kind: 'process', x: 0, y: 0, label: '' });
store.setNodeWidth(node.id, 10);
expect(store.getGraph().nodes[0].width).toBe(60);
store.setNodeWidth(node.id, 99999);
expect(store.getGraph().nodes[0].width).toBe(600);
});
test('setNodeWidth throws on unknown node id', () => {
const store = create(makeIO());
expect(() => store.setNodeWidth('nope', 200)).toThrow(/nope/);
});
test('setNodeColor pushes an undo snapshot', () => {
const store = create(makeIO());
const node = store.addNode({ kind: 'process', x: 0, y: 0, label: '' });