From 9e56464863421181756f1972a3da6c8ef0ef6021 Mon Sep 17 00:00:00 2001 From: Matt Rothenberg Date: Fri, 26 Jun 2026 09:00:22 -0400 Subject: [PATCH 1/2] feat(flow): add vertical orientation Wires up the previously no-op `orientation="vertical"` prop on Flow. The connector path routing and parallel-branch geometry already handled the vertical axis; this fills the remaining gap in FlowNodeList's top-level connector computation and makes the node list lay out top-to-bottom, with `align` controlling cross-axis (horizontal) alignment. Adds vertical demos, docs, and tests. --- .../src/components/demos/FlowDemo.tsx | 26 +++++++++++ .../src/pages/components/flow.mdx | 35 ++++++++++++++- .../src/pages/tests/flow.astro | 24 ++++++++++ packages/kumo/src/components/flow/diagram.tsx | 45 +++++++++++++------ .../kumo/src/components/flow/flow.test.tsx | 25 +++++++++++ 5 files changed, 139 insertions(+), 16 deletions(-) diff --git a/packages/kumo-docs-astro/src/components/demos/FlowDemo.tsx b/packages/kumo-docs-astro/src/components/demos/FlowDemo.tsx index 0b0cee0761..48dfb1fe54 100644 --- a/packages/kumo-docs-astro/src/components/demos/FlowDemo.tsx +++ b/packages/kumo-docs-astro/src/components/demos/FlowDemo.tsx @@ -43,6 +43,32 @@ export function FlowBasicDemo() { ); } +/** Vertical flow diagram with sequential nodes */ +export function FlowVerticalDemo() { + return ( + + Step 1 + Step 2 + Step 3 + + ); +} + +/** Vertical flow diagram with parallel branching */ +export function FlowVerticalParallelDemo() { + return ( + + Start + + Branch A + Branch B + Branch C + + End + + ); +} + /** Flow diagram with parallel branching */ export function FlowParallelDemo() { return ( diff --git a/packages/kumo-docs-astro/src/pages/components/flow.mdx b/packages/kumo-docs-astro/src/pages/components/flow.mdx index ac75cd7b6b..6e9aef0a5f 100644 --- a/packages/kumo-docs-astro/src/pages/components/flow.mdx +++ b/packages/kumo-docs-astro/src/pages/components/flow.mdx @@ -9,6 +9,8 @@ import ComponentExample from "~/components/docs/ComponentExample.astro"; import ComponentSection from "~/components/docs/ComponentSection.astro"; import { FlowBasicDemo, + FlowVerticalDemo, + FlowVerticalParallelDemo, FlowParallelDemo, FlowCustomContentDemo, FlowComplexDemo, @@ -100,6 +102,25 @@ export default function Example() { +### Vertical Orientation + +

+ Set `orientation="vertical"` to lay nodes out top-to-bottom instead of the + default left-to-right. Connectors, parallel branches, and `align` all adapt + to the vertical axis. +

+ + + + +

+ Parallel branches work the same way in vertical flows. In this orientation + `align` controls horizontal alignment of nodes. +

+ + + + ### Custom Node Styling

