/**
* Canonical axis configuration. Every axis (x, y, z) must be specified as an
* object of this shape. The `label` is optional and falls back to built-in
* defaults ('X', 'Y', 'Level') when omitted.
*
* Grid navigation properties (`min`, `max`, `tickStep`) are currently consumed
* by scatter-plot traces only; they are silently ignored by other trace types.
*
* Formatting configuration lives inline as `format` on each axis, allowing
* different formatters per axis without a separate top-level block.
*
* @example
* // Simple label
* axes: { x: { label: "Date" }, y: { label: "Price" } }
*
* @example
* // With grid navigation (scatter)
* axes: {
* x: { label: "Sepal Length", min: 4.3, max: 7.9, tickStep: 0.7 },
* y: { label: "Sepal Width", min: 2, max: 4.4, tickStep: 0.5 }
* }
*
* @example
* // With formatting
* axes: {
* x: { label: "Date" },
* y: { label: "Price", format: { type: "currency", decimals: 2 } }
* }
*/
declare interface AxisConfig {
/** Axis label displayed in text descriptions. Defaults applied when absent. */
label?: string;
/** Minimum value for grid navigation (scatter only). */
min?: number;
/** Maximum value for grid navigation (scatter only). */
max?: number;
/** Step size for grid navigation (scatter only). */
tickStep?: number;
/** Optional per-axis value formatting applied in text descriptions. */
format?: AxisFormat;
}
/**
* Configuration for formatting values on an axis.
*
* Two ways to specify formatting:
* 1. `function` - Function body string (for custom logic)
* 2. `type` - Format type specifier (for common patterns)
*
* @example
* // Using function string
* { "function": "return `$${Number(value).toFixed(2)}`" }
*
* @example
* // Using type specifier
* { "type": "currency", "decimals": 2 }
*/
declare interface AxisFormat {
/**
* Function body string for custom formatting.
* The function receives `value` as parameter and must return a string.
*
* @example
* // Currency formatting
* { "function": "return `$${Number(value).toFixed(2)}`" }
*
* @example
* // Date formatting
* { "function": "return new Date(value).toLocaleDateString('en-US')" }
*/
function?: string;
/**
* Format type specifier for common formatting patterns.
* Use with `decimals`, `currency`, `locale`, `dateOptions` for customization.
*
* @example
* { "type": "currency", "currency": "USD", "decimals": 2 }
* { "type": "percent", "decimals": 1 }
* { "type": "date", "dateOptions": { "month": "short", "day": "numeric" } }
*/
type?: FormatType;
/**
* Number of decimal places for numeric formatters.
* Used with: currency, percent, fixed, number, scientific
* @default varies by type
*/
decimals?: number;
/**
* ISO 4217 currency code for currency formatter.
* @default 'USD'
*/
currency?: string;
/**
* BCP 47 locale string for locale-aware formatters.
* Used with: currency, number, date
* @default 'en-US'
*/
locale?: string;
/**
* Options for Intl.DateTimeFormat when using date type.
*
* @example
* { "month": "short", "day": "numeric" } // "Jan 15"
* { "year": "numeric", "month": "long" } // "January 2024"
*/
dateOptions?: Intl.DateTimeFormatOptions;
}
/**
* Data point for bar charts with x and y coordinates.
*/
declare interface BarPoint {
x: string | number;
y: number | string;
}
/**
* One boxen (letter-value) plot: a median, a ladder of quantile pairs around
* it, and whatever fell outside the deepest rung.
*
* A box plot's five-number summary is this shape with exactly one rung, and
* that fixed depth is the reason it cannot express a boxen: the point of a
* letter-value plot is that a large sample gets *more* rungs, so the tails
* stay legible instead of collapsing into a whisker and a scatter of dots.
*/
declare interface BoxenPoint {
/** The category this boxen summarises. */
z: string;
/** The middle of the distribution. */
median: number;
/**
* The rungs, which the trace sorts outward from the median rather than
* trusting the order they arrive in -- a producer emitting them
* inward-first would otherwise be navigated backwards.
*/
levels: LetterValueLevel[];
/** Values beyond the deepest rung, below it and above it. */
lowerOutliers?: number[];
upperOutliers?: number[];
}
/**
* Data point for boxplots containing quartiles, min/max, and outliers.
*/
declare interface BoxPoint {
z: string;
lowerOutliers: number[];
min: number;
q1: number;
q2: number;
q3: number;
max: number;
upperOutliers: number[];
/** Mean value for violin plots when mean display is enabled. */
mean?: number;
}
/**
* DOM selectors for boxplot visual elements.
*/
declare interface BoxSelector {
lowerOutliers: string[];
min: string;
iq: string;
q2: string;
max: string;
upperOutliers: string[];
/** CSS selector for mean marker element in violin plots. */
mean?: string;
/** Optional direct CSS selector for Q1 element (bypasses iq edge derivation). */
q1?: string;
/** Optional direct CSS selector for Q3 element (bypasses iq edge derivation). */
q3?: string;
}
/**
* Data point for candlestick charts with OHLC values, volume, and trend information.
*/
declare interface CandlestickPoint {
value: string;
/**
* The period's opening price, where the chart records one.
*
* Optional because a real and common price chart does not have it.
* Highcharts registers three price series and only two carry an open:
* `candlestick` and `ohlc` do, and `hlc` draws the same high, low and
* close without it. Required, the field forced that chart to be declined
* outright -- announcing it as an error bar would have been exact in the
* data and wrong in the name, which is the trade #1140 rules out.
*
* The same shape `ErrorBarPoint.y` took in #1047 for the band that draws
* only bounds: absent means the chart never had one, not that it is zero.
*
* Its absence removes more than a row. The **body** is what an open makes
* -- so a candle without one has no bullish/bearish/neutral trend, no
* shape, and no pattern with its neighbours, because every one of those is
* a statement about the body. {@link Candlestick} drops the section, the
* trend and all of the pattern asides together rather than announcing any
* of them empty.
*/
open?: number;
high: number;
low: number;
close: number;
/** Optional volume data. May be undefined when source (e.g., Google Charts) doesn't provide it. */
volume?: number;
/**
* Which way the body ran, absent on a candle with no {@link
* CandlestickPoint.open} to measure it against.
*/
trend?: CandlestickTrend;
volatility: number;
}
/**
* DOM selectors for candlestick chart visual elements.
*/
declare interface CandlestickSelector {
body: string | string[];
wickHigh?: string | string[];
wickLow?: string | string[];
wick?: string | string[];
open?: string | string[];
close?: string | string[];
}
/**
* Represents the trend direction for candlestick data points.
* Used across the application for audio palette selection and data representation.
*/
declare type CandlestickTrend = 'Bull' | 'Bear' | 'Neutral';
/**
* One region of a choropleth map.
*
* The centroid is a **longitude and a latitude in degrees**, never a projected
* coordinate. Every producer has the pair -- `d3.geoCentroid` returns exactly
* it -- and asking for degrees removes the one thing MAIDR could not otherwise
* resolve: whether a rising `y` means north or south. Without them the map is
* read as a region list in declared order, which is a poorer reading but the
* one the data supports.
*
* @example
* { x: 'Nevada', y: 42.1, lon: -116.6, lat: 39.3, neighbors: ['Utah', 'Idaho'] }
*/
declare interface ChoroplethPoint {
/** The region's name. */
x: string | number;
/** The value the region is shaded by. */
y: number;
/** Centroid longitude, degrees east. */
lon?: number;
/** Centroid latitude, degrees north. */
lat?: number;
/**
* The regions this one shares a border with, by name.
*
* Declared because it cannot be recovered: adjacency is not derivable from
* rendered SVG paths, and not from centroids either -- two regions can have
* near centroids and no shared border, and a long region can border one
* whose centroid is far away. A layer that declares none keeps the spatial
* walk and is told nothing about borders, rather than something guessed.
*/
neighbors?: (string | number)[];
}
/**
* One point on one iso-value curve of a contour plot.
*
* A contour draws a scalar field as curves of constant value, so a layer is
* one curve per level -- structurally the multi-line layer {@link LineTrace}
* already navigates. What makes it a type of its own is that the **level is a
* first-class object rather than a colour**: the questions a reader brings are
* how many levels there are, where the 0.05 contour runs, and how far apart
* the curves are here.
*/
declare interface ContourPoint extends LinePoint {
/**
* The value of the field along this curve.
*
* Constant down a curve and carried on every point of it, the way `z` is:
* the grammar's unit is the point, and a producer emitting a flat list has
* nowhere else to put it.
*/
level?: number;
}
/**
* Converts a rendered ECharts instance into a MAIDR figure.
*
* Read from the chart's **model** rather than from its geometry:
* `getModel()` resolves every default the author did not write, so the
* values, the series names, the axis names and the stacking are all
* available without measuring a pixel. The drawing is consulted only to
* locate marks -- see `selectors.ts` for why that is the harder half.
*
* @param chart - The instance returned by `echarts.init`
* @param container - The element it was rendered into
* @param options - Overrides for the figure's id and title
* @returns The MAIDR figure
* @throws When no series the adapter reads is present
*/
export declare function createMaidrFromEChart(chart: EChartsInstance, container: HTMLElement, options?: EChartsAdapterOptions): Maidr;
/**
* A dumbbell chart: its rows, and what its two ends are called.
*
* An object rather than a bare array -- as {@link HeatmapData} and
* {@link GaugePoint} already are -- because the names of the two ends belong
* to the chart and not to any one row. Repeating them on every point would
* let a producer emit rows that disagree about what the chart is comparing.
*
* Those names are the content of the comparison. Announced as "start" and
* "end", a chart of life expectancy in 1990 against 2020 tells the reader
* which dot they are on and not which year it is, which is the one thing the
* legend gives a sighted reader for free.
*/
declare interface DumbbellData {
/** The rows, in the order the chart draws them. */
points: DumbbellPoint[];
/** What the starting end is called -- "1990", "before", "control". */
startLabel?: string;
/** What the finishing end is called -- "2020", "after", "treatment". */
endLabel?: string;
}
/**
* One row of a dumbbell chart: a category and the pair of values compared at
* it.
*
* The pair is what the chart is for -- before and after, two groups, two
* years -- and the segment drawn between the dots is the comparison. Which of
* the two is larger is not fixed: a dumbbell showing a decline draws `end`
* below `start`, and a chart usually contains both directions at once.
*
* The change between them is deliberately absent, and derived instead. A
* drawn segment cannot disagree with the dots it joins, so an authored delta
* is a second source of truth for a quantity that already has one -- and the
* one a reader would be told is the one the chart did not draw.
*/
declare interface DumbbellPoint {
/** Position along the category axis. */
x: number | string;
/** The value the segment starts at -- the earlier, or the reference, one. */
start: number;
/** The value the segment ends at. */
end: number;
}
/**
* Options accepted by {@link createMaidrFromEChart}.
*/
export declare interface EChartsAdapterOptions {
/** The figure's id. Defaults to the container's, then to a generated one. */
id?: string;
/** The figure's title. Defaults to the chart's own `title.text`. */
title?: string;
}
/**
* One axis, legend or title of a rendered chart.
*/
declare interface EChartsComponentModel {
/** Read one of the component's resolved options. */
get: (key: string) => unknown;
}
/**
* One column of a series' internal data list.
*
* Measured: a cartesian series carries `['x', 'y']`, and a scatter whose
* `symbolSize` reads a third column carries `['x', 'y', 'value']`.
*/
declare type EChartsDimension = string;
/** A graph series' nodes and links. */
declare interface EChartsGraph {
nodes: EChartsGraphNode[];
edges: EChartsGraphEdge[];
}
/** One link of a graph series. */
declare interface EChartsGraphEdge {
/** The node the link leaves. */
node1: EChartsGraphNode;
/** The node it arrives at. */
node2: EChartsGraphNode;
/** One of the link's own dimensions -- `'value'` is the flow. */
getValue: (dimension: string) => number | null | undefined;
}
/**
* One node of a graph series.
*
* There is no `name`: reading one gives `undefined`. A node is named through
* `EChartsList.getName(node.dataIndex)`, or equivalently by its `id`.
*/
declare interface EChartsGraphNode {
/** Its index into the series' data list, which is how it is named. */
dataIndex: number;
}
/**
* A rendered ECharts instance.
*
* Only `getModel` is read. `getZr()` and `dispatchAction()` are deliberately
* not declared: the first exposes no element-to-DOM-node mapping in the
* production build, and the second is ECharts' own highlighting, which
* `MaidrLayer.selectors` has no way to call (#1195).
*/
export declare interface EChartsInstance {
getModel: () => EChartsModel;
}
/**
* A series' data list.
*
* Addressed by index rather than iterated, which is what lets a reading walk
* it in the order the chart drew it.
*/
export declare interface EChartsList {
/** The column names, in order. */
dimensions: EChartsDimension[];
/** How many data points the series holds. */
count: () => number;
/**
* The category name at `index`.
*
* Measured: the category axis' label on a category axis, and the empty
* string on a value axis -- so an empty name means "this axis carries
* numbers", not "this point is unnamed".
*/
getName: (index: number) => string;
/**
* The value of one dimension at `index`.
*
* On a category axis this is the category's **index**, not its name; the
* name comes from {@link getName}.
*/
get: (dimension: EChartsDimension, index: number) => number | null | undefined;
/** The hierarchy, on a `treemap`, `sunburst` or `tree` series. */
tree?: EChartsTree;
/** The graph, on a `sankey` or `graph` series. */
graph?: EChartsGraph;
}
/**
* A rendered chart's resolved model.
*/
declare interface EChartsModel {
/** Visit every series, in declaration order. */
eachSeries: (callback: (series: EChartsSeriesModel, index: number) => void) => void;
/** Visit every component of one main type, in declaration order. */
eachComponent: (query: {
mainType: string;
}, callback: (component: EChartsComponentModel, index: number) => void) => void;
}
/**
* One series of a rendered chart.
*/
export declare interface EChartsSeriesModel {
/** The declared series type -- `'bar'`, `'line'`, `'scatter'`, … */
subType: string;
/**
* The series' resolved name.
*
* ECharts invents one (`'series 0'`, with a NUL separator) when the author
* supplied none, so a name is only worth emitting when the author wrote it;
* see `authoredName` in the converters.
*/
name: string;
/** The series' data list. */
getData: () => EChartsList;
/** Read one of the series' resolved options. */
get: (key: string) => unknown;
}
/**
* Minimal type declarations for the Apache ECharts API.
*
* These cover only the subset the MAIDR ECharts adapter reads. ECharts is
* loaded as a peer dependency or a CDN script and is not installed in this
* repo, so nothing here is imported from the package -- the shapes were
* measured against **echarts 6.1.0** driven in Chromium rather than copied
* from its published types, and the fields declared are exactly the ones a
* reading was proven to need.
*
* @see https://echarts.apache.org/
*/
/**
* The series types this adapter reads.
*
* Three are the cartesian ones (tier 1 of
* xability/maidr#1195), three carry one magnitude per named thing (tier 2a),
* two hand over a set of magnitudes already computed (tier 2b), five carry a
* hierarchy or a graph (tier 3), `pictorialBar` is a bar wearing a symbol,
* `themeRiver`, `parallel` and `radar` own the chart but are read as a set of
* series, and `boxplot` hands over a five-number summary. Two of them are
* read without an outline, which `grid.ts` records. A type outside this
* union is refused by name, so gaining a reading later is a decision rather
* than an accident.
*
* Kept in step with `READ` in `converters.ts`, which is what actually
* decides -- this type is the public statement of it, and
* `test/adapters/echarts/cartesian.test.ts` fails if the two stop agreeing.
*/
export declare type EChartsSeriesType = 'bar' | 'line' | 'scatter' | 'pie' | 'funnel' | 'gauge' | 'heatmap' | 'candlestick' | 'treemap' | 'sunburst' | 'tree' | 'sankey' | 'graph' | 'pictorialBar' | 'themeRiver' | 'parallel' | 'radar' | 'boxplot';
/**
* A hierarchy series' tree.
*
* `root` is **synthetic** -- ECharts adds it above whatever the author wrote,
* names it `''`, and gives it the sum of everything below. The real forest is
* `root.children`.
*/
declare interface EChartsTree {
root: EChartsTreeNode;
}
/**
* One node of a hierarchy series' tree.
*
* Measured: `getValue()` answers the rolled-up sum for an interior node, so
* a declared value and a derived one are indistinguishable from the value
* alone -- see `hierarchy.ts` for what is done about that.
*/
declare interface EChartsTreeNode {
/** The node's name. The synthetic root's is the empty string. */
name: string;
/** Its children, absent on a leaf. */
children?: EChartsTreeNode[];
/** Its magnitude, rolled up from the children where it has them. */
getValue: () => number | null | undefined;
}
/**
* One estimate with the interval drawn around it.
*
* The interval is the reason this is a point shape of its own rather than a
* scatter point: a chart drawn this way carries two magnitudes at every
* sample — the estimate, and how far from it the data is consistent with —
* and a reading that names only the first drops the part most statistical
* graphics are drawn to show.
*
* `lower` and `upper` are absolute positions on the value axis, not offsets
* from `y`. Producers disagree about which they hand out (matplotlib's
* `yerr` is an offset, Vega-Lite's `errorbar` computes bounds), so the
* schema fixes one and each adapter converts to it.
*
* The bounds are optional and independently so: a one-sided interval — an
* upper bound with no lower, say — is a real chart, and dropping the point
* for want of its other half would lose the estimate too.
*
* The *estimate* is optional for the mirror-image reason. A band with two
* bounds and nothing between them is a real chart too — Highcharts draws it
* as `arearange`, and the same shape arrives from `Plot.areaY` with
* `y1`/`y2`, from `geom_ribbon`, and from `fill_between` without a centre
* line. There is no honest number to put here for one: the midpoint is a
* value the chart never draws, and either bound announced as the estimate
* loses the other and implies a point reading the chart does not make
* (#1047).
*/
declare interface ErrorBarPoint {
/** Position along the main axis. */
x: number | string;
/**
* The estimate itself: a mean, a median, a fitted value.
*
* Absent on a band that draws only bounds. {@link ForestPoint} re-declares
* it required, because a forest plot's whole reading is whether the
* interval crosses the null *relative to the estimate* — so the shape that
* needs it says so, rather than every reader of this one assuming it.
*/
y?: number;
/** Absolute lower bound of the interval, when the chart draws one. */
yMin?: number;
/** Absolute upper bound of the interval, when the chart draws one. */
yMax?: number;
/**
* Name of the group this estimate belongs to, for a chart drawing an
* interval per group at each category.
*
* Named to match {@link LinePoint.z} and carried for the same reason. A
* dodged error bar over two treatments puts two estimates at every
* category, and without this they arrive as two readings of one name with
* nothing telling them apart. The grouping cannot be recovered from
* emission order, because nothing states that order — so the comparison the
* chart exists to support, whether one group's interval overlaps another's,
* is the thing that goes missing (#942).
*
* Meaningful on the grouped shape, `ErrorBarPoint[][]`, where every point
* in a series carries the same value. A single-series chart has one group
* and needs no name for it.
*
* @example
* { x: 'a', y: 2, yMin: 1.5, yMax: 2.9, z: 'control' }
*/
z?: string;
}
/**
* One weighted flow of a sankey, alluvial or chord diagram.
*
* A flow names both of its ends, so the **nodes are derived from the edges**
* and a separate node list would be a second source of truth for something the
* data already says -- the treemap's reasoning about paths, applied to a graph.
* Their order is first appearance, which is the order the producer drew them.
*
* @example
* { source: 'Coal', target: 'Electricity', value: 34 }
*/
declare interface FlowPoint {
/** The node the flow leaves. */
source: string | number;
/** The node it arrives at. */
target: string | number;
/** How much flows. */
value: number;
}
/**
* Display configuration for a forest plot layer.
*/
declare interface ForestOptions {
/**
* The value that means "no effect" -- 1 for a ratio measure, 0 for a
* difference.
*
* Whether an interval crosses it *is the result for that study*, so the
* trace announces the crossing. There is deliberately **no default**: a
* ratio chart guessed at 0 would report every study as not crossing, since
* odds ratios are all positive, and that is a confident wrong answer given
* to every row. A layer that does not declare it gets the estimate, the
* interval and the weight, and no claim about significance.
*/
nullValue?: number;
}
/**
* One row of a forest plot: a study's effect estimate with its interval.
*
* A meta-analysis draws one of these per study against a shared null line,
* with a pooled summary at the foot. It is an {@link ErrorBarPoint} laid out
* on a categorical row axis, plus the two things that make the figure a
* forest plot rather than a row of intervals.
*/
declare interface ForestPoint extends ErrorBarPoint {
/**
* The study's effect estimate.
*
* Required here where {@link ErrorBarPoint.y} is optional. A forest plot
* is read by whether each interval crosses the null, and that question is
* only answerable *relative to the estimate* — a row without one is not a
* study with a missing number, it is not a forest plot row at all (#1047).
*/
y: number;
/**
* The study's weight in the pooled estimate, as a fraction of one.
*
* A forest plot encodes this as marker *area*, which is a magnitude a
* reader is otherwise never told: two studies whose intervals look alike
* can contribute wholly differently to the result.
*/
weight?: number;
/**
* Marks the pooled summary rather than a study.
*
* It is a different kind of row -- it is not evidence, it is what the
* evidence came to -- and announcing it as one more study invites a reader
* to count it among them.
*/
pooled?: boolean;
}
/**
* Supported format type specifiers for JSON/HTML API.
*/
declare type FormatType = 'currency' | 'percent' | 'fixed' | 'number' | 'date' | 'scientific';
/**
* A gantt chart: its lanes, and how its axis reads.
*
* An object rather than a bare array, for the reason {@link DumbbellData} is
* one: a unit belongs to the chart and not to any row, and repeating it per
* point would let a producer emit rows that disagree about what their numbers
* measure.
*/
declare interface GanttData {
/**
* The lanes, in the order the chart draws them, each holding the intervals
* of one lane.
*
* Nested rather than flat so a lane with no intervals still exists: an empty
* row is a real statement about a schedule -- nothing is booked -- and a
* flat list grouped by `x` cannot say it.
*/
points: GanttPoint[][];
/**
* What each lane is called, in the order {@link GanttData.points} holds
* them.
*
* A populated lane names itself: every interval carries its lane in `x`. An
* **empty** lane holds no interval and so has nowhere to carry one, which
* makes it the only row a reader can navigate onto and be told nothing
* about -- and an empty lane is exactly the row this shape is nested to be
* able to express. This is where its name goes.
*
* Optional, and optional per entry: a chart with no empty lanes need not
* supply it, and the trace prefers a lane's own intervals over this when
* both are present, so a producer cannot make the two disagree about a
* populated lane.
*/
lanes?: (string | number)[];
/**
* What a unit of the axis is called: "days", "hours", "weeks".
*
* The length of an interval is the fact a gantt exists to carry, and a bare
* number does not carry it. Omitted, the trace announces the length without
* a unit rather than guessing one.
*/
unit?: string;
}
/**
* One interval of a gantt chart, timeline or swimlane diagram.
*
* The two coordinates are both positions on the same axis rather than a
* position and a magnitude, which is what makes this a shape of its own. A bar
* has one number and a baseline; an interval has two numbers and no baseline,
* and its length is a difference the reader has to be told rather than a
* height they can hear.
*/
declare interface GanttPoint {
/** Which lane the interval belongs to -- a task, a resource, a phase. */
x: number | string;
/** Where the interval begins. */
start: number;
/** Where the interval ends. */
end: number;
/**
* What this interval is called, when the lane is not already its name.
*
* A lane commonly holds several intervals -- a resource booked twice, a
* phase that pauses and resumes -- and without this they are announced by
* position alone. Omit it when the lane names the work.
*/
label?: string;
}
/**
* One qualitative band of a bullet chart, named and bounded above.
*
* Bands partition the range, so only the upper edge is carried: a band starts
* where the previous one ended, and the first starts at the gauge's `min`.
* Carrying both edges would let a chart declare overlapping or gapped bands
* that the drawing cannot express.
*/
declare interface GaugeBand {
/** Upper edge of the band, inclusive. */
to: number;
/** What the band is called -- "poor", "ok", "good". */
label: string;
}
/**
* A gauge or bullet chart: one measure against a range.
*
* Unlike every other trace's data this is a single object rather than an
* array, because the chart draws exactly one measure -- the same reason
* {@link HeatmapData} is an object. An array of one would describe a shape the
* chart does not have.
*
* The value alone is not the reading. "73" means nothing without the range it
* sits in, the target it was aiming at, and the band it lands in, and none of
* those are written anywhere a screen reader can reach on a drawn gauge.
*/
declare interface GaugePoint {
/** The measure. */
value: number;
/** Lower end of the dial. */
min: number;
/** Upper end of the dial. */
max: number;
/** What the measure is called, when the chart names it. */
label?: string;
/** The target marker a bullet chart draws, when it has one. */
target?: number;
/** Qualitative bands, in ascending order. */
bands?: GaugeBand[];
}
/**
* Data structure for heatmap charts with x/y labels and 2D point values.
*
* **Rows run top-first**: `y[0]` names the row drawn at the *top* of the
* chart and `points[0]` holds it, so the two arrays read the way a sighted
* reader reads the grid. {@link Heatmap} turns both over on construction, so
* that its own row 0 is the bottom of the drawn grid and ↑, which
* increments the row index, moves visually upward.
*
* Stated here because it cannot be recovered from the payload: a matrix of
* numbers looks the same either way up, so a layer written bottom-first is
* not wrong in any way the core could notice. It loads, it navigates, and
* every value is still announced against its own label -- both arrays having
* been reversed together -- while ↑ walks *down* the chart and the
* cursor enters at the top instead of the bottom. A reader who then reports
* what the top row contains has it exactly backwards (#971).
*
* Producers therefore have to know which way their own library counts.
* matplotlib's array is top-first and needs nothing; plotly numbers a
* heatmap's rows from the bottom and its adapter turns them over.
*/
declare interface HeatmapData {
/** Column labels, left to right. */
x: string[];
/** Row labels, **top row first**. */
y: string[];
/**
* `points[row][col]`, rows **top-first**, aligned with `y` and `x`.
*
* A cell the chart drew no value at is `null`, or any non-finite number --
* the same spelling {@link BarPoint} uses for a gap, and read through the
* same `toBarValue`. It is not `0`: a grid is a rectangle, and an
* adapter whose data does not fill it had no way to say so, so three of
* them filled the holes with zeros and announced a value the chart never
* drew (#1191). Measured on Highcharts 13.0.1, a 3x2 heatmap omitting one
* cell and the same heatmap stating that cell as `0` produced byte-identical
* payloads, while the first drew five cells and the second six.
*
* A calendar heat map is the case that makes it unavoidable rather than
* merely wrong: Google draws every day of every year its data spans, so a
* two-year chart of ten records is 731 cells with 721 holes.
*/
points: (number | null)[][];
}
/**
* One hexagonal bin: where its centre is, and how many points fell in it.
*
* The centre is carried per bin rather than derived from a lattice origin and
* a cell size, because a hex lattice staggers alternate rows by half a cell --
* so a bin's index does not give its position, and a consumer reconstructing
* one would have to know which rows a particular library chose to offset.
*/
declare interface HexbinPoint {
/** The bin's centre along the x axis. */
x: number | string;
/** The bin's centre along the y axis. */
y: number | string;
/** How many points fell in it. */
count: number;
}
/**
* Data point for histograms extending bar points with bin ranges.
*/
declare interface HistogramPoint extends BarPoint {
xMin: number;
xMax: number;
yMin: number;
yMax: number;
}
/**
* One rung of a letter-value ladder: a pair of quantiles symmetric about the
* median.
*
* `p` is the *tail* probability, which is how letter-value plots are defined
* and how the libraries that draw them report it: `p = 0.25` is the rung
* spanning the middle half, `p = 0.125` the middle three quarters, and so on
* inwards from the median. The trace converts it to percentiles for the
* announcement, because "the 12.5th percentile" is a number a reader can
* place and "p is 0.125" is one they have to convert.
*/
declare interface LetterValueLevel {
/**
* Tail probability, strictly between 0 and 0.5.
*
* The median is carried separately on `BoxenPoint` and is not a rung, so
* `0.5` is out of range rather than a way of naming it: a rung at `0.5`
* would put two positions labelled `50th percentile` either side of the one
* already called `median`. Values outside the range are dropped.
*/
p: number;
/** The lower quantile of the pair: the `p` quantile. */
lo: number;
/** The upper quantile of the pair: the `1 - p` quantile. */
hi: number;
}
/**
* Data point for line charts with optional fill color for multi-series plots.
*/
declare interface LinePoint {
x: number | string;
/**
* The magnitude at this x, or `null` where the series has a position but no
* reading.
*
* A gap is not a zero. `seaborn.pointplot` pads a hue level missing from one
* category so its estimate lines stay the same length, and a producer that
* meets a break in a series has nowhere else to put it. `Number(null)` is
* `0`, which would make the gap sound like a real low point, let it be
* reached as the row's minimum, and pull the range every other point's pitch
* is scaled against — the same trap `toBarValue` was written for (#925).
*
* `null` rather than a non-finite number because the payload has to survive
* `JSON.parse`: `json.dumps` writes `NaN` and `Infinity` as bare tokens that
* are legal JavaScript and invalid JSON, and a producer emitting one stops
* the chart initialising at all (xability/py-maidr#427).
*/
y: number | null;
z?: string;
/**
* Ordinal level name announced in place of the raw numeric `y`, for a chart
* whose y axis is a category rather than a magnitude — a hypnogram's sleep
* stages, a Likert response, a severity grade. `y` stays numeric because it
* drives sonification, braille and the min/max range, so the human-readable
* name has to travel alongside it.
*
* An empty string counts as absent, so a producer that emits `''` for an
* unnamed level gets the numeric announcement rather than a blank one.
* Omitting it entirely is the right shape for a continuous y.
*
* @example
* { x: 1.5, y: 3, label: 'REM' }
*/
label?: string;
/**
* Lower bound of the uncertainty around `y`, when the chart draws one.
*
* A fitted curve almost always comes with a band, and it is the reason the
* curve is drawn rather than a plain line: `geom_smooth(se = TRUE)` and
* `sns.regplot` both default to one. Carried on the sample rather than in a
* layer of its own so a reader hears the value and its interval at the same
* x — the comparison a band exists for is whether the trend is
* distinguishable from flat, and that cannot be made by navigating two
* layers in turn.
*
* Named to match {@link ErrorBarPoint}, so a producer that already computes
* an interval emits the same keys wherever it puts them.
*
* Both bounds are optional and independent: a one-sided interval is a real
* chart, and a sample missing its bounds still carries its value.
*/
yMin?: number;
/** Upper bound of the uncertainty around `y`. See {@link LinePoint.yMin}. */
yMax?: number;
}
/**
* Root MAIDR data structure containing figure metadata and subplot grid.
* This is the type for the `data` prop passed to the `` React component.
*
* @example
* ```typescript
* const data: Maidr = {
* id: 'my-chart',
* title: 'Sales by Quarter',
* subplots: [[{
* layers: [{
* id: '0',
* type: 'bar',
* axes: { x: 'Quarter', y: 'Revenue' },
* data: [{ x: 'Q1', y: 120 }, { x: 'Q2', y: 200 }],
* }],
* }]],
* };
* ```
*/
declare interface Maidr {
/** Unique identifier for the chart. Used for DOM element IDs. */
id: string;
/** Chart title displayed in text descriptions. */
title?: string;
/** Chart subtitle. */
subtitle?: string;
/** Chart caption. */
caption?: string;
/**
* Optional figure-wide axis labels shared across every subplot — e.g. a facet
* grid whose panels all sit on one common X and Y axis drawn at the figure
* margins. Only `label` is honored at the figure level, so the type is
* narrowed to `Pick` (a layer's `min` / `max` /
* `tickStep` / `format` have no figure-wide meaning and would be silently
* ignored — the narrower type surfaces that as a compile error instead).
*
* When present and authored, the figure lobby's `l x` / `l y` announce these
* as the figure-wide label; when omitted they fall back to the focused
* subplot's own axis, so existing charts are unaffected.
*
* @example
* axes: { x: { label: "Year" }, y: { label: "Revenue" } }
*/
axes?: {
x?: Pick;
y?: Pick;
};
/**
* 2D grid of subplots. Each row is an array of subplots.
* For a single chart, use `[[{ layers: [...] }]]`.
*/
subplots: MaidrSubplot[][];
/**
* Enables live/realtime mode for this chart. When true:
* - React consumers can update the `data` prop to replace the chart data in place.
* - Script-tag consumers can push updates via `window.maidrLive.setData()` /
* `window.maidrLive.appendData()`.
* - The 'M' key toggles monitor mode, which auto-sonifies and announces
* newly appended data points.
*
* Static charts (the default) are unaffected.
*/
live?: boolean;
/**
* Sliding window size for streaming data. When set, appending a data point
* beyond this width drops the oldest point(s), keeping at most `maxWidth`
* points per series. Only applies to `appendData` updates.
*/
maxWidth?: number;
/**
* Optional callback invoked when the active data point changes.
* Used by canvas-based charting libraries (e.g., Chart.js) for visual highlighting,
* since canvas elements cannot be targeted with CSS selectors.
*
* This field is not serializable as JSON; it is only available when constructing
* MAIDR data programmatically (e.g., via the Chart.js plugin or React API).
*/
onNavigate?: NavigateCallback;
}
/**
* Layer/trace definition containing plot type, data, and rendering configuration.
*/
declare interface MaidrLayer {
id: string;
type: TraceType;
title?: string;
/**
* What this layer is, when a subplot's layers are the same kind of thing.
*
* Announced on a layer switch in place of the trace type. Without it, two
* layers of one type are indistinguishable — a hue-split error bar chart
* announces "Layer 1 of 2: error_bar plot" and then "Layer 2 of 2:
* error_bar plot", so a reader hears two different sets of numbers and is
* never told that the first is Male and the second Female, which is the
* whole content of the split and what a legend gives a sighted reader for
* free.
*
* Distinct from `title`, which names the *chart* rather than the layer:
* producers put the figure's title there for every layer of a figure, so it
* cannot say which layer this is.
*
* @example
* name: 'Male'
*/
name?: string;
/**
* Which element of the chart each point of the layer is drawn as.
*
* A plain string leaves the pairing to document order; an array names one
* element per point; a grid names one per cell of a segmented layer.
*
* A grid cell may be `null`, which says the chart drew **no element** for
* that cell — a category a series has no bar at, or a position a heat grid
* is not a rectangle at. That is different from a selector that fails to
* resolve, which is a mistake and declines the whole grid: without a way to
* tell the two apart, a producer whose layer has a gap has to choose between
* losing the highlight everywhere and inferring the gaps from the values,
* and a value of zero is not evidence that a bar was never drawn (#1002).
*
* It is also not the same as a `null` in {@link HeatmapData.points}, which
* says the chart drew no *value*. A calendar has both, at different cells:
* a day inside the year with no row is drawn as a white square, so it has
* an element and no value, while the slots outside the year have neither
* (#1174).
*/
selectors?: string | string[] | (string | null)[][] | BoxSelector[] | CandlestickSelector;
/**
* Which way the layer is drawn. Defaults to {@link Orientation.VERTICAL}.
*
* For one family of traces this key decides **which field of a point holds
* the magnitude**, so getting it wrong is not a cosmetic error — the trace
* reads a category name where it expects a number and sounds with no
* magnitude at all. For every other trace it changes only which axis label
* a reading is announced against, and the payload is written the same way
* whichever value is set.
*
* **What the two words name.** Orientation is the direction the *magnitude*
* runs, not which axis happens to be called `x`. That is the convention the
* whole field uses: a horizontal bar chart is one whose bars run left to
* right, with the categories down the y axis — and it is how the drawing
* libraries name the same switch. Chart.js: `indexAxis: 'y'` gives
* "horizontal bars", the y axis holding the categories and the x axis the
* values. Highcharts: `chart.inverted` makes "the x axis vertical and y axis
* horizontal". Matplotlib 3.10 replaced `boxplot(vert=False)` with
* `orientation='horizontal'`, which "plots the boxes horizontally". Plotly
* states it outright: with `'h'`, "the value of each bar spans along the
* horizontal".
*
* A producer therefore reads its chart, not its API's vocabulary. Where a
* library names the *other* direction — ECharts' and amCharts' funnel
* `orient`/`orientation` name the way the stages progress, so their default
* funnels encode the value as a band's width and are `horz` here — the
* adapter translates, and says so where it does.
*
* The rule is: **the bar family swaps `x` and `y`; nothing else does.**
*
* | trace | `vert` | `horz` |
* | --- | --- | --- |
* | the bar family, listed below | `x` is the category, `y` the magnitude | `x` is the **magnitude**, `y` the category |
* | `error_bar`, `forest` | `x` is the category, `y`/`yMin`/`yMax` the magnitudes | unchanged — only the axis labels swap |
* | `box`, `boxen`, `violin_box` | quantile fields, no axis assignment | unchanged |
* | `gantt`, `dumbbell` | — | unchanged; navigation and panning only |
*
* The bar family is defined by what a type is built on rather than by what
* it is called, because the exchange is inherited from `AbstractBarPlot`'s
* constructor: `bar`, `histogram`, `stacked`, `dodged`, `normalized` and
* the traces built on those (`diverging`, `mosaic`) — and also `dot` and
* `lollipop`, which the factory constructs as a `BarTrace` outright, and
* `funnel`, whose trace extends `BarTrace` and never undoes the exchange.
* Reading one model file at a time misses those last three, so
* `test/type/orientationContract.test.ts` runs the list rather than
* restating it.
*
* Note that this is a different question from the one
* `resolveOrientation()` in `src/util/orientation.ts` answers. Its
* `IS_ORIENTED` record says whether a type has an orientation worth
* announcing ("vertical bar plot"); a type can be oriented in that sense
* and still not want its payload swapped, which is the trap this table
* exists to close. Both r-maidr #184 and #186 were emitted against the
* wrong half of it.
*
* @example
* // a horizontal bar chart of apple = 30
* { orientation: 'horz', data: [{ x: 30, y: 'apple' }] }
*/
orientation?: Orientation;
/**
* Optional DOM mapping hints. When provided, individual traces can opt-in
* to use these hints to map DOM elements to the internal row-major data grid
* without changing default behavior when omitted.
*/
domMapping?: {
/**
* Specify DOM flattening order for grid-like traces.
* 'row' => row-major, 'column' => column-major.
*/
order?: 'row' | 'column';
/**
* For segmented/dodged bars, control the per-column group/level iteration.
* 'forward' => iterate groups top-to-bottom (as previously domOrder='forward').
* 'reverse' => iterate bottom-to-top (default).
*/
groupDirection?: 'forward' | 'reverse';
/**
* For boxplots, control the Q1/Q3 edge mapping for IQR box.
* 'forward' => Q1=bottom, Q3=top (default for vertical)
* 'reverse' => Q1=top, Q3=bottom (for Base R vertical boxplots)
*/
iqrDirection?: 'forward' | 'reverse';
/**
* For a line-family layer, whether the chart draws the series' points in
* the opposite order from the one `data` lists them in.
* `'data'` (the default) => the r-th mark drawn is `data[r]`.
* `'reverse'` => the marks run the other way, so the last one drawn is
* `data[0]`.
*
* A reversed category axis draws a series from its far end while the
* library goes on reporting its points in the order they were written, so
* a chart read in the written order is announced as its own mirror image:
* every value right, the shape backwards, and with it the stereo pan, the
* braille line and the direction autoplay sweeps (#1007).
*
* A bar layer fixes that adapter-side, by reversing the rows and naming
* each bar outright so the highlight follows (#995). A line cannot: it has
* no per-point selector to permute -- `LineTrace` reads its points out of
* one ``'s geometry, in path order, which is the *library's* data
* order whichever way the axis runs. Reversing the payload alone would
* pair `data[0]` with the vertex at the other end of the chart, trading a
* correct highlight for a wrong one (#988, #990). This is how an adapter
* says it has reversed the payload, so the trace can pair the two halves
* back up.
*
* Read by `LineTrace` (and the traces built on it) and ignored by every
* other type. Omit it unless the drawn direction is known: a layer that
* declares `'reverse'` and is not drawn that way outlines the wrong end
* of the series, which is worse than the direction being wrong on its own.
*/
pointOrder?: 'data' | 'reverse';
};
/**
* Axis configuration. Every axis (x, y, z) is specified as an {@link AxisConfig}
* object with an optional `label`, optional grid navigation properties
* (`min`, `max`, `tickStep`), and optional per-axis `format`.
*
* @example
* // Basic labels
* axes: { x: { label: "Date" }, y: { label: "Price" } }
*
* @example
* // With per-axis formatting
* axes: {
* x: { label: "Date" },
* y: { label: "Price", format: { type: "currency", decimals: 2 } }
* }
*
* @example
* // With grid navigation (scatter)
* axes: {
* x: { label: "Sepal Length", min: 4.3, max: 7.9, tickStep: 0.7 },
* y: { label: "Sepal Width", min: 2, max: 4.4, tickStep: 0.5 }
* }
*/
axes?: {
x?: AxisConfig;
y?: AxisConfig;
z?: AxisConfig;
};
/** Display configuration for a forest plot layer. */
forestOptions?: ForestOptions;
/** Threshold configuration for a volcano or Manhattan plot layer. */
thresholdOptions?: ThresholdOptions;
/**
* Optional display configuration for violin plot layers (VIOLIN_KDE and VIOLIN_BOX).
* Controls which summary statistics are shown in the violin box overlay.
*/
violinOptions?: ViolinOptions;
/**
* Where a {@link TraceType.STEP} layer jumps between samples, and how a
* stepped {@link TraceType.AREA} band moves between them. Read by
* `StepTrace` and by `AreaTrace` -- `line.shape` and a fill are independent,
* so a band can be a staircase -- and ignored by every other trace type.
* Omit it when the producing library does not report one, rather than
* guessing: the announcement names the convention, and naming the wrong one
* is worse than staying silent.
*/
stepDirection?: StepDirection;
data: BarPoint[] | FlowPoint[] | NetworkPoint[] | BoxPoint[] | BoxenPoint[] | CandlestickPoint[] | DumbbellData | ErrorBarPoint[] | ErrorBarPoint[][] | ForestPoint[] | GanttData | GaugePoint | HeatmapData | HexbinPoint[][] | HistogramPoint[] | LinePoint[][] | PiePoint[] | ScatterPoint[] | MosaicPoint[][] | VolcanoPoint[] | SegmentedPoint[][] | SmoothPoint[][] | ContourPoint[][] | StepPoint[][] | ChoroplethPoint[] | SurvivalPoint[][] | TreemapPoint[] | ViolinKdePoint[][] | WaterfallPoint[] | WordCloudPoint[];
}
/**
* Subplot data structure containing optional legend and trace layers.
* A subplot groups one or more layers (traces) that share the same coordinate space.
*
* @example
* ```typescript
* const subplot: MaidrSubplot = {
* layers: [
* { id: '0', type: 'bar', axes: { x: 'X', y: 'Y' }, data: [...] },
* { id: '1', type: 'line', axes: { x: 'X', y: 'Y' }, data: [...] },
* ],
* };
* ```
*/
declare interface MaidrSubplot {
/** Legend labels for multi-series plots. */
legend?: string[];
/** CSS selector for the subplot container element. */
selector?: string;
/** Array of trace layers in this subplot. */
layers: MaidrLayer[];
}
/**
* One cell of a mosaic (marimekko) plot.
*
* A mosaic is a stacked bar chart in which the **bar widths also encode
* data** -- typically each category's share of all observations. A reader
* given only the segment heights has half the table: the conditional
* proportions without the group sizes they were computed from, so a category
* of six people and one of six hundred read identically.
*/
declare interface MosaicPoint extends SegmentedPoint {
/**
* The category's share of all observations, as a fraction of one -- the
* width its column is drawn at.
*
* Carried on every cell of the column rather than once per column, the way
* `z` is carried on every cell of a series: the grammar's unit is the
* point, and a producer emitting a flat list has nowhere else to put it.
*/
width?: number;
/**
* The cell's own count, when the producer has the contingency table.
*
* A mosaic is drawn *from* a two-way table, and the count is the number the
* table was built on. It is optional because a producer working from
* proportions alone genuinely does not have it, and inventing one by
* multiplying out a rounded share would put a number in the announcement
* that the data does not contain.
*/
count?: number;
}
/**
* Callback invoked when the active data point changes during navigation.
* Used by canvas-based charting libraries (e.g., Chart.js) for visual highlighting.
*
* `null` means no data point is active — the cursor has left a subplot for the
* figure lobby of a multi-panel chart. A consumer drawing an overlay must clear
* it, since there is no other signal that the selection ended: without one, the
* last point's highlight stays on screen and follows the user to another panel,
* pointing at a chart it does not belong to.
*
* @param info - The current navigation position, or `null` when nothing is
* selected
* @param info.layerId - The ID of the active layer/trace
* @param info.row - The current row index (e.g., dataset index)
* @param info.col - The current column index (e.g., data point index)
* @param info.pointIndices - Present only for a point cloud (scatter, volcano,
* manhattan), whose selection is a *set of points* rather than a cell of a
* grid: the indices into that layer's `data` array — as the producer supplied
* it — of every point the highlight covers. When it is present `row` and
* `col` are both `-1`, because no row/column pair can name the selection, and
* a consumer that bounds-checks them (as it must) then clears the overlay
* rather than outlining an arbitrary mark.
*/
declare type NavigateCallback = (info: {
layerId: string;
row: number;
col: number;
pointIndices?: readonly number[];
} | null) => void;
/**
* One link of a network or node-link diagram.
*
* Undirected: a link between two nodes is a fact about the pair, not a
* direction, and the nodes are derived from the links exactly as a
* {@link FlowPoint}'s are.
*
* **There is deliberately no position here.** Where a force-directed node
* lands is a fact about the solver's seed rather than about the data, so
* announcing it would be inventing a finding -- and a field that existed
* would eventually be announced.
*
* @example
* { source: 'Ada', target: 'Grace' }
*/
declare interface NetworkPoint {
/** One end of the link. */
source: string | number;
/** The other end. */
target: string | number;
}
/**
* Which way a layer is drawn, for the many trace types that can go either way
* — the bar family, the box and violin family, error bars, funnels, Gantt
* charts and dumbbells among them.
*
* See {@link MaidrLayer.orientation} for what setting it actually changes,
* which is not the same for every type: for the bar family it selects which
* field of a point carries the magnitude, and elsewhere it only swaps which
* axis label a reading is announced against.
*/
declare enum Orientation {
VERTICAL = "vert",
HORIZONTAL = "horz"
}
/**
* Data point for one slice of a pie chart.
*
* A pie layer's `data` is a flat `PiePoint[]` — one entry per slice, in the
* order the slices are drawn — never the nested group array the bar-family
* types use.
*
* `y` is strictly numeric, unlike {@link BarPoint.y}: it is both the sonified
* magnitude and the numerator of the slice's percentage, and a percentage
* derived from a string is not a percentage.
*
* There is deliberately no `percentage` field. The share of the whole is
* derived once in the model as `y / sum(y) * 100`, so an authored percentage
* can never disagree with the values it is supposedly derived from.
*/
declare interface PiePoint {
/** Slice label, e.g. the category the slice stands for. */
x: string | number;
/** Slice magnitude. Negative values are not meaningful in a pie. */
y: number;
}
/**
* Data point for scatter plots with x and y coordinates, plus optional z for 3D.
*/
declare interface ScatterPoint {
x: number;
y: number;
z?: number;
/**
* What the point *is* -- a country, a gene, a node of a tree.
*
* Distinct from {@link ScatterPoint.xLabel} and
* {@link ScatterPoint.yLabel}, which name *the category a coordinate is a
* position for*. \"This slot on the x axis is called Norway\" and \"this
* point is Norway\" are different statements, and a chart can make either
* one: a strip plot makes the first, a labelled scatter the second.
*
* On some charts identity is the whole payload rather than a decoration.
* A reader told \"x is 2.3, y is 14.1\" has been given the two numbers they
* can see the shape of already and withheld the one thing they came for --
* which is why a volcano plot, a Manhattan plot and Observable's canonical
* country-names-against-GDP scatter are all drawn this way, and why
* `Plot.text` exists as a mark at all.
*
* Declared here rather than on {@link VolcanoPoint}, where it began: the
* field is a property of *a point that has a name*, not of one chart type,
* and `VolcanoPoint` inherits it unchanged.
*
* An empty string counts as absent, per {@link ScatterPoint.xLabel}.
*
* @example
* { x: 3.1, y: 14.2, label: 'Norway' }
*/
label?: string;
/**
* The name of the category `x` is a position for, when the x axis carries
* names rather than measurements.
*
* `x` stays numeric because {@link ScatterTrace} does arithmetic on it —
* `sort((a, b) => a.x - b.x)`, `Math.hypot(center.x - _x, …)`, and the
* column index that stereo panning resolves through. `'a' - 'b'` is `NaN`,
* so a string in `x` alone would give an unstable sort, a broken column
* index and a highlight that resolves to nothing. The name therefore
* travels *alongside* the position rather than in place of it, which is
* also what the chart is: a category **at** a slot.
*
* {@link LinePoint} needs no equivalent for x because it never subtracts
* one — `LinePoint.x` simply widens to `string`. {@link LinePoint.label} is
* the same idea as this one applied to a line's *y*, which cannot widen
* because it drives sonification.
*
* A categorical scatter is not a rare shape: it is what
* `seaborn.stripplot`, `seaborn.swarmplot` and `ggplot2::geom_jitter` draw,
* and what any `geom_point` on a discrete scale draws. Without this a
* reader hears "g is 0" where the chart says "a" (#927).
*
* An empty string counts as absent, so a producer emitting `''` for an
* unnamed slot gets the numeric announcement rather than a blank one.
*
* @example
* { x: 0, xLabel: 'a', y: 12.5 }
*/
xLabel?: string;
/**
* The name of the category `y` is a position for, for the same reasons
* {@link ScatterPoint.xLabel} gives.
*
* Both axes carry one because either can be the categorical one: a strip
* plot drawn `sns.stripplot(df, x='g', y='v')` puts the names on x, and
* `sns.stripplot(df, y='g', x='v')` puts them on y. A single un-suffixed
* `label` would leave a consumer guessing which coordinate it names.
*
* @example
* { x: 12.5, y: 0, yLabel: 'a' }
*/
yLabel?: string;
}
/**
* Data point for segmented/grouped bar charts with fill color identifier.
*/
declare interface SegmentedPoint extends BarPoint {
z: string;
}
/**
* Data point for smooth/regression plots with data and SVG coordinate pairs.
*/
declare interface SmoothPoint {
x: number;
y: number;
svg_x: number;
svg_y: number;
}
/**
* Where a step chart jumps between two consecutive samples.
*
* - `hv` — hold `y[i]` until `x[i+1]`, then jump (matplotlib `steps-post`).
* - `vh` — jump at `x[i]`, then hold until `x[i+1]` (matplotlib `steps-pre`).
* - `mid` — jump at the midpoint of the two x values (matplotlib `steps-mid`).
*
* `hv` is what `ggplot2::geom_step()` draws by default, but MAIDR substitutes
* no default of its own: see {@link MaidrLayer.stepDirection}.
*/
declare type StepDirection = 'hv' | 'vh' | 'mid';
/**
* Data point for step charts — structurally a {@link LinePoint}.
*
* The ordinal `label` that lets a hypnogram announce "REM" instead of "3"
* started here, but it is not a step-only pairing: a line or path over the
* same ordinal y needs it just as much, so it now lives on `LinePoint` and
* every trace in the line family reads it.
*
* The name is kept because a step layer's `data` is authored as
* `StepPoint[][]`, and it says which chart the points belong to.
*
* @example
* { x: 1.5, y: 3, label: 'REM' }
*/
declare type StepPoint = LinePoint;
/**
* One point of a Kaplan-Meier survival curve.
*
* The curve itself is a step function -- survival holds until an event drops
* it -- so this is a {@link StepPoint} with the two things a survival figure
* carries that a step chart does not.
*/
declare interface SurvivalPoint extends StepPoint {
/**
* A subject left the study at this time without the event happening.
*
* Censoring marks are drawn as ticks on the curve rather than as steps,
* because censoring does not change the estimate -- it changes how much of
* the curve is still supported by data. A reader who cannot tell a censored
* time from an ordinary one cannot tell a flat tail backed by two hundred
* subjects from one backed by three.
*/
censored?: boolean;
/** Lower bound of the confidence band at this time, when the chart draws one. */
yMin?: number;
/** Upper bound of the confidence band at this time, when the chart draws one. */
yMax?: number;
}
/**
* Display configuration for a volcano or Manhattan plot layer.
*/
declare interface ThresholdOptions {
/**
* The significance cutoff on the y axis.
*
* There is deliberately no default. These charts are drawn on transformed
* axes whose conventions differ by field and by software: -log10(p) at 1.3
* for p < 0.05, and at 7.3 for genome-wide significance. A guessed line
* would sort every point on the figure onto the wrong side, silently.
*/
significance?: number;
/**
* Which side of the significance cutoff is the significant one.
*
* `above` is the default because the transformed axes these charts usually
* carry -- -log10(p) and its relatives -- put the interesting points at the
* top. A **raw p axis runs the other way**: there, p <= 0.05 is the
* finding, and a reading fixed to `above` would select precisely the points
* that failed to reach significance and announce them as the result.
*
* That is not a degraded reading, it is the exact inverse of one, which is
* why this is declarable rather than assumed.
*/
significanceDirection?: 'above' | 'below';
/**
* The effect-size cutoff on the x axis, applied to its **magnitude** -- a
* volcano is symmetric, and a fold change of -2 is as large an effect as
* one of +2.
*/
effect?: number;
}
/**
* Enumeration of supported plot trace types.
* Use these values for the `type` field in {@link MaidrLayer}.
*
* @example
* ```typescript
* import { TraceType } from 'maidr/react';
* const layer = { id: '0', type: TraceType.BAR, ... };
* // Or use the string value directly:
* const layer2 = { id: '0', type: 'bar', ... };
* ```
*/
declare enum TraceType {
/**
* A filled band between a series and a baseline. Navigates exactly as
* {@link TraceType.LINE} does — the fill is what the mark looks like, not
* an extra magnitude — so several `AREA` series are read independently of
* one another. Use {@link TraceType.STACKED_AREA} when the bands sit on
* top of each other instead.
*/
AREA = "area",
/**
* Categories that stay put while a quantity is re-divided between them at
* each step -- an alluvial diagram. The same weighted flow a
* {@link TraceType.SANKEY} carries, drawn without a left-to-right budget.
*/
ALLUVIAL = "alluvial",
BAR = "bar",
/**
* Rank over time, one line per competitor -- a bump chart. Navigated as a
* multi-line layer, with the one difference that decides whether it reads
* correctly: the y axis is a *rank*, so rank 1 is the best position and the
* smallest number, and the pitch is inverted to match. Each point announces
* the places gained or lost alongside the rank, since the overtake is what
* the chart is drawn for.
*
* A slope graph of *values* is a {@link TraceType.LINE} layer with two
* samples, not this.
*/
BUMP = "bump",
BOX = "box",
/**
* A letter-value plot: the box plot's five-number summary generalised to a
* variable-depth ladder of quantiles, so a large sample's tails stay
* legible. Navigated as a box plot is -- one distribution per row, its
* summary values walked along the other axis -- with the ladder read
* outward from the median in value order, and each rung announced as the
* percentile it actually is.
*/
BOXEN = "boxen",
CANDLESTICK = "candlestick",
/**
* Virtual layer comparing candlestick OHLC fields against a reference
* line (e.g. a moving average). Never declared in MAIDR JSON — created at
* runtime by the candlestick delta feature (Alt+L to toggle, Ctrl+Shift+L
* to pick the reference line).
*/
CANDLESTICK_DELTA = "candlestick_delta",
/**
* Flow between members of one set, drawn around a circle. Cyclic by
* construction, so it has no stages -- and every ribbon still follows.
*/
CHORD = "chord",
/**
* Geographic regions shaded by a value. Read as a bar chart whose
* categories happen to be places, it loses everything spatial: where the
* high values sit, which way the gradient runs, and which borders the
* value jumps across.
*/
CHOROPLETH = "choropleth",
/**
* A scalar field drawn as curves of constant value. Read as a
* {@link TraceType.LINE} layer the level is just a series name, so the two
* questions the chart is drawn for -- what value this curve is, and how
* steeply the field changes here -- both go unanswered.
*/
CONTOUR = "contour",
/**
* Two series drawn back to back across a shared category axis, one growing
* left and one growing right -- a population pyramid, or a Likert scale
* split around a neutral midpoint. Navigated as a
* {@link TraceType.STACKED} layer is, with the one difference that decides
* whether it reads correctly: the values arrive **signed**, and the sign is
* a direction rather than a magnitude, so the pitch takes the size and the
* announcement names the side.
*/
DIVERGING = "diverging_bar",
DODGED = "dodged_bar",
/**
* A category and a value drawn as a point rather than a bar -- a Cleveland
* dot plot. Read exactly as a {@link TraceType.BAR} is; the two differ in
* the mark, not in what a reader navigates, which is why this carries no
* model of its own. It exists so the chart announces itself as the chart
* the author drew.
*/
DOT = "dot",
/**
* Two values per category joined by a segment -- before and after, two
* groups, two years. The gap is the message, so the trace announces the
* change alongside each end rather than leaving the reader to subtract two
* numbers they heard one at a time.
*/
DUMBBELL = "dumbbell",
/**
* An estimate with the interval drawn around it — an error bar, a
* confidence interval, a point range. Navigated as a grid of
* `[lower, value, upper]` against the samples, so the reader can move
* between the three magnitudes at one x as readily as between samples.
*/
ERROR_BAR = "error_bar",
/**
* One effect estimate with its interval per study, against a shared null
* line, with a pooled summary at the foot -- the standard figure of a
* meta-analysis. Read as an {@link TraceType.ERROR_BAR} layer it loses the
* three things it is drawn for: whether an interval crosses the null, how
* much each study weighs, and which row is the pooled result rather than
* evidence.
*/
FOREST = "forest",
/**
* Intervals along a shared axis, one lane per row -- a gantt chart, a
* timeline, a swimlane diagram. Each point carries a start and an end
* rather than a magnitude, so what the reader is told is a span and its
* length, and where it sits is carried in the panning: a lane's intervals
* sweep left to right with the axis, so later is audibly later.
*/
GANTT = "gantt",
/**
* A population shrinking across ordered stages. Navigated as a
* {@link TraceType.BAR} layer is, with the one difference that decides
* whether the chart is readable: the number a reader wants is the
* **retention** between adjacent stages, not the count, so that is what the
* pitch carries. The counts are announced alongside it.
*/
FUNNEL = "funnel",
/**
* A single measure read against a range -- a gauge, or a bullet chart with
* its target and qualitative bands. One navigable point whose meaning is
* entirely relational: 73 says nothing without the 100 it is out of, the 80
* it was aiming at, and the band it lands in.
*/
GAUGE = "gauge",
HEATMAP = "heat",
/**
* Hexagonal binning: the standard answer to an overplotted scatter. Read as
* a lattice of cells each carrying a count, which is a {@link
* TraceType.HEATMAP} -- with the one difference that decides its
* navigation: a hex lattice staggers alternate rows, so a column index does
* not identify a position. A vertical move keeps the bin whose centre is
* nearest in x, and the announcement gives the centre rather than the
* indices.
*/
HEXBIN = "hexbin",
HISTOGRAM = "hist",
/**
* The same hierarchy as a {@link TraceType.TREEMAP}, drawn as depth-ordered
* bands rather than nested rectangles. The layout differs; the tree does
* not, so it is read by the same trace.
*/
ICICLE = "icicle",
LINE = "line",
/**
* A dot plot with a stem to the baseline. Read exactly as
* {@link TraceType.DOT} and {@link TraceType.BAR} are -- the stem is what
* the mark looks like, not a second magnitude.
*/
LOLLIPOP = "lollipop",
/**
* Genomic position against significance -- the standard figure of a GWAS.
* Read as a {@link TraceType.SCATTER} it offers point-by-point navigation
* over tens of thousands of points, which is not a viable path to the few
* dozen that matter.
*/
MANHATTAN = "manhattan",
/**
* A stacked bar chart whose bar **widths** also encode data -- a two-way
* contingency table drawn as tiles. Read as a {@link TraceType.STACKED}
* layer it loses the width entirely, which is half the table: the
* conditional proportions arrive without the group sizes they were
* computed from.
*/
MOSAIC = "mosaic",
/**
* A node-link diagram: nodes joined by undirected links, laid out by a
* force solver or similar. The same graph a {@link TraceType.SANKEY}
* carries with the constraints relaxed -- no stages, no direction -- and
* with degree in place of magnitude as the thing a reader is after.
*/
NETWORK = "network",
NORMALIZED = "stacked_normalized_bar",
/** {@link TraceType.STACKED_AREA} whose bands are shares of a common total. */
NORMALIZED_AREA = "stacked_normalized_area",
/**
* One polyline per observation across several axes, one axis per variable.
* Navigated as a multi-line layer -- an observation per row, an axis per
* column -- with the one difference that decides the chart: every column is
* a different quantity, so a value is pitched against its OWN axis rather
* than against one range for the layer.
*/
PARALLEL = "parallel_coordinates",
PIE = "pie",
/**
* Categories arranged around a circle rather than along an axis, drawn as
* wedges whose radius is the value -- a polar area, coxcomb or rose chart.
* Read exactly as {@link TraceType.RADAR} is; the two differ in the mark,
* not in what a reader navigates.
*/
POLAR_AREA = "polar_area",
/**
* Categories arranged around a circle rather than along an axis, joined
* into a closed outline -- a radar or spider chart. Navigated as a
* multi-line layer, with each spoke a column and each series a row; what
* the circle adds is that a spoke's stereo position follows its angle
* rather than its index, so a sweep goes out and comes back.
*/
RADAR = "radar",
/**
* One density curve per group along a shared value axis, the curves offset
* down the page so their shapes can be compared. The offset is presentation
* -- it exists so the curves do not overlap illegibly -- so a layer carries
* each group's curve on its own terms and never the baseline it was drawn
* from. Reading it as a {@link TraceType.VIOLIN_KDE} pitches every group
* against a reference curve, which answers a different question than the
* one a ridgeline is drawn to ask.
*/
RIDGELINE = "ridgeline",
/**
* Weighted flow between nodes, drawn as ribbons whose width is the
* magnitude. The chart exists to show routing and proportion at once, which
* is a question about topology -- and there is no partial reading of it
* available on a grid, because the chart is a graph.
*/
SANKEY = "sankey",
SCATTER = "point",
SMOOTH = "smooth",
/**
* A scatter for data with ties, where several observations landing on one
* coordinate are drawn as a single mark with that many petals.
*
* Read by {@link ScatterTrace}, over plain {@link ScatterPoint}s whose `z`
* is how many observations are on the mark -- `z` being announced with its
* axis label and driving the intensity, so the multiplicity is both spoken
* and audible rather than a field the reader has to go looking for.
*
* Named apart from {@link TraceType.SCATTER} for a reason stronger than the
* one {@link TraceType.TREE} gives. It is not only that "scatter plot"
* names a chart nobody drew: a sunflower plot's **marks are not its
* observations**. Sixty observations come back as twenty-one marks, because
* coincident ones were collapsed -- which is the single fact the chart was
* chosen to convey. A reader told "scatter" has been told the marks are the
* data, and here they are not.
*/
SUNFLOWER = "sunflower",
STACKED = "stacked_bar",
/**
* Area bands stacked on one another, so a band's *height* is its own
* series' value while the band's *top edge* is the running total. Reading
* such a layer as a {@link TraceType.LINE} announces one number where the
* chart draws two, with nothing to say which one was heard — which is why
* this is a type of its own rather than a line with a fill.
*/
STACKED_AREA = "stacked_area",
STEP = "step",
/**
* A hierarchy drawn as nested rectangles whose area is a magnitude. It is
* the first trace type that is not a flat grid: a node's address is its
* depth and its position within that depth, and the arrow keys move between
* parent, child and sibling rather than along rows and columns.
*/
TREEMAP = "treemap",
/**
* The same hierarchy as a {@link TraceType.TREEMAP}, drawn as boxes joined
* by links rather than as nested areas. The tree does not differ and the
* painting does, so it is read by the same trace and named apart only so
* that the reader is told what is on the page: an organization chart
* announced as a treemap is a chart type nobody drew.
*
* The magnitude is commonly absent here -- a reporting line has no size --
* which is the case {@link TreemapPoint.y} being optional exists for.
*/
TREE = "tree",
/**
* The same hierarchy as a {@link TraceType.TREEMAP}, drawn as circles
* nested inside circles rather than as nested rectangles. Sized by value
* like a treemap and navigated identically, and named apart for the reason
* {@link TraceType.TREE} is: the reader is told which chart is on the page,
* and a circle-packing diagram announced as a treemap is a chart type
* nobody drew.
*/
PACK = "pack",
/**
* The same hierarchy as a {@link TraceType.TREEMAP}, drawn as rings around
* a centre rather than as nested rectangles. The layout differs and the
* tree does not, so it is read by the same trace -- with one thing of its
* own: the rings are angular, so the sound is panned around the dial the
* way a pie's is, and sweeping a ring goes out and comes back.
*/
SUNBURST = "sunburst",
/**
* A Kaplan-Meier survival curve: the probability of surviving past each
* time, dropping in steps as events occur. Read as a {@link TraceType.STEP}
* layer it loses the two facts the figure is drawn for -- the median
* survival, which is the number most readers came for, and which times are
* censored rather than events.
*/
SURVIVAL = "survival",
VIOLIN_BOX = "violin_box",
VIOLIN_KDE = "violin_kde",
/**
* Effect size against significance -- the standard figure of a differential
* expression analysis. Read as a {@link TraceType.SCATTER} it announces the
* two coordinates and withholds the point's identity, which is the payload.
*/
VOLCANO = "volcano",
/**
* A sequence of signed contributions carrying a starting value to an ending
* one — the staple of financial and product reporting. Each step draws a
* floating bar from its running total before to its running total after, so
* the point carries both the contribution and the total it produced.
*/
WATERFALL = "waterfall",
/**
* Terms sized by weight. The layout carries no information -- it is chosen
* to pack glyphs, not to encode anything -- so the trace reads it as what
* it measures: a term and a magnitude, walked in weight order.
*/
WORD_CLOUD = "word_cloud"
}
/**
* One node of a treemap, or of any other hierarchy drawn as area.
*
* The hierarchy is declared as a **path** rather than as a parent pointer. A
* path is acyclic by construction and cannot dangle: there is no id to point
* at a node that was never emitted, and no way to author a cycle. Every
* producer has one -- a `d3.hierarchy` walk yields it directly, and Plotly's
* `labels`/`parents` pair resolves to it -- and it doubles as the breadcrumb
* the reader is told when they are several levels down.
*
* Interior nodes need not be declared. A layer emitting only its leaves --
* which is what a treemap draws -- gets its interior nodes and their totals
* derived from the paths.
*
* @example
* // A leaf three levels down, with its two ancestors named.
* { x: 'France', y: 67.4, path: ['World', 'Europe'] }
*/
declare interface TreemapPoint {
/** What the node is called. Unique among its siblings, not chart-wide. */
x: string | number;
/**
* The node's magnitude.
*
* Omitted for an interior node whose value is the sum of its children,
* which is the ordinary case. A declared value is kept even where it
* disagrees with that sum: a parent may carry mass no child accounts for,
* and overwriting it would be inventing data.
*/
y?: number;
/**
* The node's ancestors, root first, **excluding the node itself**.
*
* A top-level node omits it or declares `[]`.
*/
path?: (string | number)[];
}
/**
* Data point for violin KDE (kernel density estimation) curves.
* Library-agnostic — no SVG coordinates embedded in data.
* The density field falls back to width if absent.
*/
declare interface ViolinKdePoint {
/** Categorical label for the violin (e.g., "setosa") */
x: string | number;
/** Position along the density axis */
y: number;
/** KDE density value at this point. Falls back to `width` if absent. */
density?: number;
/** Half-width of the violin at this Y level (used as density fallback) */
width?: number;
/** SVG viewport x-coordinate for highlight positioning (provided by backend) */
svg_x?: number;
/** SVG viewport y-coordinate for highlight positioning (provided by backend) */
svg_y?: number;
}
/**
* Configuration options for violin plot display.
* Controls which summary statistics are shown in the violin box overlay.
* Sent from the Python backend alongside violin_kde and violin_box layers.
*/
declare interface ViolinOptions {
/** Show median line marker. Default: true */
showMedian?: boolean;
/** Show mean value marker. Default: false */
showMean?: boolean;
/** Show extrema (min/max) markers. Default: true */
showExtrema?: boolean;
}
/**
* One point of a volcano or Manhattan plot.
*
* Both are scatters read almost entirely through a threshold: a volcano puts
* effect size against significance, a Manhattan puts genomic position against
* it. They routinely carry tens of thousands of points of which a few dozen
* matter, so the question is never "what is at this coordinate" -- it is
* "which points cross the line, and what are they called".
*/
declare interface VolcanoPoint extends ScatterPoint {
/**
* The region the point belongs to -- a chromosome on a Manhattan plot.
*
* Announced alongside the point, because "which chromosome is it on" is
* the second question every one of these charts is read for.
*/
group?: string;
}
/**
* What a waterfall step does to the running total.
*
* `total` marks a step that restates the running total rather than changing
* it — the opening and closing bars, and any subtotal drawn along the way.
* Those sit on the baseline instead of floating, and a reader told a subtotal
* "rose by 950" would be hearing a contribution the chart never made.
*/
declare type WaterfallKind = 'increase' | 'decrease' | 'total';
/**
* One step of a waterfall chart.
*
* A waterfall answers "how did we get from here to there", so a step carries
* two numbers that a bar chart would conflate: the contribution it made
* (`delta`) and the running total it produced (`end`). The bar is drawn
* floating between `start` and `end`, which is why neither alone describes it
* — the height is the contribution and the position is the total.
*
* `start` and `end` are absolute positions on the value axis, so a producer
* that only knows offsets has to accumulate them before emitting, the same
* way {@link ErrorBarPoint} fixes absolute bounds.
*/
declare interface WaterfallPoint {
/** The step's label along the category axis. */
x: number | string;
/** Running total before this step. */
start: number;
/** Running total after this step. */
end: number;
/**
* The signed contribution, `end - start`.
*
* Carried rather than derived because a producer may round the two totals
* for display, and a delta recomputed from rounded ends is not the number
* the chart's own label shows.
*/
delta: number;
/** Whether the step adds, subtracts, or restates the total. */
kind: WaterfallKind;
}
/**
* One term of a word cloud.
*
* A word cloud is the canonical chart that carries real data while being
* readable only by eye: the weight is encoded as glyph size and written down
* nowhere on the page. Structurally it is a categorical label and a
* magnitude, which is why it needs no shape of its own beyond naming them.
*/
declare interface WordCloudPoint {
/** The term. */
x: string;
/**
* Its weight -- a frequency, a score, a count.
*
* Widened to accept a string for the same reason {@link BarPoint.y} is:
* hand-authored JSON and some producers send numbers as strings, and the
* trace coerces on the way in. Declaring it `number` alone would not stop
* one arriving, it would only stop the compiler from admitting it -- and a
* string reaching the description's running total would concatenate rather
* than add.
*/
y: number | string;
}
export { }