/**
* SankeyChart — where a quantity came from and where it ended up.
*
* ```tsx
*
*
*
*
*
*
*
* ```
*
* ## What it answers that the other charts do not
*
* Every other chart here takes one set of things and measures them. This one
* takes two and says how much of the first became the second. A treemap cuts a
* total into its parts, a funnel counts what survived each step, a waterfall
* carries a balance from one figure to another — none of them can say that
* *this* source fed *that* destination, because none of them draws a thing that
* has two ends.
*
* So the question to bring to it is a routing question. Which campaigns
* produced which signups; which budget lines paid for which departments; what
* the traffic that arrived on the landing page went on to do. If the answer
* does not need a source *and* a target, one of the simpler charts will read
* better at the same size.
*
* ## The ribbon is the reading
*
* A ribbon's thickness is its value, on one scale shared by the whole diagram,
* so a ribbon twice as thick is twice as much wherever it is on the page. That
* is the only quantity here: the horizontal distance a ribbon travels is the
* number of columns between its ends and means nothing else, and the vertical
* order within a column is chosen to keep the ribbons from crossing rather than
* to rank anything.
*
* A node's height is what passes through it — the larger of what arrives and
* what leaves, which are the same number unless some of it went nowhere. Where
* they differ, give the node an explicit `value` to pin it; the diagram cannot
* infer a loss it was never told about.
*
* ## Columns are the flow's, not the caller's
*
* Nothing about the order of the `nodes` array decides where a node is drawn. A
* node that receives from another has to be drawn after it or its ribbon would
* run backwards, so the columns come out of the links. `align` only settles the
* cases the flow leaves open, and the default pushes every node that feeds
* nothing into the last column, so the diagram ends on a straight edge of
* destinations instead of a ragged one.
*
* ## Bad rows are dropped rather than fatal
*
* Flow data is nearly always joined together from somewhere nobody in the room
* owns, and it arrives with rows that name a node that is not there, carry a
* zero, or close a loop. A loop in particular has no left-to-right reading at
* all. All of them are dropped and counted, and `onDropLinks` reports how many
* — so a screen can say "3 rows could not be drawn" instead of going blank or
* quietly showing less than it was given.
*/
import { type ReactNode } from 'react';
import { type ViewProps } from 'react-native';
import { type ChartAccessibilityProps } from '../../primitives/chart-accessibility.js';
import { type SankeyAlign } from './sankey-layout.js';
export type { SankeyAlign } from './sankey-layout.js';
export type SankeyChartStatus = 'loading' | 'ready';
export interface SankeyNode {
/** Stable key the links name. Must be unique; a repeat is ignored. */
id: string;
/** Name drawn beside the bar. Defaults to the id. */
label?: string;
/** Explicit colour, instead of the node's place in the palette. */
color?: string;
/**
* Pins what passes through the node, for a stage that loses some of what it
* received to somewhere the links do not describe. Left unset the node is
* worth the larger of what arrives and what leaves.
*/
value?: number;
}
export interface SankeyLink {
/** `id` of the node it leaves. */
source: string;
/** `id` of the node it reaches. */
target: string;
/** How much travels. Zero, negative and non-finite rows are dropped. */
value: number;
/** Explicit colour, instead of taking the source node's. */
color?: string;
}
/** The selected node and what runs through it, for something drawn inside the chart. */
export declare function useSankeyChart(): {
activeId: string | null;
activeNode: SankeyNode | null;
/** What passes through it — what the bar's height is drawn from. */
activeValue: number;
/** What arrives. Zero at a node the flow starts from. */
incoming: number;
/** What leaves. Zero at a node the flow ends at. */
outgoing: number;
};
export interface SankeyChartProps extends ViewProps, ChartAccessibilityProps {
className?: string;
/** The stages. Order does not decide position — the links do. */
nodes: SankeyNode[];
/** What travels between them. */
links: SankeyLink[];
/**
* How tall the diagram is drawn, in points.
*
* The width is the card's, but nothing in a flow says how deep it should be:
* a diagram of four nodes and one of forty are the same data at two heights,
* and which of them is right is a question about the screen.
*/
height?: number;
/** How thick a node's bar is, in points. */
nodeWidth?: number;
/**
* The gap between two nodes in a column, in points.
*
* A maximum rather than a promise. A crowded column gives its spacing up
* before it gives up the height of its bars, because the bar is the reading.
*/
nodePadding?: number;
/** Which column a node goes in where the flow leaves a choice. */
align?: SankeyAlign;
/** Relaxation rounds spent untangling the ribbons. */
iterations?: number;
/** How far a ribbon bends, `0` for a straight diagonal and `0.5` for an S. */
curve?: number;
/** The first hue. The rest of the palette follows from the theme's tokens. */
color?: string;
/** Milliseconds for one column to draw itself. */
animationDuration?: number;
/** Milliseconds between one column starting and the next. `0` for all at once. */
staggerDelay?: number;
/** `loading` draws a plain placeholder until the data arrives. */
status?: SankeyChartStatus;
/** Selected node. Leave unset to let the chart track it. */
activeId?: string | null;
/** Fires with the selected node's id, or `null` when the selection is cleared. */
onActiveIdChange?: (id: string | null) => void;
/**
* Fires with how many link rows could not be drawn — ones naming a node that
* is not there, carrying nothing, or closing a loop. `0` after a clean render,
* so a banner can be shown and taken away from the same signal.
*/
onDropLinks?: (count: number) => void;
children?: ReactNode;
}
/** Imperative handle: re-run the entrance, for a "replay" control. */
export interface SankeyChartHandle {
replay: () => void;
}
export interface SankeyChartLinksProps {
/** A ribbon's opacity at rest. */
opacity?: number;
/** A ribbon's opacity when its node is selected. */
activeOpacity?: number;
/** And when something else is. */
dimOpacity?: number;
}
/**
* The ribbons.
*
* Drawn before the bars so a bar sits on top of the flows that meet it, which
* is what gives a node a clean edge to arrive at instead of a fringe of ribbon
* ends poking through it.
*
* Translucent at rest, and that is not decoration. Ribbons cross — that is the
* shape of routed data — and an opaque one hides whatever passes under it, so
* the reader loses the smaller of every pair. At this opacity a crossing reads
* as two ribbons rather than as one with a notch in it.
*/
declare function SankeyChartLinks({ opacity, activeOpacity, dimOpacity, }: SankeyChartLinksProps): import("react").JSX.Element | null;
declare namespace SankeyChartLinks {
var displayName: string;
var slot: "svg";
}
export interface SankeyChartNodesProps {
/** Corner radius on a node's bar, in points. */
radius?: number;
/** A bar's opacity when something else is selected. */
dimOpacity?: number;
}
/**
* The bars the ribbons run between.
*
* Solid where the ribbons are translucent, because a node is the one thing on
* the diagram that is not crossing anything else — it is the edge the flow
* arrives at, and it reads as an edge only if nothing shows through it.
*/
declare function SankeyChartNodes({ radius, dimOpacity }: SankeyChartNodesProps): import("react").JSX.Element | null;
declare namespace SankeyChartNodes {
var displayName: string;
var slot: "svg";
}
export interface SankeyChartLabelsProps {
className?: string;
/** Format the figure beside a name. Defaults to a compact number. */
formatValue?: (value: number, node: SankeyNode) => string;
/** Show the figure under the name. */
showValue?: boolean;
/**
* Hide the name on a bar shorter than this, in points.
*
* A diagram of forty nodes has bars a few points tall, and forty names at
* that spacing overlap into a grey band that hides the flow behind it. The
* names that are dropped are the smallest ones, which is where the tooltip
* takes over.
*/
minHeight?: number;
}
/**
* The names, and the press targets that go with them.
*
* Outside the bars rather than on them. A node's bar is as thick as it was
* asked to be — ten points by default — and no name fits inside ten points, so
* putting the name on the bar means widening every bar to suit the longest
* label and losing the width the ribbons need.
*
* Which side a name goes on is decided by the column: the last column reads
* inwards from the right edge, everything else outwards to the right. So the
* names stay inside the chart's box at both ends, instead of the leftmost and
* rightmost ones being clipped.
*
* The target is the label's row, not the bar. A node worth one percent of the
* flow is a two-point sliver and cannot be hit; the row it sits in can, and it
* is padded out to a proper target where the sliver is smaller than one.
*/
declare function SankeyChartLabels({ className, formatValue, showValue, minHeight, }: SankeyChartLabelsProps): import("react").JSX.Element | null;
declare namespace SankeyChartLabels {
var displayName: string;
var slot: "overlay";
}
export interface SankeyChartTooltipProps {
className?: string;
/** Format the figures. Defaults to a compact number. */
formatValue?: (value: number) => string;
}
/**
* What the selected node carries: the total through it, and what that total is
* made of at each end.
*
* In and out are shown separately because they are the two readings a flow
* diagram is for, and they are only the same number when nothing was lost. A
* node where they differ is the interesting one on the whole chart, and a
* single total would hide exactly that.
*
* Anchored beside the node and clamped to the plot, so it never leaves the box
* it belongs to — a card half off the edge of a phone is a card nobody can read.
*/
declare function SankeyChartTooltip({ className, formatValue }: SankeyChartTooltipProps): import("react").JSX.Element | null;
declare namespace SankeyChartTooltip {
var displayName: string;
var slot: "overlay";
}
export interface SankeyChartSkeletonProps {
color?: string;
}
/**
* The loading state: a few plain bars and the ribbons between them, carrying no
* values.
*
* Every bar the same height and every ribbon the same thickness, deliberately.
* A placeholder with varied thicknesses would be an invented routing, and a
* reader cannot tell an invented one from a real one until it changes under
* them — which is worse than showing nothing, because it is showing something
* wrong.
*/
declare function SankeyChartSkeleton({ color }: SankeyChartSkeletonProps): import("react").JSX.Element | null;
declare namespace SankeyChartSkeleton {
var displayName: string;
var slot: "svg";
}
export interface SankeyChartHeaderProps extends ViewProps {
className?: string;
/** Small line above the value — what the flow is of. */
title?: string;
/** The readout. The largest thing on the card, and the first thing read. */
value?: string;
/** One muted line under the value — a period, a comparison, a caveat. */
caption?: string;
/** Trailing slot — a control, a badge, a range picker. */
children?: ReactNode;
}
/**
* The strip above the diagram: what the flow is of and what it totals.
*
* The value is not derived even though there are sources to add up, because the
* formatting is not the chart's to guess: 128400 is a count, a currency or a
* rate depending on what was routed.
*/
declare function SankeyChartHeader({ className, title, value, caption, children, ...props }: SankeyChartHeaderProps): import("react").JSX.Element;
declare namespace SankeyChartHeader {
var displayName: string;
var slot: "header";
}
export declare const SankeyChart: import("react").ForwardRefExoticComponent> & {
Header: typeof SankeyChartHeader;
Links: typeof SankeyChartLinks;
Nodes: typeof SankeyChartNodes;
Labels: typeof SankeyChartLabels;
Tooltip: typeof SankeyChartTooltip;
Skeleton: typeof SankeyChartSkeleton;
};
//# sourceMappingURL=index.d.ts.map