/** * TreemapChart — a total, cut into the things it is made of, by area. * * ```tsx * * * * * * * ``` * * ## What it is for, against the dial next door * * A pie and a treemap answer the same question — what is this total made of — * and they fail at different sizes. A dial can carry five or six slices before * the small ones become slivers with nowhere to put a name. A treemap keeps * going, because a share is a rectangle rather than an angle: it can be read * at a tenth the size, it tiles the box with nothing left over, and it has a * flat side to write on. * * So the rule of thumb is the count. Up to about six parts, a `PieChart` is * easier to read and more familiar. Past that, a treemap is the one that still * works. * * The trade-off is precision. People compare angles badly and areas worse, so * nobody should be reading values off the tiles — the layout is for *ranking * and grouping* at a glance, and the numbers are in the labels. * * ## The layout * * Squarified. Tiles are laid in rows across whichever side of the remaining * space is shorter, and a row takes another tile only while doing so makes its * worst rectangle *less* elongated than it already is. The result is tiles * close to square, which matters for two reasons: a square is the shape whose * area the eye judges least badly, and it is the only shape with room for a * name across it. * * It follows that the tiles are sorted, largest first, and the order is the * chart's rather than the caller's. An unsorted treemap squarifies badly — * rows end up mixing one large tile with several small ones, which is exactly * the case the row test cannot rescue. Pass `sort={false}` where the given * order carries meaning and the shapes may suffer for it. * * ## Too many parts * * A treemap of two hundred rows on a phone is a texture, not a chart. `maxTiles` * keeps the largest few and gathers the rest into one tile, which is the honest * summary of a long tail — the reader can see how much of the total it is worth * instead of squinting at forty slivers that were never legible. * * ## Colour * * One hue, stepping down the ranking, rather than a colour per tile. The tiles * are parts of one total and the area already says which is bigger, so a set of * unrelated hues would be claiming a distinction that is not in the data — and * a treemap has more parts than there are chart tokens, so they would repeat * and two unrelated tiles would come out matching. A tile can still be given * its own `color` where it means something, and that one is drawn at full * strength against the ramp. */ import { type ReactNode } from 'react'; import { type ViewProps } from 'react-native'; /** Whether the chart is showing data or waiting for it. */ export type TreemapChartStatus = 'loading' | 'ready'; /** One part of the total. */ export interface TreemapDatum { /** Name of the part, for the label, the readout and the legend. */ label: string; /** Its size. Negatives are treated as zero — an area cannot be less than none. */ value: number; /** Explicit colour, drawn at full strength instead of the ramp. */ color?: string; } /** A tile's box inside the chart, in points. */ export interface TreemapRect { x: number; y: number; width: number; height: number; } /** One laid-out tile: its datum, its share of the total, and where it sits. */ export interface TreemapTile extends TreemapRect { label: string; value: number; /** Its share of the whole chart, `0` to `1`. */ share: number; color: string; /** How far along the ramp it is drawn, `0` to `1`. `1` where a colour was given. */ strength: number; /** * Its row in `data`, or `-1` for the gathered tile `maxTiles` makes, which * stands for several rows and so belongs to none of them. */ sourceIndex: number; } /** * Squarified treemap layout: values in, rectangles out, in the same order. * * Written out rather than taken from a layout dependency, because it is sixty * lines and the alternative is shipping a tree library to call one function of. * * The shape of it: take the remaining box, and start a row along whichever of * its sides is shorter. Add tiles to that row one at a time, and after each, * ask what the worst aspect ratio in the row now is. While that number keeps * falling the row is getting better and the tile is kept; the first tile that * makes it rise is put back, the row is closed and laid out, and the box * shrinks by the strip the row took. * * Rows go along the *shorter* side because a row is divided along its length * and is a fixed depth: dividing the long side gives thin tiles, and the whole * point of the exercise is not to have any. * * @param values Tile sizes. Must be non-negative, and should be descending. * @param ratio The aspect ratio the row test aims at. */ export declare function squarifyLayout(values: number[], width: number, height: number, ratio?: number): TreemapRect[]; /** * The selected tile, for something rendered *inside* the chart. A readout in * the card's header is outside this provider — use `onActiveIndexChange` there. */ export declare function useTreemapChart(): { /** Index into the tiles **as laid out**, which is the sorted order. */ activeIndex: number; activeTile: TreemapTile | null; tiles: TreemapTile[]; total: number; }; export interface TreemapChartProps extends ViewProps { className?: string; /** The parts of the total, in any order. Sorted by the chart unless told not to. */ data: TreemapDatum[]; /** Width ÷ height of the box the tiles fill. */ aspectRatio?: number; /** Space between one tile and the next, in points. */ gap?: number; /** Corner radius of a tile, in points. */ cornerRadius?: number; /** * Sort the tiles largest first. * * On by default, and worth leaving on. The row test assumes a descending run * — given a large tile next to a small one it has no good row to make, and * the chart comes out as slivers. Turn it off only where the given order is * itself the message. */ sort?: boolean; /** * Keep the largest `maxTiles` and gather the rest into one. * * A phone-width treemap runs out of legible tiles somewhere around twenty. * Past that the tail is texture, and one tile that says how much the tail is * worth is more use than forty that cannot be read or hit. */ maxTiles?: number; /** What the gathered tile is called. */ otherLabel?: string; /** The ramp's hue. Defaults to the first chart token. */ color?: string; /** * Smallest side, in points, a tile needs before `Labels` writes on it. * * A name clipped to two letters is not a shorter name, it is a different * word. Tiles under this are left blank and read through the readout. */ minLabelSize?: number; /** Milliseconds for one tile to grow. */ animationDuration?: number; /** Milliseconds between one tile starting and the next. `0` for all at once. */ staggerDelay?: number; /** `loading` draws the box undivided until the data arrives. */ status?: TreemapChartStatus; /** Selected tile, indexed as laid out. Leave unset to let the chart track it. */ activeIndex?: number; /** Fires with the selected tile, 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 TreemapChartHandle { replay: () => void; } export interface TreemapChartTilesProps { /** Opacity of the tiles that are not selected, once one is. */ dimOpacity?: number; } /** * Every tile, drawn in the order they were laid out. * * One part rather than one per datum: a tile's box is decided by every tile * before it in the row, so they cannot be configured apart without the layout * coming apart with them. */ declare function TreemapChartTiles({ dimOpacity }: TreemapChartTilesProps): import("react").JSX.Element | null; declare namespace TreemapChartTiles { var displayName: string; var slot: "svg"; } export interface TreemapChartSkeletonProps { /** Milliseconds for one pass of the sweep. */ duration?: number; color?: string; } /** * The loading state: the box as one plain rectangle, with a highlight * travelling across it. * * Undivided on purpose. Placeholder tiles would be a made-up split, 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 TreemapChartSkeleton({ duration, color }: TreemapChartSkeletonProps): import("react").JSX.Element | null; declare namespace TreemapChartSkeleton { var displayName: string; var slot: "svg"; } export interface TreemapChartLabelsProps { /** Show each tile's value under its name. */ showValue?: boolean; /** Show each tile's share of the total under its name. */ showShare?: boolean; /** Format the value. Defaults to a compact number. */ formatValue?: (value: number, tile: TreemapTile) => string; className?: string; } /** * The name and reading on each tile that has room for them. * * Real text over the SVG rather than SVG text, so the labels follow the theme's * font and the platform's text scaling — SVG text does neither. * * A tile smaller than `minLabelSize` on either side is left blank. The * alternative is a name clipped to its first two letters, which is not a * shorter name but a different word, and a chart of those is a chart nobody can * read. Those tiles are read through `Tooltip` instead. * * Each label takes its colour from the tile under it rather than from the * theme. A tile is the chart's own hue, and a theme is free to set that hue * anywhere on the scale — a fixed white label vanishes on a pale one, and the * foreground token would be the wrong colour on half the tiles in either mode. */ declare function TreemapChartLabels({ showValue, showShare, formatValue, className, }: TreemapChartLabelsProps): import("react").JSX.Element | null; declare namespace TreemapChartLabels { var displayName: string; var slot: "overlay"; } export interface TreemapChartTooltipProps { /** Format the value. Defaults to a compact number. */ formatValue?: (value: number, tile: TreemapTile) => string; className?: string; } /** * The readout for the selected tile, floating over the box. * * This is how the small tiles are read. They are the ones with no room for a * label, so without it a treemap answers questions about its largest parts only * — which is the half the reader could already see. */ declare function TreemapChartTooltip({ formatValue, className }: TreemapChartTooltipProps): import("react").JSX.Element | null; declare namespace TreemapChartTooltip { var displayName: string; var slot: "overlay"; } export interface TreemapChartLegendProps extends ViewProps { className?: string; /** How many tiles to name before stopping. The rest are left to the chart. */ limit?: number; /** Show each tile's share beside its name. */ showShare?: boolean; } /** * A swatch and a name per tile, under the box. Pressable in the same way the * tiles are. * * Inline and wrapping, because the tiles are already in size order and the * legend is a lookup rather than a ranking — it is read by searching for a * name, not from the top down. */ declare function TreemapChartLegend({ className, limit, showShare, ...props }: TreemapChartLegendProps): import("react").JSX.Element | null; declare namespace TreemapChartLegend { var displayName: string; var slot: "footer"; } export interface TreemapChartHeaderProps extends ViewProps { className?: string; /** Small line above the value — what the total 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 box: what the total is of and what it reads. * * The value is not derived even though the chart knows the total, because the * formatting is not the chart's to guess: 48200 is a count, a currency or a * rate depending on what was counted. */ declare function TreemapChartHeader({ className, title, value, caption, children, ...props }: TreemapChartHeaderProps): import("react").JSX.Element; declare namespace TreemapChartHeader { var displayName: string; var slot: "header"; } export declare const TreemapChart: import("react").ForwardRefExoticComponent> & { Header: typeof TreemapChartHeader; Tiles: typeof TreemapChartTiles; Labels: typeof TreemapChartLabels; Tooltip: typeof TreemapChartTooltip; Legend: typeof TreemapChartLegend; Skeleton: typeof TreemapChartSkeleton; }; export {}; //# sourceMappingURL=index.d.ts.map