Skip to content

Flow: per-connector styling, wrapping layout, and standalone Flow.Node #613

Description

@awhobbs87

Summary

Flow is a great fit for linear/parallel workflow diagrams, but its node and connector rendering are tightly coupled, which blocks a class of "labelled ladder" diagrams: sequences where each connector carries semantic meaning (colour/direction) and where the diagram should wrap rather than pan. Today the only escape hatch is to abandon Flow entirely and hand-roll connectors.

This issue proposes three related enhancements so Flow can cover these cases natively.

Motivating use case

We have a "tier ladder" in an internal admin tool: the rate plans in a product family shown left-to-right as an upgrade/downgrade path. The connectors are the information, not just decoration:

  • Green connector: the path from the lowest tier up to the account's currently-active tier ("already traversed").
  • Red, reversed connector: a downgrade candidate.
  • Neutral connector: everything else.

The ladder also lives in a dense, narrow column, so it wraps into rows of three with a "continues below" down-connector, rather than panning horizontally.

We evaluated migrating this to Flow and could not, for the reasons below.

Current limitations (v2.6.0)

  1. Connectors are owned entirely by <Flow> and expose no per-connector styling. Connector geometry is computed in FlowDiagram from registered node rects; the only per-node knob that reaches a connector is disabled (greys it). There is no supported way to say "the connector arriving at / leaving this node is red" or "this connector points the other way". (See packages/kumo/src/components/flow/.)

  2. Flow.Node cannot be used outside <Flow>. Flow.Node calls useDescendantsContext, which throws useDescendantsContext must be used within DescendantsProvider when there is no <Flow> ancestor. So consumers cannot reuse just the node presentation while composing their own connectors.

  3. Layout is single-line + pan only; no wrapping mode. When a horizontal diagram overflows its container it pans. There is no row-wrapping layout for dense/narrow containers.

The net effect: Flow is all-or-nothing. If you need any per-connector semantics or a wrapping layout, you must drop Flow and reimplement connectors by hand, losing the component's measurement/anchor machinery.

Proposed enhancements

Any one of these would help; ideally 1 + 3.

  1. Per-connector styling API. For example, a connector prop on Flow.Node (styling the incoming edge) or a renderConnector/connectorVariant prop on Flow / Flow.Parallel:

    <Flow>
      <Flow.Node>Tier 1</Flow.Node>
      <Flow.Node connector={{ variant: 'success' }}>Tier 2 (active)</Flow.Node>
      <Flow.Node connector={{ variant: 'danger', direction: 'reverse' }}>Tier 3 (downgrade)</Flow.Node>
    </Flow>

    A small set of semantic variants (default | success | danger | muted) that map to Kumo tokens would cover most cases; a render prop would cover the rest.

  2. Wrapping layout mode. An opt-in layout="wrap" (or wrap / columns) on Flow that flows nodes into rows when they exceed the container width, with connectors drawn per-row and an inter-row indicator, as an alternative to canvas panning.

  3. Standalone Flow.Node presentation. Let Flow.Node render its styled body when there is no DescendantsProvider (no-op the connector registration) instead of throwing, so the node's look-and-feel can be reused while a consumer composes connectors themselves. This decouples node presentation from the connector engine.

Why native support is preferable

Hand-rolling connectors means reimplementing getBoundingClientRect tracking, ResizeObserver, scroll/resize remeasurement, and anchor bookkeeping that Flow already does well. Exposing a thin styling/layout seam would let these semantic-ladder diagrams stay on Flow and benefit from that machinery.

Happy to help prototype the connector-styling seam if the direction sounds reasonable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions