/** * FunnelChart — how many were left at each step, and where the rest went. * * ```tsx * * * * * * ``` * * ## What it is, against the bars next door * * A bar chart compares quantities that need not have anything to do with each * other. A funnel makes a much stronger claim: every stage is a *subset of the * one above it*, in order, and the reader is being shown where a population * drained away. That is why the stages are not sorted, why nothing is * normalised to a total, and why the interesting number is not any stage's * value but the ratio between two of them. * * It follows that the order is the caller's and never the chart's. Stages are * steps in a process — a signup, a checkout, a support queue — and reordering * them by size would destroy the only thing the chart is asserting. A stage * larger than the one above it is therefore drawn as given, wider than its * parent, because that is a real and visible data problem and hiding it would * be the chart lying to save face. * * ## Drawing * * One ribbon running across the card, not a stack of blocks. The stages divide * the width between them, and each is a band symmetrical about the centre line * — as tall as its value where it starts and as tall as the next stage's where * it ends. The sides are curves that reach past each other, so consecutive * bands meet flush and the whole run reads as a single narrowing channel rather * than a row of separate shapes. The slope across a band is the drop. * * Each band is drawn several times over, concentrically: a wide faint ring on * the outside through to a tight near-solid core. It is a halo, and it does two * jobs. It gives the ribbon an edge that falls off rather than stopping dead; * and it leaves a band of low-opacity fill above and below the core that text * can sit on and still be read. * * The stages arrive one after another rather than together, each growing out of * the centre line. A funnel is a sequence, and a sequence that assembles in its * own order tells the reader which way to read it before they have read a word. * * ## Reading it * * The readings are laid out around the ribbon rather than crowded onto one * line: the count above the band, the name below it, and the conversion in a * pill in the middle of the band itself. Three readings, three places, none of * them competing for the same space — which is what keeps a stage name whole * under a column narrow enough to fit five of them on a phone, and what keeps * the pill legible whatever the ribbon is doing underneath it. * * ## Colour * * One hue, fading along the run, rather than a colour per stage. A funnel's * stages are one quantity at successive moments, not five unrelated series, and * five hues would say they were. A stage can still be given its own `color` * when it means something — the step where the money is taken, the one being * discussed — and that one is drawn at full strength. */ import { type ReactNode } from 'react'; import { type ViewProps } from 'react-native'; /** Whether the chart is showing data or waiting for it. */ export type FunnelChartStatus = 'loading' | 'ready'; /** Whether the sides of a band are drawn as curves or as straight diagonals. */ export type FunnelEdges = 'curved' | 'straight'; /** One step of the process. */ export interface FunnelDatum { /** Name of the step, for the label, the legend and the accessibility label. */ label: string; /** How many were left at it. Negatives are treated as zero. */ value: number; /** Explicit colour, drawn at full strength instead of the faded hue. */ color?: string; } /** The selected stage and how it converted, for something drawn inside the chart. */ export declare function useFunnelChart(): { activeIndex: number; activeStage: FunnelDatum | null; /** Its share of the first stage, 0 to 1. */ activeShare: number; /** Its share of the stage above it, 0 to 1. */ activeStep: number; }; export interface FunnelChartProps extends ViewProps { className?: string; /** The steps, in the order they happen. Never reordered. */ data: FunnelDatum[]; /** * How tall the run is drawn, in points. * * The run is as wide as it is given and as deep as this: the width is the * card's, but nothing in the data says how far the ribbon should taper * through, so it is a decision rather than a measurement. */ height?: number; /** * How wide one stage is, in points. * * Left unset the stages divide the width between them, which is nearly always * what a run across a card wants. Worth setting only to make a run stop short * of the edge. */ stageSize?: number; /** Space between one stage and the next, in points. */ gap?: number; /** * Concentric rings drawn per stage, faint and wide on the outside through to * a near-solid core. `1` draws the band once, flat. */ layers?: number; /** Whether the sides of a band are curves or straight diagonals. */ edges?: FunnelEdges; /** * The shortest a non-zero stage is drawn, as a share of the tallest. * * A stage worth a fifth of a percent of the first is a hairline: it reads as * missing rather than as small, and "missing" is a different claim. The floor * is only applied to stages that have something in them — a genuine zero is * drawn as nothing, because there it is the truth. */ minWidth?: number; /** The funnel's hue. Defaults to the first chart token. */ color?: string; /** Milliseconds for one stage to grow. */ animationDuration?: number; /** Milliseconds between one stage starting and the next. `0` for all at once. */ staggerDelay?: number; /** `loading` draws one plain muted ribbon until the data arrives. */ status?: FunnelChartStatus; /** Selected stage. Leave unset to let the chart track it. */ activeIndex?: number; /** Fires with the selected stage, or `-1` when the selection is cleared. */ onActiveIndexChange?: (index: number) => void; children?: ReactNode; } /** Imperative handle: re-run the entrance, for a "replay" control. */ export interface FunnelChartHandle { replay: () => void; } export interface FunnelChartStagesProps { /** Opacity of the stages that are not selected, once one is. */ dimOpacity?: number; } /** * Every stage, drawn in the order the data lists them. * * One part rather than one per datum. A stage's near edge is the previous * stage's far edge, so they cannot be configured apart without the ribbon * coming apart with them — a funnel whose third stage could be given its own * height would be a funnel drawing a shape that is not in the data. */ declare function FunnelChartStages({ dimOpacity }: FunnelChartStagesProps): import("react").JSX.Element | null; declare namespace FunnelChartStages { var displayName: string; var slot: "svg"; } export interface FunnelChartSkeletonProps { color?: string; } /** * The loading state: one plain ribbon over the whole run, undivided. * * Deliberately undivided. Placeholder stages would be an invented drop-off, and * a reader has no way to 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 FunnelChartSkeleton({ color }: FunnelChartSkeletonProps): import("react").JSX.Element | null; declare namespace FunnelChartSkeleton { var displayName: string; var slot: "svg"; } /** Which ratio a stage's pill reports. */ export type FunnelShare = 'previous' | 'top' | 'none'; export interface FunnelChartLabelsProps { className?: string; /** Format the count. Defaults to a compact number. */ formatValue?: (value: number, stage: FunnelDatum) => string; /** Format the conversion in the pill. Defaults to a whole percent. */ formatShare?: (share: number, stage: FunnelDatum) => string; /** * Which conversion the pill reports. `top` is the share of the first stage, * which every stage has and which reads along the run as one falling series. * `previous` is the drop from the stage above — the step-by-step reading, * where the first stage has nothing above it and so carries no pill. */ share?: FunnelShare; /** Show the count above the ribbon. */ showValue?: boolean; /** Show the stage's name under the ribbon. */ showLabel?: boolean; } /** * The readings, arranged around the ribbon: the count above the band, the name * under it, and the conversion in a pill on the band itself. * * Three places rather than one line, and that is the whole point of it. Put a * name, a count and a percentage together on one row and the row is as wide as * all three — so at the width a phone actually has, the name is the one that * gives way and the reader is left with "Checkout st…" against a number. Split * around the band, each reading has the stage's full column to itself. * * The pill is a filled chip rather than bare text because it is the one reading * that sits *on* the ribbon, where the fill behind it is the same token family * as the text would be. Punched out of its own background, it reads whatever * the band is doing underneath. * * The columns are pressable rather than the shape alone: a two-percent stage is * a sliver, and the column it lives in is a target. */ declare function FunnelChartLabels({ className, formatValue, formatShare, share, showValue, showLabel, }: FunnelChartLabelsProps): import("react").JSX.Element | null; declare namespace FunnelChartLabels { var displayName: string; var slot: "overlay"; } /** How the key under the run is arranged. */ export type FunnelLegendLayout = 'list' | 'inline'; export interface FunnelChartLegendProps extends ViewProps { className?: string; /** * `list` gives every stage a row of its own, with the names down one column * and the numbers down another. `inline` runs them together across the width * and wraps, which is the denser arrangement where the names are short. */ layout?: FunnelLegendLayout; /** Show each stage's reading beside its name. */ showValue?: boolean; /** Format the value in a `list` key. Defaults to a compact number. */ formatValue?: (value: number, stage: FunnelDatum) => string; } /** * A swatch, a name and a reading per stage, under the run. Pressable in the * same way the stages are. * * For a funnel drawn without `Labels` — a compact one on a dashboard card, * where the run is a shape and the reading is underneath it. * * A row each, by default. The stages of a funnel are a sequence, and a wrapped * centred line loses that: the reader gets a ragged block of names in which the * order is only implied by the order they happen to be read in, and a long * stage name breaks it across lines that no longer line up with anything. Down * a column the order is the order, and the numbers stack into a column of their * own that can be compared at a glance. */ declare function FunnelChartLegend({ className, layout, showValue, formatValue, ...props }: FunnelChartLegendProps): import("react").JSX.Element | null; declare namespace FunnelChartLegend { var displayName: string; var slot: "footer"; } export interface FunnelChartHeaderProps extends ViewProps { className?: string; /** Small line above the value — what the funnel 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 run: what the funnel is of and what it reads. * * It belongs to the chart rather than to the card around it because it is about * the *stages* — the number changes as one is selected. The card's header is a * caption on the tray the chart sits in; this is the chart introducing itself. * * The value is not derived even though there is a first stage to derive it * from, because the formatting is not the chart's to guess: 41800 is a count, * a currency or a rate depending on what was counted. */ declare function FunnelChartHeader({ className, title, value, caption, children, ...props }: FunnelChartHeaderProps): import("react").JSX.Element; declare namespace FunnelChartHeader { var displayName: string; var slot: "header"; } export declare const FunnelChart: import("react").ForwardRefExoticComponent> & { Header: typeof FunnelChartHeader; Stages: typeof FunnelChartStages; Labels: typeof FunnelChartLabels; Legend: typeof FunnelChartLegend; Skeleton: typeof FunnelChartSkeleton; }; export {}; //# sourceMappingURL=index.d.ts.map