/** * Flow — a canvas of nodes joined by edges, that you pan, pinch and rearrange * with a finger. * * ```tsx * * * * … * * * … * * * * * ``` * * ## Where the positions live * * Every node's box is kept twice: on the UI thread, where a drag writes it * every frame and the node's own transform reads it, and in React state, where * the edges are rendered from it. A dragged node therefore never lags its own * finger, and the edges attached to it redraw as it moves. * * The tempting design is one copy — everything on the UI thread, edges * animating their own path strings, no renders at all. It does not draw. An * animated SVG path in React Native reliably animates its `d` and nothing * else, and only while nothing else about it is animated; anything more is * dropped with no error, leaving a path that never receives its geometry. So * the edges are ordinary elements and cost a render per drag frame. That is a * real cost and it is the right trade. * * An `animated` edge keeps that arrangement and animates the one property the * geometry does not own: the dash offset. Its `d` still arrives by re-render, * so a dragged node reshapes the edge while the dashes keep marching. * * JavaScript is told a drag has finished through `onNodeDragEnd`. Positions * are otherwise yours to leave alone: pass `position` once and the canvas * takes it from there, or keep it in state and pass it back to drive nodes * from outside. * * ## Two gestures, one canvas * * The pane pans and pinches; a node drags; a handle draws a new connection. * They are nested gesture detectors rather than one gesture doing three jobs, * so the innermost thing under the finger wins, which is what a finger expects. */ import { type ReactNode } from 'react'; import { type ViewProps } from 'react-native'; import { type FlowPoint, type FlowRect, type FlowSide } from './flow-paths.js'; import { type FlowEndpoint, type FlowEndpointReference } from './flow-identifiers.js'; export type FlowEdgeVariant = 'bezier' | 'smoothstep' | 'step' | 'straight'; export interface FlowViewport { x: number; y: number; zoom: number; } export interface FlowNodePosition { x: number; y: number; } /** What `onConnect` is handed when a connection lands. */ export interface FlowConnection { /** Node the drag started from. */ source: string; /** Handle it started from, when it started from one. */ sourceHandle?: string; /** Node it was dropped on. */ target: string; /** Handle it was dropped on, when it landed on one. */ targetHandle?: string; } /** * Which layer a part belongs to. Declared on the component as a static so the * root can sort its children without depending on function identity. */ type FlowSlot = 'background' | 'overlay'; export interface FlowProps extends Omit { className?: string; /** Where the canvas starts. `zoom` of 1 is one graph point per screen point. */ defaultViewport?: FlowViewport; /** Closest the canvas will zoom out. */ minZoom?: number; /** Closest it will zoom in. */ maxZoom?: number; /** Drag the empty canvas to move it. */ panOnDrag?: boolean; /** Pinch to zoom. */ zoomOnPinch?: boolean; /** * Frame every node once they have all measured themselves. For a graph whose * positions come from data and are not laid out against a known screen size. */ fitViewOnMount?: boolean; /** Padding left around the graph when fitting, in screen points. */ fitViewPadding?: number; /** The canvas has moved or zoomed. Fired as it happens, on the JS thread. */ onViewportChange?: (viewport: FlowViewport) => void; /** A node was dropped somewhere new. The only time a drag reaches JavaScript. */ onNodeDragEnd?: (id: string, position: FlowNodePosition) => void; /** * A connection was drawn between two handles. The canvas never adds the edge * itself — the graph is yours, so what a new connection means is yours too. */ onConnect?: (connection: FlowConnection) => void; /** Refuse a connection before `onConnect` sees it. */ isValidConnection?: (connection: FlowConnection) => boolean; children?: ReactNode; } declare function FlowRoot({ className, defaultViewport, minZoom, maxZoom, panOnDrag, zoomOnPinch, fitViewOnMount, fitViewPadding, onViewportChange, onNodeDragEnd, onConnect, isValidConnection, children, ...props }: FlowProps): import("react").JSX.Element; export interface FlowBackgroundProps { /** The mark repeated across the canvas. */ variant?: 'dots' | 'lines' | 'cross' | 'none'; /** Points between marks. */ gap?: number; /** How big each mark is drawn. */ size?: number; /** Mark colour. Defaults to a muted theme token. */ color?: string; } /** * The grid behind everything. It lives inside the transformed layer, so it * pans and scales with the graph — which is the whole point: a grid that * stayed put would say the canvas was not moving. */ declare function FlowBackground({ variant, gap, size, color, }: FlowBackgroundProps): import("react").JSX.Element | null; declare namespace FlowBackground { var displayName: string; var slot: FlowSlot; } export interface FlowNodeProps extends Omit { /** Identifies the node to edges and to `onNodeDragEnd`. Must be unique. */ id: string; /** Where it starts, in graph coordinates. */ position: FlowNodePosition; className?: string; /** Let a finger move it. */ draggable?: boolean; /** * Keep the node inside the `Flow.Group` it is drawn in. A drag stops at the * container's edge instead of leaving it. Ignored outside a group — there is * nothing to be kept inside of. */ confine?: boolean; /** * Hold the node still: it takes no drag of its own and moves only when its * container does. For a diagram where the boxes are what you rearrange and * their contents are a fixed part of them. */ pinned?: boolean; /** Draw the selected ring. */ selected?: boolean; /** Tapping the node — separate from dragging it. */ onPress?: () => void; /** * Delete the node when assistive technology requests the advertised Delete * node action. No delete action is exposed when this is omitted. */ onDelete?: () => void; /** Graph points covered by each Move up, right, down or left accessibility action. */ accessibilityMoveStep?: number; /** Spoken name. Defaults to the node's id. */ accessibilityLabel?: string; children?: ReactNode; } /** * One box on the canvas. Its own content is whatever you put inside — a Frame * is the usual answer, since a node is a titled card of rows more often than * it is anything else. */ declare function FlowNode({ id, position, className, draggable, confine, pinned, selected, onPress, onDelete, accessibilityMoveStep, accessibilityLabel, accessibilityActions, onAccessibilityAction, children, ...props }: FlowNodeProps): import("react").JSX.Element; declare namespace FlowNode { var displayName: string; } export interface FlowHandleProps { /** Names the handle to an edge, as `"nodeId.handleId"`. */ id?: string; /** Which face it sits on. */ position?: FlowSide; /** * `source` starts connections, `target` receives them, `both` does either. * A drag from a source can only land on a target, and the other way round. */ type?: 'source' | 'target' | 'both'; /** Where along the face, 0–1. For more than one handle on a side. */ offset?: number; className?: string; /** * Spoken handle name used in the parent node's connection actions. Defaults * to the handle's id. */ accessibilityLabel?: string; /** Draw nothing. The handle still anchors edges and still accepts a drop. */ hidden?: boolean; } /** * A port on a node — both the point an edge attaches to and the grip a new * connection is dragged from. * * Its position is worked out from the node's box and the face it names, so it * never has to measure itself. That matters: a handle that measured would be * one frame behind the node it sits on, and the edge would trail its own port. */ declare function FlowHandle({ id, position, type, offset, className, accessibilityLabel, hidden, }: FlowHandleProps): import("react").JSX.Element | null; declare namespace FlowHandle { var displayName: string; } export interface FlowEdgeProps { /** Source node or handle. Strings retain the `"nodeId.handleId"` shorthand. */ from: FlowEndpointReference; /** Target node or handle, in the same shape. */ to: FlowEndpointReference; /** How the edge is routed. */ variant?: FlowEdgeVariant; /** * Mark the edge as carrying something — a request, a build, a dependency * that is live rather than declared. Draws it dashed and marches the dashes * from source to target. Falls back to a still dashed edge when the * operating system is set to reduce motion. */ animated?: boolean; /** Draw it broken rather than solid. */ dashed?: boolean; /** * Stroke colour. Defaults to the muted-foreground token — an edge is content * rather than chrome, and the border token it would otherwise share with the * nodes is, by design, barely there. */ color?: string; /** Stroke width in graph points. */ width?: number; /** Put an arrowhead on the target end. */ arrow?: boolean; /** Override the face it leaves from. Otherwise worked out from the layout. */ fromSide?: FlowSide; /** Override the face it arrives at. */ toSide?: FlowSide; /** Corner radius for `smoothstep`. */ radius?: number; /** How far the edge steps clear of a node before turning. */ gap?: number; /** Curve strength for `bezier`. */ curvature?: number; } /** * A line between two nodes. It names them rather than coordinates, and works * out its own geometry from wherever they currently are — including which face * to use, which is why a graph the user rearranges does not need re-specifying. * * It draws nothing where it is written. Every edge in a canvas has to end up * inside one ``, under every node — an SVG element rendered among the * nodes is not in an SVG at all and silently draws nothing — so an edge * registers itself and the canvas paints it in the right layer. Which means an * edge can be written wherever it reads best: beside the nodes it joins, inside * a group, inside a `.map`, behind a condition. */ declare function FlowEdge(props: FlowEdgeProps): null; export interface FlowGroupProps extends Omit { /** Identifies the group, the same way a node's id does. */ id: string; /** Where the container sits. */ position: FlowNodePosition; /** How big the container is. Children are positioned inside it. */ size: { width: number; height: number; }; /** Caption drawn along the top edge. */ label?: string; className?: string; /** Move the group, and everything in it, with a finger. */ draggable?: boolean; children?: ReactNode; } /** * A container other nodes sit in and travel with. * * Dragging it offsets every child in the same worklet that moves the group, so * a group of twenty nodes costs one frame's work rather than twenty. */ declare function FlowGroup({ id, position, size, label, className, draggable, children, ...props }: FlowGroupProps): import("react").JSX.Element; declare namespace FlowGroup { var displayName: string; } export interface FlowControlsProps { className?: string; /** Show the zoom in and out buttons. */ zoom?: boolean; /** Show the fit-to-graph button. */ fit?: boolean; /** Show the lock, which freezes panning, zooming and dragging together. */ lock?: boolean; /** How much one press of zoom in multiplies the scale by. */ step?: number; } /** * The button stack in the corner. It sits outside the transformed layer, so it * stays put while the canvas moves under it. * * Worth having even where pinch works: on a phone, pinching to a specific * scale is imprecise, and framing the whole graph by hand is worse. */ declare function FlowControls({ className, zoom: showZoom, fit, lock, step, }: FlowControlsProps): import("react").JSX.Element; declare namespace FlowControls { var displayName: string; var slot: FlowSlot; } export interface FlowMiniMapProps { className?: string; /** Which corner it sits in. */ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'; /** Box size in screen points. */ width?: number; height?: number; /** Colour of a node in the map. Defaults to the muted-foreground token. */ nodeColor?: string; } /** * An overview of the whole graph with the visible region marked on it — for a * canvas bigger than the screen, where panning alone loses you. * * The map's own scale is derived once per frame and shared by every node in it, * so N nodes cost one bounds calculation rather than N. */ declare function FlowMiniMap({ className, position, width, height, nodeColor, }: FlowMiniMapProps): import("react").JSX.Element; declare namespace FlowMiniMap { var displayName: string; var slot: FlowSlot; } export declare const Flow: typeof FlowRoot & { Background: typeof FlowBackground; Node: typeof FlowNode; Handle: typeof FlowHandle; Edge: typeof FlowEdge; Group: typeof FlowGroup; Controls: typeof FlowControls; MiniMap: typeof FlowMiniMap; }; export type { FlowEndpoint, FlowEndpointReference, FlowSide, FlowRect, FlowPoint }; //# sourceMappingURL=index.d.ts.map