@@ -238,12 +259,22 @@ export default function Example() { + + orientation + "horizontal" | "vertical" + + Layout direction of the flow. `"horizontal"` (default) lays nodes out + left-to-right, `"vertical"` lays them out top-to-bottom. + + align "start" | "center" - Vertical alignment of nodes. `"start"` (default) aligns to top, - `"center"` vertically centers nodes. + Cross-axis alignment of nodes. In horizontal orientation this is + vertical alignment; in vertical orientation it is horizontal + alignment. `"start"` (default) aligns to the start of the cross axis, + `"center"` centers nodes. diff --git a/packages/kumo-docs-astro/src/pages/tests/flow.astro b/packages/kumo-docs-astro/src/pages/tests/flow.astro index 5165aaf14d..94195929f4 100644 --- a/packages/kumo-docs-astro/src/pages/tests/flow.astro +++ b/packages/kumo-docs-astro/src/pages/tests/flow.astro @@ -2,6 +2,8 @@ import BaseLayout from "../../layouts/BaseLayout.astro"; import { FlowBasicDemo, + FlowVerticalDemo, + FlowVerticalParallelDemo, FlowParallelDemo, FlowCustomContentDemo, FlowComplexDemo, @@ -28,6 +30,28 @@ import { +

+

FlowVerticalDemo

+

+ Vertical flow diagram with sequential nodes +

+
+ +
+
+ +
+

+ FlowVerticalParallelDemo +

+

+ Vertical flow diagram with parallel branching +

+
+ +
+
+

FlowParallelDemo

Flow diagram with parallel branching

diff --git a/packages/kumo/src/components/flow/diagram.tsx b/packages/kumo/src/components/flow/diagram.tsx index ae519d6b20..fe0f9de4a1 100644 --- a/packages/kumo/src/components/flow/diagram.tsx +++ b/packages/kumo/src/components/flow/diagram.tsx @@ -40,7 +40,6 @@ function isEventFromNode(target: EventTarget | null): boolean { /** Minimum scrollbar thumb size in percentage to ensure visibility */ const MIN_SCROLLBAR_THUMB_SIZE = 10; -// Vertical orientation is currently a no-op type Orientation = "horizontal" | "vertical"; type Align = "start" | "center"; @@ -66,9 +65,11 @@ export function useDiagramContext(): DiagramContextValue { interface FlowDiagramProps { orientation?: Orientation; /** - * Controls vertical alignment of nodes in horizontal orientation. - * - `start`: Nodes align to the top (default) - * - `center`: Nodes are vertically centered + * Controls cross-axis alignment of nodes. + * In horizontal orientation this is vertical alignment; in vertical + * orientation it is horizontal alignment. + * - `start`: Nodes align to the start of the cross axis (default) + * - `center`: Nodes are centered on the cross axis */ align?: Align; /** @@ -428,11 +429,26 @@ export function FlowNodeList({ children }: { children: ReactNode }) { if (currentRect && nextRect) { const isDisabled = currentNode.props.disabled || nextNode.props.disabled; + // Horizontal flows left-to-right: connect the right edge of the + // current node to the left edge of the next. Vertical flows + // top-to-bottom: connect the bottom edge of the current node to the + // top edge of the next, using horizontal centers. + const edge = + orientation === "vertical" + ? { + x1: currentRect.left - offsetX + currentRect.width / 2, + y1: currentRect.top - offsetY + currentRect.height, + x2: nextRect.left - offsetX + nextRect.width / 2, + y2: nextRect.top - offsetY, + } + : { + x1: currentRect.left - offsetX + currentRect.width, + y1: currentRect.top - offsetY + currentRect.height / 2, + x2: nextRect.left - offsetX, + y2: nextRect.top - offsetY + nextRect.height / 2, + }; edges.push({ - x1: currentRect.left - offsetX + currentRect.width, - y1: currentRect.top - offsetY + currentRect.height / 2, - x2: nextRect.left - offsetX, - y2: nextRect.top - offsetY + nextRect.height / 2, + ...edge, disabled: isDisabled, single: true, fromId: currentNode.id, @@ -442,7 +458,7 @@ export function FlowNodeList({ children }: { children: ReactNode }) { } setConnectors(edges); - }, [descendants.descendants]); + }, [descendants.descendants, orientation]); /** * Recompute connectors after layout so that containerRect and node rects are @@ -497,12 +513,13 @@ export function FlowNodeList({ children }: { children: ReactNode }) {
    {children} diff --git a/packages/kumo/src/components/flow/flow.test.tsx b/packages/kumo/src/components/flow/flow.test.tsx index b51a2667a2..30410b050e 100644 --- a/packages/kumo/src/components/flow/flow.test.tsx +++ b/packages/kumo/src/components/flow/flow.test.tsx @@ -19,6 +19,31 @@ describe("Flow", () => { expect(path).toBe("M 0 17 L 32 17 L 32 63 Q 32 71 40 71 L 48 71"); expect(path).not.toContain(","); }); + + it("routes vertical paths through a horizontal mid-segment", () => { + const path = createRoundedPath( + { x1: 17, y1: 0, x2: 71, y2: 56 }, + { orientation: "vertical", single: false }, + ); + + expect(path).toContain("M 17 0"); + expect(path).not.toContain(","); + }); + }); + + describe("Vertical orientation", () => { + it("renders sequential nodes top-to-bottom in a column", () => { + render( + + Step 1 + Step 2 + Step 3 + , + ); + + const list = screen.getByText("Step 1").closest("ul"); + expect(list?.className).toContain("flex-col"); + }); }); describe("Compound component API", () => { From a20bbc4877890f82f0b5fe5698f7f0dde5c7de46 Mon Sep 17 00:00:00 2001 From: Matt Rothenberg Date: Fri, 26 Jun 2026 09:00:49 -0400 Subject: [PATCH 2/2] chore: add changeset for flow vertical orientation --- .changeset/flow-vertical-orientation.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/flow-vertical-orientation.md diff --git a/.changeset/flow-vertical-orientation.md b/.changeset/flow-vertical-orientation.md new file mode 100644 index 0000000000..6cd18f2497 --- /dev/null +++ b/.changeset/flow-vertical-orientation.md @@ -0,0 +1,5 @@ +--- +"@cloudflare/kumo": minor +--- + +Add `orientation="vertical"` support to `Flow` for laying out nodes top-to-bottom. The `align` prop now controls cross-axis alignment in both orientations.