/** * Area chart type: independent bands, stacked bands, or stacked bands scaled * to a common whole. */ export declare type AreaTraceType = typeof TraceType.AREA | typeof TraceType.NORMALIZED_AREA | typeof TraceType.STACKED_AREA; /** * 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; } /** * Trace types that share the bar extraction: one category and one value per * mark, read the same way whichever mark is drawn. * * A dot plot draws a point where a bar chart draws a bar, a lollipop adds a * stem to the baseline, and a funnel draws its stages as trapezoids — none of * which changes what a reader navigates, so all four are built by * {@link buildBarLayer} and differ only in the type the layer announces. */ export declare type BarMarkTraceType = typeof TraceType.BAR | typeof TraceType.DOT | typeof TraceType.FUNNEL | typeof TraceType.LOLLIPOP; /** * Data point for bar charts with x and y coordinates. */ declare interface BarPoint { x: string | number; y: number | string; } /** * Binds a D3.js alluvial diagram to MAIDR. * * An alluvial is a sankey whose node columns repeat — the same category * observed at several points, with the ribbons carrying how much moved between * them — so the extraction is that of {@link bindD3Sankey} and only the chart's * announced name changes. * * @param svg - The SVG element containing the D3 alluvial diagram. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * bindD3Alluvial(svgElement, { * selector: 'path.ribbon', * title: 'Party support between elections', * axes: { x: 'Group', y: 'Voters' }, * }); * ``` */ export declare function bindD3Alluvial(svg: Element, config: D3FlowConfig): D3BinderResult; /** * Binds a D3.js area chart to MAIDR, generating the accessible data * representation. * * Handles all three variants through `config.type`: independent bands * (`TraceType.AREA`, the default), stacked areas and streamgraphs * (`TraceType.STACKED_AREA`), and 100% stacked areas * (`TraceType.NORMALIZED_AREA`). The variant decides how the layer is read: a * stacked trace announces the running total at each x, and the point's share * of it, alongside the band's own value. * * Two D3 patterns are supported, and the binder tells them apart by the datum * bound to the first matched ``: * * 1. **`d3.stack()` output** — the series array itself, with `.key`, whose * items are `[y0, y1]` tuples. `x` is read from each tuple's `.data` row * (function accessors included, so write `d => d.year`, not * `d => d.data.year`), `y` is the band's own height (`y1 - y0`, which is * what the trace needs — it re-derives the running total itself), and * `.key` names the series. * 2. **Plain point arrays** — one `{ x, y }` array per ``, or per-point * elements via `pointSelector`. Read exactly as {@link bindD3Line} reads * them. * * @remarks * **Timing — call after D3 has rendered.** Like every D3 binder, this reads * each matched element's D3-bound `__data__`; calling it before * `.data().join()` has run (or before the SVG is mounted) throws "No elements * found for selector …" or "Property '…' not found on datum". * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 area chart. * @param config - Configuration specifying selectors, accessors, and variant. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * // d3.stack() + d3.area().y0(d => y(d[0])).y1(d => y(d[1])) * const series = d3.stack().keys(['Subscriptions', 'Services'])(rows); * svg.selectAll('path.area').data(series).join('path').attr('d', area); * * bindD3Area(svgElement, { * selector: 'path.area', * type: TraceType.STACKED_AREA, * title: 'Revenue by Product', * axes: { x: 'Year', y: 'Revenue', fill: 'Product' }, * x: 'year', // a key on the stacked row, not on the [y0, y1] tuple * }); * ``` */ export declare function bindD3Area(svg: Element, config: D3AreaConfig): D3BinderResult; /** * Binds a D3.js bar chart to MAIDR, generating the accessible data representation. * * Extracts data from D3-bound SVG elements (``, ``, etc.) and * produces a complete {@link Maidr} data structure for sonification, text * descriptions, braille output, and keyboard navigation. * * @remarks * **Timing — call after D3 has rendered.** This function reads each matched * element's D3-bound `__data__`: the x (category) and y (numeric) properties * you name via the `x` / `y` accessors. Calling it before `.data().join()` * has run (or before the SVG is mounted) throws "No elements found for * selector …" or "Property '…' not found on datum". * * Typical call sites: * - **Vanilla JS:** right after your `selectAll(...).data(...).join(...)` chain. * - **React:** inside `useEffect`, never during render. Prefer * {@link MaidrD3} / {@link useD3Adapter} from `maidr/react`, which * handle the post-render timing for you. * - **Async data:** inside the `.then(...)` of your fetch, after drawing. * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element (or container) containing the D3 bar chart. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * // D3 bar chart with data bound to elements * const result = bindD3Bar(svgElement, { * selector: 'rect.bar', * title: 'Sales by Quarter', * axes: { x: 'Quarter', y: 'Revenue' }, * x: 'quarter', // property name on the bound datum * y: 'revenue', // property name on the bound datum * }); * * // Use with maidr-data attribute * svgElement.setAttribute('maidr-data', JSON.stringify(result.maidr)); * * // Or use with React * ... * ``` */ export declare function bindD3Bar(svg: Element, config: D3BarConfig): D3BinderResult; /** * Binds a D3.js box plot to MAIDR, generating the accessible data representation. * * Box plots in D3 are typically constructed from multiple SVG elements per box * (a rect for the IQR, lines for whiskers, a line for the median, and circles * for outliers). This binder extracts statistical summary data from D3-bound * data on the box group elements. * * @remarks * **Timing — call after D3 has rendered.** This function reads each matched * box-group element's D3-bound `__data__`: the 5-number summary * (`min`/`q1`/`q2`/`q3`/`max`) plus optional outlier arrays. Calling it * before `.data().join()` has run (or before the SVG is mounted) throws * "No elements found for selector …" or "Property '…' not found on datum". * * Typical call sites: * - **Vanilla JS:** right after your `selectAll(...).data(...).join(...)` chain. * - **React:** inside `useEffect`, never during render. Prefer * {@link MaidrD3} / {@link useD3Adapter} from `maidr/react`, which * handle the post-render timing for you. * - **Async data:** inside the `.then(...)` of your fetch, after drawing. * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 box plot. * @param config - Configuration specifying selectors and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * const result = bindD3Box(svgElement, { * selector: 'g.box', * title: 'Distribution by Category', * axes: { x: 'Category', y: 'Value' }, * fill: 'category', * min: 'whiskerLow', * q1: 'q1', * q2: 'median', * q3: 'q3', * max: 'whiskerHigh', * lowerOutliers: 'lowOutliers', * upperOutliers: 'highOutliers', * }); * ``` */ export declare function bindD3Box(svg: Element, config: D3BoxConfig): D3BinderResult; /** * Binds a D3.js boxen (letter-value) plot to MAIDR, generating the accessible * data representation. * * Point `selector` at one element per distribution — the `` holding that * category's stack of nested rungs — the way {@link bindD3Box} points at a box * group. Every rung of a distribution highlights that group: a chart does not * draw an element per quantile that MAIDR could pair up positionally, and * inventing one would highlight geometry the chart never drew. * * @remarks * **Timing — call after D3 has rendered.** Like every D3 binder, this reads * each matched element's D3-bound `__data__`; calling it before * `.data().join()` has run (or before the SVG is mounted) throws "No elements * found for selector …" or "Property '…' not found on datum". * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 boxen plot. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * // One per distribution, its datum carrying the ladder the rungs * // were drawn from. * const result = bindD3Boxen(svgElement, { * selector: 'g.boxen', * title: 'Response Time by Group', * axes: { x: 'Group', y: 'Milliseconds' }, * x: 'group', * levels: 'letterValues', * }); * ``` */ export declare function bindD3Boxen(svg: Element, config: D3BoxenConfig): D3BinderResult; /** * Binds a D3.js bump chart (rank over time) to MAIDR. * * A bump chart is a multi-line layer whose y values are **ranks**: 1 is the * best position and the smallest number. The extraction is that of * {@link bindD3Line} — one `` per competitor, `fill` naming it — and the * ranks you already bind are what `y` should read. The trace inverts the pitch * so first place is the highest note, and announces the places gained or lost * at each period, so nothing extra has to be computed here. * * A slope graph of *values* is a line chart with two samples, not this. * * @param svg - The SVG element containing the D3 bump chart. * @param config - Configuration specifying selectors and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * bindD3Bump(svgElement, { * selector: 'path.rank-line', * title: 'League Table by Round', * axes: { x: 'Round', y: 'Rank', fill: 'Team' }, * x: 'round', * y: 'rank', * fill: 'team', * }); * ``` */ export declare function bindD3Bump(svg: Element, config: D3LineConfig): D3BinderResult; /** * Binds a D3.js candlestick chart to MAIDR. * * Candlestick charts show OHLC (Open, High, Low, Close) data for financial * time series. This binder extracts data from D3-bound SVG elements * representing candlestick bodies (typically ``) and optional wicks. * * @remarks * **Timing — call after D3 has rendered.** This function reads each matched * element's D3-bound `__data__`: the OHLC + volume bound to each candlestick * body. Calling it before `.data().join()` has run (or before the SVG is * mounted) throws "No elements found for selector …" or "Property '…' not * found on datum". * * Typical call sites: * - **Vanilla JS:** right after your `selectAll(...).data(...).join(...)` chain. * - **React:** inside `useEffect`, never during render. Prefer * {@link MaidrD3} / {@link useD3Adapter} from `maidr/react`, which * handle the post-render timing for you. * - **Async data:** inside the `.then(...)` of your fetch, after drawing. * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 candlestick chart. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * const result = bindD3Candlestick(svgElement, { * selector: 'rect.candle', * title: 'Stock Price', * axes: { x: 'Date', y: 'Price ($)' }, * value: 'date', * open: 'open', * high: 'high', * low: 'low', * close: 'close', * volume: 'volume', * }); * ``` */ export declare function bindD3Candlestick(svg: Element, config: D3CandlestickConfig): D3BinderResult; /** * Binds a D3.js chord diagram to MAIDR. * * `selector` matches the ribbon `` elements `d3.ribbon()` drew from * `d3.chord()(matrix)` — the chords themselves, not the group arcs around the * dial. * * **Declare `names`.** A chord layout is computed from a matrix, so each end of * a ribbon is a row *index* (`{ index, value, … }`) and the labels a sighted * reader takes from the ring are not in the data at all. Without `names` the * chart announces "0 to 3" — true, and useless. * * @param svg - The SVG element containing the D3 chord diagram. * @param config - Configuration specifying the selector, the group names and * any data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * const chords = d3.chord()(matrix); * svg.selectAll('path.chord').data(chords).join('path').attr('d', d3.ribbon()…); * * bindD3Chord(svgElement, { * selector: 'path.chord', * title: 'Migration between regions', * axes: { x: 'Region', y: 'People' }, * names: ['Africa', 'Americas', 'Asia', 'Europe'], * }); * ``` */ export declare function bindD3Chord(svg: Element, config: D3FlowConfig): D3BinderResult; /** * Binds a D3.js choropleth map to MAIDR, generating the accessible data * representation. * * Point `selector` at the region paths — one per feature — and the defaults * read the name and the value off the feature's `properties`. Pass `lon` and * `lat` as `d3.geoCentroid(d)[0]` / `[1]`: with them the arrow keys move * north, south, east and west across the map, and without them the map is * read as a region list in the order it was drawn. * * A region whose value does not resolve is left out of the payload rather * than shaded with a zero — a map's "no data" regions are drawn, and * announcing one as zero is a number the chart does not contain. Its path is * left out of the highlight selectors with it, so the two stay in step. * * @remarks * **Timing — call after D3 has rendered.** Like every D3 binder, this reads * each matched element's D3-bound `__data__`; calling it before * `.data().join()` has run (or before the SVG is mounted) throws "No elements * found for selector …". * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 map. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * @throws Error when the selector matches nothing, when a matched element * carries no D3-bound datum, or when no region resolves a value. * * @example * ```ts * svg.selectAll('path.region') * .data(topojson.feature(us, us.objects.states).features) * .join('path') * .attr('d', d3.geoPath(projection)); * * const result = bindD3Choropleth(svgElement, { * selector: 'path.region', * title: 'Unemployment by State', * axes: { x: 'State', y: 'Rate' }, * value: d => rateByFips.get(d.id), * lon: d => d3.geoCentroid(d)[0], // DEGREES — never geoPath().centroid * lat: d => d3.geoCentroid(d)[1], * }); * ``` */ export declare function bindD3Choropleth(svg: Element, config: D3ChoroplethConfig): D3BinderResult; /** * Binds a D3.js contour plot to MAIDR, generating the accessible data * representation. * * Point `selector` at the level paths — one per threshold — and give `x` and * `y` the transforms from the grid `d3.contours()` walked back onto the * chart's axes (`x: i => x0 + i * dx`), or the inverse scales when the * geometry is `d3.contourDensity()`'s pixels (`x: px => xScale.invert(px)`). * * A level drawn as several disjoint rings is flattened into one curve, in the * order the rings appear: a row of the payload is a single polyline. Every * point announced is a real point of that level; what a reader cannot hear is * the jump from the end of one ring to the start of the next. * * The rows keep the order the paths were drawn in, which for `d3.contours()` * is ascending threshold — the order `ContourTrace` measures the gap to the * adjacent* level in, which is how it reports the gradient. * * @remarks * **Timing — call after D3 has rendered.** Like every D3 binder, this reads * each matched element's D3-bound `__data__`; calling it before * `.data().join()` has run (or before the SVG is mounted) throws "No elements * found for selector …". * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 contour plot. * @param config - Configuration specifying the selector and grid transforms. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * const contours = d3.contours().size([n, m])(values); * svg.selectAll('path.contour').data(contours).join('path').attr('d', d3.geoPath()); * * const result = bindD3Contour(svgElement, { * selector: 'path.contour', * title: 'Density Field', * axes: { x: 'X', y: 'Y', fill: 'Density' }, * x: column => x0 + column * cellWidth, * y: row => y0 + row * cellHeight, * }); * ``` */ export declare function bindD3Contour(svg: Element, config: D3ContourConfig): D3BinderResult; /** * Binds a D3.js diverging bar chart — a population pyramid, a Likert scale — * to MAIDR. * * A diverging chart is two series drawn back to back across a shared category * axis, which is the segmented extraction with a different reading: the sign * of a value is a **side**, not a magnitude, and the trace pitches the * magnitude while announcing the side. * * So emit the values **as the chart draws them** — the left-hand series * negative — and do not take their absolute value. Handed unsigned data the * trace has no way to tell the two sides apart, and the balance it reports * between them becomes a total instead of a comparison. * * A pyramid is usually drawn on its side. Pass * `orientation: Orientation.HORIZONTAL` (with `x` reading the signed value and * `y` the category) so the axes are announced the way it was drawn. * * @param svg - The SVG element containing the D3 diverging bar chart. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * bindD3Diverging(svgElement, { * selector: 'rect.band', * title: 'Population by Age Band', * orientation: Orientation.HORIZONTAL, * axes: { x: 'People, thousands', y: 'Age band', fill: 'Sex' }, * x: 'people', // negative for the side drawn to the left * y: 'band', * fill: 'sex', * }); * ``` */ export declare function bindD3Diverging(svg: Element, config: D3SegmentedConfig): D3BinderResult; /** * Binds a D3.js Cleveland dot plot to MAIDR. * * A dot plot is a bar chart drawn with a different mark: one category, one * value, navigated the same way. The extraction is therefore identical to * {@link bindD3Bar} — point `selector` at the `` marks so MAIDR * highlights the dots themselves — and only the announced chart type differs. * * Dot plots are usually drawn with the categories down the page. Pass * `orientation: Orientation.HORIZONTAL` (with `x` reading the value and `y` * the category) so the axes are announced the way the chart was drawn. * * @param svg - The SVG element (or container) containing the D3 dot plot. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * bindD3Dot(svgElement, { * selector: 'circle.dot', * title: 'Median Response Time', * orientation: Orientation.HORIZONTAL, * axes: { x: 'Milliseconds', y: 'Endpoint' }, * x: 'ms', * y: 'endpoint', * }); * ``` */ export declare function bindD3Dot(svg: Element, config: D3BarConfig): D3BinderResult; /** * Binds a D3.js dumbbell chart to MAIDR, generating the accessible data * representation. * * Point `selector` at the **connectors** — one `` per row — rather than * at the dots. A dumbbell draws one segment and two dots per row, so the * connectors are the elements that map one-to-one onto the data; a selector * matching the dots would produce twice as many elements as rows and the trace * would withdraw highlighting rather than pair them wrongly. * * Name the two ends with `startLabel` / `endLabel`. They are what the chart's * legend gives a sighted reader for free: without them a chart of life * expectancy in 1990 against 2020 tells the reader they are on the "start" * dot, which is the one thing they already knew. * * @remarks * **Timing — call after D3 has rendered.** Like every D3 binder, this reads * each matched element's D3-bound `__data__`; calling it before * `.data().join()` has run (or before the SVG is mounted) throws "No elements * found for selector …" or "Property '…' not found on datum". * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 dumbbell chart. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * const result = bindD3Dumbbell(svgElement, { * selector: 'line.connector', * title: 'Life Expectancy, 1990 against 2020', * orientation: Orientation.HORIZONTAL, * axes: { x: 'Years', y: 'Country' }, * x: 'country', * start: 'y1990', * end: 'y2020', * startLabel: '1990', * endLabel: '2020', * }); * ``` */ export declare function bindD3Dumbbell(svg: Element, config: D3DumbbellConfig): D3BinderResult; /** * Binds a D3.js error-bar chart to MAIDR, generating the accessible data * representation. * * Point `selector` at one element per estimate — the canonical D3 idiom is a * `` holding the interval's `` and the estimate's marker, and that * group is what the reader's cursor should highlight. * * **The bounds are absolute positions on the value axis, not half-widths.** * Producers disagree about which they hand out, so the grammar fixes one and * each adapter converts. The binder cannot tell an offset from a bound by * looking at it, so a datum carrying `±se` needs a function accessor: * `yMin: d => d.mean - 1.96 * d.se`. * * @remarks * **Timing — call after D3 has rendered.** Like every D3 binder, this reads * each matched element's D3-bound `__data__`; calling it before * `.data().join()` has run (or before the SVG is mounted) throws "No elements * found for selector …" or "Property '…' not found on datum". * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 error-bar chart. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * const result = bindD3ErrorBar(svgElement, { * selector: 'g.estimate', * title: 'Mean Response by Dose', * axes: { x: 'Group', y: 'Response' }, * x: 'group', * y: 'mean', * yMin: 'ciLow', * yMax: 'ciHigh', * }); * ``` */ export declare function bindD3ErrorBar(svg: Element, config: D3ErrorBarConfig): D3BinderResult; /** * Binds a faceted D3 chart (homogeneous small multiples) to MAIDR. * * Every element matched by `panelSelector` inside the SVG becomes one MAIDR * subplot; the per-type binder selected by `chartType` runs with that panel * element as its extraction root, so `config.selector` (e.g. `'rect.bar'`) * only needs to match marks *within* a panel. * * Panel display titles come from `panelTitle`, resolved against each panel * element's D3-bound `__data__`. When panels were joined with `d3.groups` * output (`[key, values]` tuples) or nest-style `{ key, values }` objects, * the group key is used automatically; otherwise the fallback is `Panel `. * * The grid shape follows `layout` when given, else panel geometry (bounding * boxes, then `transform="translate(x,y)"`), else a single row in DOM order. * Panels are emitted in visual reading order (top-left first). * * @remarks * **Timing — call after D3 has rendered**, exactly like the single-chart * binders: panel elements and their marks must exist with `__data__` bound. * * @param svg - The SVG element containing all panels. * @param config - Panel selector, chart type + per-type config, and options. * @returns A {@link D3MultiPanelResult} with the multi-subplot MAIDR data. * * @example * ```ts * // One per cylinder count, each holding rect.bar marks * const result = bindD3Facets(svgElement, { * panelSelector: 'g.panel', * chartType: 'bar', * config: { * selector: 'rect.bar', * title: 'MPG by Origin, faceted by Cylinders', * axes: { x: 'Origin', y: 'MPG' }, * x: 'origin', * y: 'mpg', * }, * panelTitle: d => `cyl = ${d[0]}`, // d3.groups tuple * }); * ``` */ export declare function bindD3Facets(svg: Element, config: D3FacetsConfig): D3MultiPanelResult; /** * Binds a D3.js forest plot to MAIDR, generating the accessible data * representation. * * A forest plot is a point-range chart of studies against a shared null line, * so `selector` matches one element per study exactly as * {@link bindD3ErrorBar}'s does, and the bounds are the same absolute positions * on the value axis. * * Three things make it a forest plot rather than an error bar, and each is what * a sighted reader takes from the drawing: * * - **`nullValue`** — the line at 1 (a ratio) or 0 (a difference). Whether a * study's interval crosses it *is the result for that study*, and the trace * announces the crossing. Without it, no claim about significance is made: * there is no default, because a ratio chart guessed at 0 would report every * study as not crossing. * - **`weight`** — the 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. * - **`pooledSelector`** — the diamond at the bottom. It is drawn as a * different mark from the studies, so it is selected separately and appended * after them; 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. * * @param svg - The SVG element containing the D3 forest plot. * @param config - Configuration specifying the selectors and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * bindD3Forest(svgElement, { * selector: 'g.study', * pooledSelector: 'path.pooled', * title: 'Effect of the Intervention', * orientation: Orientation.HORIZONTAL, * axes: { x: 'Odds ratio', y: 'Study' }, * x: 'study', * y: 'or', * yMin: 'ciLow', * yMax: 'ciHigh', * weight: 'weight', * nullValue: 1, * }); * ``` */ export declare function bindD3Forest(svg: Element, config: D3ForestConfig): D3BinderResult; /** * Binds a D3.js funnel chart to MAIDR. * * A funnel is a bar chart whose **order is meaningful**: the trace pitches the * retention between adjacent stages rather than the raw counts, because the * drop-off is what the chart is drawn for. Stage order therefore comes * straight from the DOM — the binder keeps the elements in the order D3 joined * them, so draw the stages top-to-bottom (or left-to-right) in funnel order. * * `selector` matches one element per stage, whichever mark you drew it with: * a trapezoid ``, a centred ``, or a `` per stage. * * @param svg - The SVG element (or container) containing the D3 funnel chart. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * bindD3Funnel(svgElement, { * selector: 'path.stage', * title: 'Checkout Funnel', * axes: { x: 'Stage', y: 'People' }, * x: 'stage', * y: 'count', * }); * ``` */ export declare function bindD3Funnel(svg: Element, config: D3BarConfig): D3BinderResult; /** * Binds a D3.js gantt chart to MAIDR, generating the accessible data * representation. * * Point `selector` at the interval marks — one `` per booked interval. * The binder groups them into lanes itself, so a lane holding several intervals * needs nothing extra; what it cannot discover is an **empty** lane, which has * no element in the DOM at all. Declare those with `lanes`. * * **Dates become epoch milliseconds.** A `Date`, or a string one parses from, * is coerced so the trace can measure lengths; pair it with * `format: { type: 'date' }` so the ends are announced as dates. `unit` names * what a length is counted in, which a bare number cannot say. * * @remarks * **Timing — call after D3 has rendered.** Like every D3 binder, this reads * each matched element's D3-bound `__data__`; calling it before * `.data().join()` has run (or before the SVG is mounted) throws "No elements * found for selector …". * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 gantt chart. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * const result = bindD3Gantt(svgElement, { * selector: 'rect.task', * title: 'Project Schedule', * axes: { x: 'Day', y: 'Phase' }, * x: 'phase', * start: 'from', * end: 'to', * label: 'task', * lanes: ['Design', 'Build', 'Review', 'Launch'], * unit: 'days', * }); * ``` */ export declare function bindD3Gantt(svg: Element, config: D3GanttConfig): D3BinderResult; /** * Binds a D3.js gauge or bullet chart to MAIDR, generating the accessible data * representation. * * Point `selector` at the mark that moves with the value — the needle, the * value arc, the bullet's measure bar. Only the first match is read: a gauge * draws exactly one measure, which is why its payload is a single object * rather than an array of one. * * `min` and `max` are required because 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 a sighted reader takes all three from the * dial's geometry, which is exactly what a screen reader cannot reach. * * @remarks * **Timing — call after D3 has rendered.** Like every D3 binder, this reads * the matched element's D3-bound `__data__`; calling it before * `.data().join()` has run (or before the SVG is mounted) throws "No elements * found for selector …" or "Property '…' not found on datum". * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 gauge. * @param config - Configuration specifying the selector, the range and the * chart's own annotations. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * const result = bindD3Gauge(svgElement, { * selector: 'rect.measure', * title: 'Conversion Rate against Target', * axes: { x: 'Measure', y: 'Percent' }, * label: 'Conversion', * min: 0, * max: 100, * target: 80, * bands: [ * { to: 50, label: 'poor' }, * { to: 75, label: 'ok' }, * { to: 100, label: 'good' }, * ], * }); * ``` */ export declare function bindD3Gauge(svg: Element, config: D3GaugeConfig): D3BinderResult; /** * Binds a D3.js heatmap to MAIDR, generating the accessible data representation. * * Extracts cell data from D3-bound SVG elements (``) organized in a grid * and produces a complete {@link Maidr} data structure. The cells are grouped * by their x and y category values to form the 2D points grid. * * @remarks * **Timing — call after D3 has rendered.** This function reads each matched * element's D3-bound `__data__`: the x/y category pair and cell value bound * to each heatmap cell. Calling it before `.data().join()` has run (or * before the SVG is mounted) throws "No elements found for selector …" or * "Property '…' not found on datum". * * Typical call sites: * - **Vanilla JS:** right after your `selectAll(...).data(...).join(...)` chain. * - **React:** inside `useEffect`, never during render. Prefer * {@link MaidrD3} / {@link useD3Adapter} from `maidr/react`, which * handle the post-render timing for you. * - **Async data:** inside the `.then(...)` of your fetch, after drawing. * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @remarks * **Say which row is the top one, and which column is the leftmost.** The * schema orders a heatmap's rows top-first and its columns left-first, and * without {@link D3HeatmapConfig.yOrder} and {@link D3HeatmapConfig.xOrder} * both are taken in the order the cells appear in the DOM -- the order your * `.data().join()` ran in, which need not be the order your scales draw. A * band scale whose domain ascends up the page joins bottom-first, and the * chart is then read upside down: the cursor enters at the top row and * Up walks down it. The column equivalent reads it mirrored, with * Right walking left. Nothing errors either way, and every value is * still announced against its own label (#978, #1013). Pass * `yOrder: yScale.domain()` and `xOrder: xScale.domain()`, reversing either * for a band scale that runs the other way. * * @param svg - The SVG element containing the D3 heatmap. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * @throws Error if any cell coordinate pair is missing from the extracted data. * * @example * ```ts * const result = bindD3Heatmap(svgElement, { * selector: 'rect.cell', * title: 'Correlation Matrix', * axes: { x: 'Variable', y: 'Variable', fill: 'Correlation' }, * x: 'xVar', * y: 'yVar', * value: 'correlation', * }); * ``` */ export declare function bindD3Heatmap(svg: Element, config: D3HeatmapConfig): D3BinderResult; /** * Binds a D3.js hexbin density plot to MAIDR, generating the accessible data * representation. * * Point `selector` at the hexagons — one per bin — and give `x` and `y` the * inverse scales, so the bins announce their centres on the chart's own axes * rather than in pixels. `count` defaults to a bin's `length`, which is what * `d3-hexbin` gives it. * * The lattice rows are assembled from the emitted data rather than assumed: * an empty bin is not drawn, so the rows do not all hold the same number of * bins and no rectangular grid could be laid over them. * * @remarks * **Timing — call after D3 has rendered.** Like every D3 binder, this reads * each matched element's D3-bound `__data__`; calling it before * `.data().join()` has run (or before the SVG is mounted) throws "No elements * found for selector …". * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 hexbin plot. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * const result = bindD3Hexbin(svgElement, { * selector: 'path.hexagon', * title: 'Point Density', * axes: { x: 'Carat', y: 'Price', fill: 'Count' }, * x: d => xScale.invert(d.x), * y: d => yScale.invert(d.y), * }); * ``` */ export declare function bindD3Hexbin(svg: Element, config: D3HexbinConfig): D3BinderResult; /** * Binds a D3.js histogram to MAIDR, generating the accessible data representation. * * D3 histograms are typically created with `d3.bin()` (or `d3.histogram()` in v5), * which produces arrays with `x0` and `x1` properties for bin boundaries. This * binder extracts bin data from D3-bound rect elements. * * @remarks * **Timing — call after D3 has rendered.** This function reads each matched * element's D3-bound `__data__`: the bin boundaries (`x0`/`x1`) and count * bound to each bar — typically produced by `d3.bin()`. Calling it before * `.data().join()` has run (or before the SVG is mounted) throws "No * elements found for selector …" or "Property '…' not found on datum". * * Typical call sites: * - **Vanilla JS:** right after your `selectAll(...).data(...).join(...)` chain. * - **React:** inside `useEffect`, never during render. Prefer * {@link MaidrD3} / {@link useD3Adapter} from `maidr/react`, which * handle the post-render timing for you. * - **Async data:** inside the `.then(...)` of your fetch, after drawing. * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 histogram. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * // D3 histogram using d3.bin() * const result = bindD3Histogram(svgElement, { * selector: 'rect.bar', * title: 'Age Distribution', * axes: { x: 'Age', y: 'Count' }, * x: (d) => `${d.x0}-${d.x1}`, * y: (d) => d.length, * xMin: 'x0', * xMax: 'x1', * yMin: () => 0, * yMax: (d) => d.length, * }); * ``` */ export declare function bindD3Histogram(svg: Element, config: D3HistogramConfig): D3BinderResult; /** * Binds a D3.js icicle to MAIDR. * * An icicle is the sunburst's partition drawn in cartesian coordinates: the * same `d3.partition()` over the same `d3.hierarchy()`, laid out as * depth-ordered bands rather than concentric arcs. Nothing about the tree * changes, so the extraction is that of {@link bindD3Sunburst} — `selector` * matches the `` bands the partition produced, and whatever you joined * (leaves alone, or `descendants()` with its interior nodes) is what the reader * navigates. * * @param svg - The SVG element containing the D3 icicle. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * const root = d3.partition().size([height, width])( * d3.hierarchy(data).sum(d => d.population), * ); * svg.selectAll('rect.band').data(root.descendants().slice(1)).join('rect')…; * * bindD3Icicle(svgElement, { * selector: 'rect.band', * title: 'World population by region', * axes: { x: 'Region', y: 'Population, millions' }, * }); * ``` */ export declare function bindD3Icicle(svg: Element, config: D3TreemapConfig): D3BinderResult; /** * Binds a D3.js line chart to MAIDR, generating the accessible data representation. * * Supports both single-line and multi-line charts. Data can be extracted from: * 1. D3-bound data on point elements (circles, etc.) via `pointSelector`. * When using `pointSelector`, each line path and its associated points * must share the same parent `` group element for correct scoping. * 2. D3-bound data on the path elements themselves (array of points per path). * * @remarks * **Timing — call after D3 has rendered.** This function reads each matched * element's D3-bound `__data__`: an array of points per line path, or * individual point data when `pointSelector` is set. Calling it before * `.data().join()` has run (or before the SVG is mounted) throws "No * elements found for selector …" or "Property '…' not found on datum". * * Typical call sites: * - **Vanilla JS:** right after your `selectAll(...).data(...).join(...)` chain. * - **React:** inside `useEffect`, never during render. Prefer * {@link MaidrD3} / {@link useD3Adapter} from `maidr/react`, which * handle the post-render timing for you. * - **Async data:** inside the `.then(...)` of your fetch, after drawing. * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 line chart. * @param config - Configuration specifying selectors and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * // Multi-line chart with paths and point circles * const result = bindD3Line(svgElement, { * selector: 'path.line', * pointSelector: 'circle.data-point', * title: 'Temperature Over Time', * axes: { x: 'Month', y: 'Temperature (F)' }, * x: 'month', * y: 'temp', * fill: 'city', * }); * ``` */ export declare function bindD3Line(svg: Element, config: D3LineConfig): D3BinderResult; /** * Binds a D3.js lollipop chart to MAIDR. * * A lollipop is a dot plot with a stem to the baseline: the stem is what the * mark looks like, not a second magnitude, so the extraction is again that of * {@link bindD3Bar}. * * Point `selector` at the **heads** (one `` per category) or at a * `` wrapping each head-and-stem pair — one matched element per category. * A selector that also matched the `` stems would produce two elements * per category, so the data would be doubled and highlighting would land on * the wrong mark. * * @param svg - The SVG element (or container) containing the D3 lollipop chart. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * bindD3Lollipop(svgElement, { * selector: 'circle.head', * title: 'Life Expectancy', * orientation: Orientation.HORIZONTAL, * axes: { x: 'Years', y: 'Country' }, * x: 'years', * y: 'country', * }); * ``` */ export declare function bindD3Lollipop(svg: Element, config: D3BarConfig): D3BinderResult; /** * Binds a D3.js Manhattan plot to MAIDR. * * A Manhattan plot is a scatter of `-log10(p)` against genomic position, drawn * with tens of thousands of points of which a few dozen matter. The question a * reader asks it is never "what is at this coordinate" — it is "which points * cross the line, and what are they called", so the two accessors that answer * that (`label`, `group`) and the `significance` cutoff are what this binder * adds to {@link bindD3Scatter}. * * All three are optional, and the chart still reads without them: the trace * simply reports no findings when no cutoff was declared, rather than guessing * one. It is worth supplying them — the cutoff is the whole reason the chart * was drawn, and it is written nowhere a screen reader can reach. * * @param svg - The SVG element containing the D3 Manhattan plot. * @param config - Configuration specifying the selector, accessors and cutoff. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * bindD3Manhattan(svgElement, { * selector: 'circle.snp', * title: 'Genome-wide Association', * axes: { x: 'Position', y: '-log10(p)', fill: 'Chromosome' }, * x: 'pos', * y: 'logP', * label: 'snp', * group: 'chromosome', * significance: 7.3, * }); * ``` */ export declare function bindD3Manhattan(svg: Element, config: D3ManhattanConfig): D3BinderResult; /** * Binds a D3.js mosaic (marimekko) plot to MAIDR. * * A mosaic is a stacked bar chart in which the **column widths also encode * data** — usually each category's share of all observations. That share is * the one thing this binder reads that the stacked one does not, and it is * worth supplying: a reader given only the segment heights has half the table, * so a category of six people and one of six hundred read identically. * * The width is read from the datum, never measured off the rendered ``. * A drawn width is a layout fact — padding, margins, the scale the columns * were laid out on — and turning it back into a proportion would announce a * number the data does not contain. * * `count` travels too when you have the contingency table the mosaic was drawn * from, since those counts are the numbers a reader would quote back. * * @param svg - The SVG element containing the D3 mosaic plot. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * bindD3Mosaic(svgElement, { * selector: 'rect.cell', * title: 'Survival by Passenger Class', * axes: { x: 'Class', y: 'Proportion', fill: 'Outcome' }, * x: 'klass', * y: 'share', * fill: 'outcome', * width: 'columnShare', * count: 'n', * }); * ``` */ export declare function bindD3Mosaic(svg: Element, config: D3MosaicConfig): D3BinderResult; /** * Binds a D3.js force-directed network to MAIDR, generating the accessible * data representation. * * Point `selector` at the **links** — one `` per edge — rather than at * the node circles. The nodes are derived from the links, so the links are what * map one-to-one onto the payload; a selector matching the circles would give * the trace a node count where it expects a link count, and highlighting would * be withdrawn. * * @remarks * **Timing — call after the simulation has been wired up.** `d3.forceLink` * replaces each link's `source` and `target` with the resolved node objects, so * the binder reads names either way; what it cannot do is run before the lines * are joined. Bind right after the `.data(links).join('line')` chain — there is * no need to wait for the simulation to settle, since no position is read. * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 network. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * const link = svg.selectAll('line.link').data(links).join('line'); * d3.forceSimulation(nodes).force('link', d3.forceLink(links).id(d => d.id)); * * bindD3Network(svgElement, { * selector: 'line.link', * title: 'Collaborations', * axes: { x: 'Person', y: 'Links' }, * }); * ``` */ export declare function bindD3Network(svg: Element, config: D3NetworkConfig): D3BinderResult; /** * Binds a D3.js parallel coordinates plot to MAIDR, generating the accessible * data representation. * * Point `selector` at the observation paths — one per row of your data — and * list the axes in `dimensions`, in the order they are drawn. `label` names * the observation, which is what the trace announces the row as. * * The trace derives each axis' range from the data itself and sonifies every * value against its own axis, so nothing here needs per-axis minima: a car with * the best economy and the worst power sounds like exactly that, rather than * like the units the two variables happen to be measured in. * * @remarks * **Timing — call after D3 has rendered.** Like every D3 binder, this reads * each matched element's D3-bound `__data__`; calling it before * `.data().join()` has run (or before the SVG is mounted) throws "No elements * found for selector …". * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 parallel coordinates plot. * @param config - Configuration specifying the selector, dimensions and label. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * const result = bindD3Parallel(svgElement, { * selector: 'path.observation', * title: 'Car Characteristics', * axes: { x: 'Variable', y: 'Value', fill: 'Car' }, * dimensions: ['mpg', 'hp', 'weight'], * label: 'name', * }); * ``` */ export declare function bindD3Parallel(svg: Element, config: D3ParallelConfig): D3BinderResult; /** * Binds a D3.js pie or doughnut chart to MAIDR, generating the accessible * data representation. * * Extracts data from the wedge `` elements produced by the canonical * `d3.pie()` + `d3.arc()` pair and produces a complete {@link Maidr} data * structure for sonification, text descriptions, braille output, and keyboard * navigation. A pie is one row of slices: left and right move between them. * * @remarks * **Timing — call after D3 has rendered.** This function reads each matched * element's D3-bound `__data__`. When the marks were joined to `d3.pie()` * output — the usual case — the slice magnitude is taken from the arc's * `value`, which is what the layout itself drew the angle from, so only the * label accessor is normally needed. Calling the binder before * `.data().join()` has run (or before the SVG is mounted) throws "No elements * found for selector …" or "Property '…' not found on datum". * * Typical call sites: * - **Vanilla JS:** right after your `selectAll(...).data(...).join(...)` chain. * - **React:** inside `useEffect`, never during render. Prefer * {@link MaidrD3} / {@link useD3Adapter} from `maidr/react`, which * handle the post-render timing for you. * - **Async data:** inside the `.then(...)` of your fetch, after drawing. * * **Slice order.** `d3.pie()` returns its arcs in the input data order even * when it sorts them by value for drawing, so the wedges a single * `.data(pie(data)).join('path')` produces are already in the order MAIDR * needs: matched element k is data point k. * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element (or container) containing the D3 pie chart. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * // svg.selectAll('path.slice').data(d3.pie().value(d => d.units)(data)) * const result = bindD3Pie(svgElement, { * selector: 'path.slice', * title: 'Fruit sales', * axes: { x: 'Fruit', y: 'Units' }, * x: 'fruit', // property name on YOUR datum, not on the arc * }); * ``` */ export declare function bindD3Pie(svg: Element, config: D3PieConfig): D3BinderResult; /** * Binds a D3.js polar area (coxcomb, rose) chart to MAIDR, generating the * accessible data representation. * * Point `selector` at the wedge `` elements, exactly as for * {@link bindD3Pie} — including when they were joined to `d3.pie()` output, * which the binder unwraps for you. The difference is in the reading: a polar * area gives every category the same angle and encodes its value as the * wedge's **radius**, so the values are a series around the spokes rather than * shares of a whole, and the trace announces them as such (with no * percentages) while panning each spoke to where it is drawn on the dial. * * @remarks * **Timing — call after D3 has rendered.** Like every D3 binder, this reads * each matched element's D3-bound `__data__`; calling it before * `.data().join()` has run (or before the SVG is mounted) throws "No elements * found for selector …" or "Property '…' not found on datum". * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 polar area chart. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * // svg.selectAll('path.wedge').data(d3.pie().value(d => d.deaths)(rows)) * const result = bindD3PolarArea(svgElement, { * selector: 'path.wedge', * title: 'Causes of Mortality', * axes: { x: 'Month', y: 'Deaths' }, * x: 'month', // property name on YOUR datum, not on the arc * }); * ``` */ export declare function bindD3PolarArea(svg: Element, config: D3PolarAreaConfig): D3BinderResult; /** * Binds a D3.js radar (spider) chart to MAIDR. * * A radar is a multi-line layer wrapped around a circle: `selector` matches one * closed `d3.lineRadial()` `` per series, `x` names the **spoke** (the * variable) and `fill` names the series. The trace pans each spoke by its angle * — 12 o'clock centre, 3 o'clock hard right — so a radar sounds like a circle * rather than a row of bars, and nothing extra has to be computed here. * * A closed outline is usually drawn by repeating the first vertex at the end. * That repeat is how the polygon closes, not a spoke of its own, so the binder * drops a trailing sample whose `x` matches the first one — otherwise the chart * announces one spoke more than it has, and the reader walks off the end into * a duplicate. * * @param svg - The SVG element containing the D3 radar chart. * @param config - Configuration specifying selectors and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * bindD3Radar(svgElement, { * selector: 'path.radar-area', * title: 'Model Comparison', * axes: { x: 'Attribute', y: 'Score', fill: 'Model' }, * x: 'attribute', * y: 'score', * fill: 'model', * }); * ``` */ export declare function bindD3Radar(svg: Element, config: D3LineConfig): D3BinderResult; /** * Binds a D3.js ridgeline (joy) plot to MAIDR, generating the accessible data * representation. * * Point `selector` at the group curves — one `` per ridge. Each path's * datum supplies the group's samples: the array itself, a `d3.groups()` tuple, * or a `values`/`samples`/`points`/`curve` property holding one. * * The three per-sample fields are named for what they mean rather than for the * payload keys they land on, because a ridgeline's value axis is usually the * drawn `x`: `group` names the ridge, `value` is the position along the value * axis, and `density` is the curve's own half-width there — the density * **before** the ridge's baseline was added to it. Passing the drawn y for * `density` is the one mistake this chart type invites; the binder cannot * detect it, so it is worth checking against the array you fed * `d3.area().y1(...)`. * * @remarks * **Timing — call after D3 has rendered.** Like every D3 binder, this reads * each matched element's D3-bound `__data__`; calling it before * `.data().join()` has run (or before the SVG is mounted) throws "No elements * found for selector …". * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 ridgeline plot. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * const result = bindD3Ridgeline(svgElement, { * selector: 'path.ridge', * title: 'Delivery Time by Cohort', * axes: { x: 'Days', y: 'Cohort', fill: 'Cohort' }, * group: 'cohort', * value: 'days', * density: 'density', * }); * ``` */ export declare function bindD3Ridgeline(svg: Element, config: D3RidgelineConfig): D3BinderResult; /** * Binds a D3.js sankey diagram to MAIDR, generating the accessible data * representation. * * Point `selector` at the **ribbons** — one `` per link, the * `d3-sankey` idiom being `.data(graph.links).join('path')` — rather than at * the node rectangles. The nodes are derived from the links, so the links are * what map one-to-one onto the payload; a selector matching the node rects * would give the trace a node count where it expects a link count, and * highlighting would be withdrawn. * * @remarks * **Timing — call after the layout has run.** `sankey(graph)` replaces each * link's `source` and `target` with the resolved node objects, and the binder * reads names either way; what it cannot do is run before the ribbons are * joined. * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 sankey. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * const graph = sankey({ nodes, links }); * svg.selectAll('path.ribbon').data(graph.links).join('path')…; * * bindD3Sankey(svgElement, { * selector: 'path.ribbon', * title: 'Energy flow', * axes: { x: 'Node', y: 'Petajoules' }, * }); * ``` */ export declare function bindD3Sankey(svg: Element, config: D3FlowConfig): D3BinderResult; /** * Binds a D3.js scatter plot to MAIDR, generating the accessible data representation. * * Extracts x/y data from D3-bound SVG point elements (``, ``, etc.) * and produces a complete {@link Maidr} data structure. * * @remarks * **Timing — call after D3 has rendered.** This function reads each matched * element's D3-bound `__data__`: the numeric x/y bound to each point element. * Calling it before `.data().join()` has run (or before the SVG is mounted) * throws "No elements found for selector …" or "Property '…' not found on * datum". * * Typical call sites: * - **Vanilla JS:** right after your `selectAll(...).data(...).join(...)` chain. * - **React:** inside `useEffect`, never during render. Prefer * {@link MaidrD3} / {@link useD3Adapter} from `maidr/react`, which * handle the post-render timing for you. * - **Async data:** inside the `.then(...)` of your fetch, after drawing. * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 scatter plot. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * const result = bindD3Scatter(svgElement, { * selector: 'circle.dot', * title: 'Height vs Weight', * axes: { x: 'Height (cm)', y: 'Weight (kg)' }, * x: 'height', * y: 'weight', * }); * ``` */ export declare function bindD3Scatter(svg: Element, config: D3ScatterConfig): D3BinderResult; /** * Binds a D3.js segmented bar chart (stacked, dodged, or normalized) to MAIDR. * * Segmented bar charts extend regular bar charts with a `fill` dimension that * identifies the segment/group within each bar. The data is organized as a * 2D array where each inner array represents a series/group. * * @remarks * **Timing — call after D3 has rendered.** This function reads each matched * element's D3-bound `__data__`: the x/y/fill bound to each bar segment — * or, with `groupSelector`, the `d3.stack()` tuple plus the parent group's * `.key`. Calling it before `.data().join()` has run (or before the SVG is * mounted) throws "No elements found for selector …" or "Property '…' not * found on datum". * * Typical call sites: * - **Vanilla JS:** right after your `selectAll(...).data(...).join(...)` chain. * - **React:** inside `useEffect`, never during render. Prefer * {@link MaidrD3} / {@link useD3Adapter} from `maidr/react`, which * handle the post-render timing for you. * - **Async data:** inside the `.then(...)` of your fetch, after drawing. * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 segmented bar chart. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * // Flat structure: each rect has { x, y, fill } data * const result = bindD3Segmented(svgElement, { * selector: 'rect.bar', * type: 'stacked_bar', * title: 'Revenue by Region and Quarter', * axes: { x: 'Quarter', y: 'Revenue', fill: 'Region' }, * x: 'quarter', * y: 'revenue', * fill: 'region', * }); * * // d3.stack() structure: groups contain segments * const result = bindD3Segmented(svgElement, { * groupSelector: 'g.series', * selector: 'rect', * type: 'stacked_bar', * title: 'Revenue by Region and Quarter', * x: (d) => d.data.category, * y: (d) => d[1] - d[0], * }); * ``` */ export declare function bindD3Segmented(svg: Element, config: D3SegmentedConfig): D3BinderResult; /** * Binds a D3.js smooth/regression curve to MAIDR. * * Smooth plots represent fitted curves (e.g., LOESS, regression lines). * The data includes both the data-space coordinates (x, y) and SVG-space * coordinates (svg_x, svg_y) for each point along the curve. * * @remarks * **Timing — call after D3 has rendered.** This function reads each matched * element's D3-bound `__data__`: both the data-space (`x`/`y`) and SVG-space * (`svg_x`/`svg_y`) coords bound to each curve point. Calling it before * `.data().join()` has run (or before the SVG is mounted) throws "No * elements found for selector …" or "Property '…' not found on datum". * * Typical call sites: * - **Vanilla JS:** right after your `selectAll(...).data(...).join(...)` chain. * - **React:** inside `useEffect`, never during render. Prefer * {@link MaidrD3} / {@link useD3Adapter} from `maidr/react`, which * handle the post-render timing for you. * - **Async data:** inside the `.then(...)` of your fetch, after drawing. * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 smooth curve. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * const result = bindD3Smooth(svgElement, { * selector: 'circle.smooth-point', * title: 'LOESS Regression', * axes: { x: 'X', y: 'Y (predicted)' }, * x: 'x', * y: 'yPredicted', * svgX: 'screenX', * svgY: 'screenY', * }); * ``` */ export declare function bindD3Smooth(svg: Element, config: D3SmoothConfig): D3BinderResult; /** * Binds a heterogeneous grid of D3 charts (one SVG, several independently * drawn panels of possibly different chart types) to MAIDR. * * Each entry names a chart type, its binder config, and the panel's DOM * `root` (an element or a CSS selector resolved against `container`). The * matching per-type binder runs against that root and the resulting layer * becomes one MAIDR subplot. * * Pass `subplots` as a 2D array for an explicit row-major grid (ragged rows * allowed), or as a flat array arranged via `layout` / geometry inference — * see {@link D3PanelLayout}. * * @param container - The SVG (or wrapping element) containing all panels. * @param spec - Figure-level fields plus the panel entries. * @returns A {@link D3MultiPanelResult} with the multi-subplot MAIDR data. * * @example * ```ts * const result = bindD3Subplots(svgElement, { * title: 'Sales Overview', * subplots: [[ * { chartType: 'bar', root: 'g.revenue', config: { selector: 'rect.bar', title: 'Revenue' } }, * { chartType: 'line', root: 'g.trend', config: { selector: 'path.line', title: 'Trend' } }, * ]], * }); * ``` */ export declare function bindD3Subplots(container: Element, spec: D3SubplotsConfig): D3MultiPanelResult; /** * Binds a D3.js sunburst to MAIDR. * * A sunburst is the same tree as a treemap drawn in polar coordinates, so the * extraction is that of {@link bindD3Treemap}: `selector` matches the arc * `` elements a `d3.partition()` produced. The trace pans each node by * its angle around the dial, which is what distinguishes a ring from a row of * rectangles by ear. * * What differs in practice is *which* nodes are drawn: a partition lays out * interior nodes as well as leaves, and `root.descendants()` includes the root * itself. Whatever you joined is what the binder emits, so select exactly the * arcs you drew. * * @param svg - The SVG element containing the D3 sunburst. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * const root = d3.partition().size([2 * Math.PI, radius])( * d3.hierarchy(data).sum(d => d.population), * ); * svg.selectAll('path.arc').data(root.descendants().slice(1)).join('path')…; * * bindD3Sunburst(svgElement, { * selector: 'path.arc', * title: 'World population by region', * axes: { x: 'Region', y: 'Population, millions' }, * }); * ``` */ export declare function bindD3Sunburst(svg: Element, config: D3TreemapConfig): D3BinderResult; /** * Binds a D3.js Kaplan-Meier survival curve to MAIDR, generating the * accessible data representation. * * Point `selector` at the step paths — one per arm — exactly as with * {@link bindD3Line}, and `fill` at whatever names the arm. Everything a * survival figure adds is optional and read from the same data: * * - **Censoring.** Either the curve's samples carry a `censored` column, or * the ticks are their own selection — set `censoredSelector` and the binder * merges each tick into its arm by time. * - **The confidence band.** `yMin` and `yMax` are announced alongside the * estimate, which is the comparison a reader makes when two arms look * separated. * * The trace derives median survival itself, so nothing here computes it. * * @remarks * **Timing — call after D3 has rendered.** Like every D3 binder, this reads * each matched element's D3-bound `__data__`; calling it before * `.data().join()` has run (or before the SVG is mounted) throws "No elements * found for selector …". * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 survival curve. * @param config - Configuration specifying selectors and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * const result = bindD3Survival(svgElement, { * selector: 'path.km-curve', * censoredSelector: 'line.censor-tick', * title: 'Overall Survival', * axes: { x: 'Months', y: 'Survival probability', fill: 'Arm' }, * x: 'time', * y: 'surv', * fill: 'arm', * yMin: 'lower', * yMax: 'upper', * }); * ``` */ export declare function bindD3Survival(svg: Element, config: D3SurvivalConfig): D3BinderResult; /** * Binds a D3.js treemap to MAIDR, generating the accessible data representation. * * Point `selector` at the node marks — one `` per leaf for the canonical * `d3.treemap()(root).leaves()` join. Every matched element becomes one point, * in DOM order, and **nothing is filtered**: the interior nodes and their * totals are derived from the paths, so selecting the leaves alone is a * complete tree, and selecting more than you drew would leave the reader * navigating nodes that highlight nothing. * * @remarks * **Timing — call after D3 has rendered.** Like every D3 binder, this reads * each matched element's D3-bound `__data__`; calling it before * `.data().join()` has run (or before the SVG is mounted) throws "No elements * found for selector …". * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 treemap. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * const root = d3.treemap().size([w, h])( * d3.hierarchy(data).sum(d => d.population), * ); * svg.selectAll('rect.leaf').data(root.leaves()).join('rect')…; * * bindD3Treemap(svgElement, { * selector: 'rect.leaf', * title: 'World population by region', * axes: { x: 'Region', y: 'Population, millions' }, * }); * ``` */ export declare function bindD3Treemap(svg: Element, config: D3TreemapConfig): D3BinderResult; /** * Binds a D3.js violin plot to MAIDR, generating the accessible data * representation. * * A violin is `d3.area()` over the KDE bins, mirrored about each category's * centre, so `selector` matches one `` per category with that category's * bin array bound to it — exactly the shape {@link bindD3Line} reads off a line * path. Point `boxSelector` at the box overlay's groups, when the chart draws * one, and the summary is read from them the way {@link bindD3Box} reads a box. * * @remarks * **The KDE is never used to derive the summary.** Quartiles can be computed * off a density curve, and doing so would announce numbers that are not the * data's: a KDE is smoothed, so its quartiles belong to the bandwidth rather * than to the observations, and a reader told "Q1 is 4.2" cannot tell that it * was inferred. A violin drawn without a summary is read as its curves alone. * * **Timing — call after D3 has rendered**, as with every binder here: the * bound `__data__` is what is read, so calling before `.data().join()` throws * "No elements found for selector …". * * @param svg - The SVG element containing the D3 violin plot. * @param config - Configuration specifying selectors and data accessors. * @returns A {@link D3BinderResult} whose `layer` is the KDE and whose `layers` * carries the box summary too, when the chart states one. * * @example * ```ts * bindD3Violin(svgElement, { * selector: 'path.violin', * boxSelector: 'g.box', * title: 'Distribution by Species', * axes: { x: 'Species', y: 'Sepal length' }, * fill: 'species', * value: 'v', * density: 'estimate', * }); * ``` */ export declare function bindD3Violin(svg: Element, config: D3ViolinConfig): D3BinderResult; /** * Binds a D3.js volcano plot to MAIDR. * * A volcano plots effect size against significance, and is read exactly as a * Manhattan plot is: which points clear the line, and what they are called. * The difference is that it has **two** lines — a gene matters when its change * is both large and significant — so `effect` joins `significance`. * * Supply `label`. On a volcano the gene name is the payload: a reader told * "x is 2.3, y is 14.1" has been given the two numbers whose shape they can * already hear, and withheld the one thing they came for. The binder warns * when no label resolves rather than failing, since the chart still reads * without it. * * @param svg - The SVG element containing the D3 volcano plot. * @param config - Configuration specifying the selector, accessors and cutoffs. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * bindD3Volcano(svgElement, { * selector: 'circle.gene', * title: 'Differential Expression', * axes: { x: 'log2 fold change', y: '-log10(p)' }, * x: 'lfc', * y: 'logP', * label: 'gene', * significance: 1.3, * effect: 1, * }); * ``` */ export declare function bindD3Volcano(svg: Element, config: D3VolcanoConfig): D3BinderResult; /** * Binds a D3.js waterfall chart to MAIDR, generating the accessible data * representation. * * Point `selector` at one element per step — the floating `` the bar is * drawn as, or a `` wrapping it and its label. The step's two running * totals are read from the datum: `start` is where the bar's base sits and * `end` where its top does, which is what a waterfall's `y` scale is already * called with. * * **Mark the totals.** An opening, closing or subtotal bar is drawn exactly * like a step but contributes nothing, and no amount of looking at the numbers * reveals which bars those are — so pass a `kind` accessor for them. Without * one they are announced as ordinary increases, and the chart's count of * increases and decreases includes bars that changed nothing. * * @remarks * **Timing — call after D3 has rendered.** Like every D3 binder, this reads * each matched element's D3-bound `__data__`; calling it before * `.data().join()` has run (or before the SVG is mounted) throws "No elements * found for selector …" or "Property '…' not found on datum". * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 waterfall chart. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * const result = bindD3Waterfall(svgElement, { * selector: 'rect.step', * title: 'Quarterly Budget Bridge', * axes: { x: 'Step', y: 'Amount (thousands)' }, * x: 'label', * kind: d => (d.isTotal ? 'total' : undefined), * }); * ``` */ export declare function bindD3Waterfall(svg: Element, config: D3WaterfallConfig): D3BinderResult; /** * Binds a D3.js word cloud to MAIDR, generating the accessible data * representation. * * Point `selector` at the `` elements — one per term. The default * accessors are `d3-cloud`'s own datum keys (`text` and `size`), since the * plugin writes them onto every word it lays out; a cloud drawn from another * shape names its keys through `x` / `y` as usual. * * The trace reads the terms heaviest first regardless of the order they are * emitted in, and matches the glyphs to them by the same permutation — so the * DOM order of the `` elements (which for a cloud is packing order) does * not have to mean anything. * * @remarks * **Timing — call after D3 has rendered.** With `d3-cloud` that means inside * the layout's `on('end', …)` callback: the words are placed asynchronously, * so a binder called straight after `.start()` sees an empty SVG and throws * "No elements found for selector …". * * @see {@link MaidrD3} * @see {@link useD3Adapter} * * @param svg - The SVG element containing the D3 word cloud. * @param config - Configuration specifying the selector and data accessors. * @returns A {@link D3BinderResult} with the MAIDR data and generated layer. * * @example * ```ts * cloud() * .words(terms.map(t => ({ text: t.term, size: t.count }))) * .on('end', words => { * svg.selectAll('text.term').data(words).join('text')…; * bindD3WordCloud(svgElement, { * selector: 'text.term', * title: 'Terms in the Abstracts', * axes: { x: 'Term', y: 'Occurrences' }, * }); * }) * .start(); * ``` */ export declare function bindD3WordCloud(svg: Element, config: D3WordCloudConfig): D3BinderResult; /** * 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; } /** * Configuration for binding a D3 area chart (plain, stacked, or 100% stacked). * * Extends {@link D3LineConfig} because an area is a line with the band under * it filled: `selector` matches one `` per series, and the same * accessors read each sample. * * Supports both common D3 patterns: * * 1. **Plain point arrays** — `d3.area()` over an array of `{ x, y }` rows, * one array bound per `` (or per-point elements via `pointSelector`). * 2. **`d3.stack()` output** — the datum bound to each `` is the series * array itself, carrying `.key`, whose items are `[y0, y1]` tuples with a * `.data` back-reference to the row. The binder recognises that shape and * unwraps it: `x` is read from the row, `y` is the band's own height * (`y1 - y0`), and the series' `.key` becomes its name. * * In that second shape the two accessors address different objects, because * that is where the two values live: `x` is resolved against the **row** — * function accessors included, so write `d => d.year`, not `d => d.data.year` * — while an explicit `y` is resolved against the **tuple**, keeping * `d => d[1] - d[0]` and any custom offset expressible. * * @example * ```ts * // d3.stack() + d3.area().y0(d => y(d[0])).y1(d => y(d[1])) * bindD3Area(svg, { * selector: 'path.area', * type: TraceType.STACKED_AREA, * axes: { x: 'Year', y: 'Revenue', fill: 'Product' }, * x: 'year', // a key on the stacked row, not on the [y0, y1] tuple * }); * ``` */ export declare interface D3AreaConfig extends D3LineConfig { /** The type of area chart. @default TraceType.AREA */ type?: AreaTraceType; } /** * A single axis spec for D3 binder input. Accepts either a plain string * (shorthand for `{ label: value }`) or a full {@link AxisConfig} object * for advanced cases (per-axis `format`, grid navigation for scatter). */ declare type D3AxisInput = string | AxisConfig; /** * Configuration for binding a D3 bar chart. * * Also the config for the other three bar-family marks — {@link bindD3Dot}, * {@link bindD3Lollipop} and {@link bindD3Funnel} — which read the same * `{ category, value }` datum off a different element. */ export declare interface D3BarConfig extends D3BinderConfig { /** CSS selector for the bar elements (e.g., `'rect.bar'`, `'rect'`, `'path'`). */ selector: string; /** Accessor for the x-axis (category) value. @default 'x' */ x?: DataAccessor; /** Accessor for the y-axis (numeric) value. @default 'y' */ y?: DataAccessor; /** Chart orientation. @default Orientation.VERTICAL */ orientation?: Orientation; } /** * Common configuration shared across all D3 chart binders. */ export declare interface D3BinderConfig { /** Unique identifier for the chart. Used as the MAIDR `id`. */ id?: string; /** Chart title displayed in text descriptions. */ title?: string; /** Chart subtitle. */ subtitle?: string; /** Chart caption. */ caption?: string; /** * Axis configuration. Each axis may be provided as either a plain string * (shorthand for `{ label: value }`) or a full {@link AxisConfig} object * (for per-axis `format`, or grid navigation on scatter). * * For heatmaps and segmented bar charts, use `fill` for the color/category * axis; the binder maps it to the canonical `z` axis in the MAIDR schema. */ axes?: { x?: D3AxisInput; y?: D3AxisInput; /** Fill/color axis for heatmaps and segmented bars. Maps to `z` internally. */ fill?: D3AxisInput; }; /** * Optional formatting configuration applied to axes that do not specify * their own `format`. Per-axis `format` on `AxisConfig` takes precedence. */ format?: AxisFormat; /** * When `true` (the default), the binder writes the generated MAIDR schema * to the SVG as a `maidr-data` attribute so vanilla-JS users don't need * to call `svg.setAttribute(...)` themselves. The returned result is * unchanged either way. * * Set to `false` if you are driving MAIDR yourself — e.g. passing the * returned schema to `` or persisting it elsewhere. * The React adapter ({@link useD3Adapter}, {@link MaidrD3}) forces this * to `false` internally so it can stay in control of the schema. * * @default true */ autoApply?: boolean; } /** * Result of a D3 binder function. * Contains the complete MAIDR data structure and the generated layer * for further customization if needed. */ export declare interface D3BinderResult { /** Complete MAIDR JSON data ready to use with the `` component or `maidr-data` attribute. */ maidr: MaidrData; /** * The layer the binder is named for, for direct inspection or modification. * * A binder that emits more than one — a violin, which is a KDE curve plus a * box summary — puts the rest in {@link layers}; this stays the primary one, * so a caller written before that existed still reads what it expected. */ layer: MaidrLayer; /** * Every layer the bind produced, in the order the subplot carries them. * * Always populated, and `[layer]` for the binders that emit one. Read this * rather than {@link layer} when what you want is "what did this bind * produce" rather than "what is this chart". */ layers: MaidrLayer[]; } /** * Configuration for binding a D3 box plot. */ export declare interface D3BoxConfig extends D3BinderConfig { /** * CSS selector for the box group elements. Each matched element should * represent one box (e.g., `'g.box'`). */ selector: string; /** Selector for the IQR box rectangle within each box group. @default 'rect' */ boxSelector?: string; /** Selector for the median line within each box group. @default 'line.median' */ medianSelector?: string; /** Selector for the whisker lines within each box group. */ whiskerSelector?: string; /** Selector for outlier points within each box group. @default 'circle' */ outlierSelector?: string; /** Accessor for the group/fill label. @default 'fill' */ fill?: DataAccessor; /** Accessor for the min value. @default 'min' */ min?: DataAccessor; /** Accessor for q1 value. @default 'q1' */ q1?: DataAccessor; /** Accessor for median (q2) value. @default 'q2' */ q2?: DataAccessor; /** Accessor for q3 value. @default 'q3' */ q3?: DataAccessor; /** Accessor for the max value. @default 'max' */ max?: DataAccessor; /** Accessor for lower outlier values. @default 'lowerOutliers' */ lowerOutliers?: DataAccessor; /** Accessor for upper outlier values. @default 'upperOutliers' */ upperOutliers?: DataAccessor; /** Chart orientation. @default Orientation.VERTICAL */ orientation?: Orientation; } /** * Configuration for binding a D3 boxen (letter-value) plot. * * Point `selector` at one element per distribution — the `` holding the * stack of nested rungs — the way {@link D3BoxConfig} points at a box group. * Every rung of a distribution highlights that whole group, because a chart * does not draw an element per quantile that MAIDR could pair up positionally. * * The ladder is read from the datum rather than measured off the rungs: a * letter-value plot computes its quantiles before it draws them, and a height * in pixels is a layout fact rather than a quantile. * * @example * ```ts * bindD3Boxen(svgElement, { * selector: 'g.boxen', * axes: { x: 'Group', y: 'Milliseconds' }, * x: 'group', * median: 'median', * levels: 'letterValues', * }); * ``` */ export declare interface D3BoxenConfig extends D3BinderConfig { /** CSS selector for the per-distribution elements (e.g. `'g.boxen'`). */ selector: string; /** * Accessor for the category the distribution summarises. @default 'x', * falling back to `z`, `category`, `label`, `name`, `key`, or `group`. */ x?: DataAccessor; /** * Accessor for the middle of the distribution. @default 'median', falling * back to `q2`, `mid`, or `y`. */ median?: DataAccessor; /** * Accessor for the ladder of quantile pairs — one entry per rung, each * carrying the tail probability `p` and the pair of quantiles `lo` / `hi` * (`lower` / `upper` are accepted too). * * @default 'levels', falling back to `letterValues`, `letter_values`, * `quantiles`, or `ladder`. A rung whose three numbers are not all finite is * dropped rather than announced as a quantile the data does not contain. */ levels?: DataAccessor; /** Accessor for values below the deepest rung. @default 'lowerOutliers' */ lowerOutliers?: DataAccessor; /** Accessor for values above the deepest rung. @default 'upperOutliers' */ upperOutliers?: DataAccessor; /** Chart orientation. @default Orientation.VERTICAL */ orientation?: Orientation; } /** * Configuration for binding a D3 candlestick chart. */ export declare interface D3CandlestickConfig extends D3BinderConfig { /** CSS selector for the candlestick body elements (e.g., `'rect.candle'`). */ selector: string; /** Accessor for the label/date value. @default 'value' */ value?: DataAccessor; /** Accessor for the open price. @default 'open' */ open?: DataAccessor; /** Accessor for the high price. @default 'high' */ high?: DataAccessor; /** Accessor for the low price. @default 'low' */ low?: DataAccessor; /** Accessor for the close price. @default 'close' */ close?: DataAccessor; /** Accessor for the trading volume. @default 'volume' */ volume?: DataAccessor; /** Accessor for the trend direction. Auto-computed from open/close if not provided. */ trend?: DataAccessor<'Bull' | 'Bear' | 'Neutral'>; } /** * Configuration for binding a D3 choropleth map. * * A choropleth is `d3.geoPath()` over a projection: one `` per region, * each bound to the GeoJSON feature it was drawn from. So `selector` matches * the region paths, and a **string accessor names a key on the feature or on * its `properties`**, in that order — a feature keeps only `type`, `id`, * `geometry` and `properties` at the top level, so the joined value and the * place name are in `properties` on almost every map. A function accessor is * invoked with the whole feature. * * **`lon` and `lat` are degrees, and the wrong call gives pixels.** * `d3.geoPath().centroid(feature)` returns the centre of the drawn shape in * projected screen coordinates; `d3.geoCentroid(feature)` returns the * unprojected longitude/latitude pair, and that is the one to read: * * ```ts * lon: d => d3.geoCentroid(d)[0], * lat: d => d3.geoCentroid(d)[1], * ``` * * Where only a projection is to hand, `projection.invert([px, py])` inverts * the pixels — but `invert` is optional in d3's projection API and several * projections do not implement it. When neither yields degrees, leave both * out: a coordinate outside ±180°/±90° is dropped rather than converted by * guesswork, and the map is then read as a region list in drawn order, which * is the poorer reading the grammar sanctions. A wrong pair is worse, because * it puts regions in directions from one another that the map does not. * * `neighbors` is not read: adjacency is not recoverable from rendered paths, * and deriving it needs shared-border topology this repository has no * dependency for. A layer that declares none keeps the spatial walk. */ export declare interface D3ChoroplethConfig extends D3BinderConfig { /** CSS selector for the region paths (e.g. `'path.region'`). One per region. */ selector: string; /** * Accessor for the region's name. * @default 'region', falling back to `name`, `NAME`, `name_long`, `admin`, * `state`, `id`, `label` or `x` — on the feature or in its `properties`. */ region?: DataAccessor; /** * Accessor for the value the region is shaded by. A region this resolves * nothing for is left out of the payload — and out of the highlight * selectors with it — rather than announced as a zero. * @default 'value', falling back to `y`, `rate`, `density` or `count`. */ value?: DataAccessor; /** * Accessor for the region's centroid longitude, in **degrees east**. * `d3.geoCentroid(d)[0]`, never `d3.geoPath().centroid(d)[0]`. * @default 'lon', falling back to `longitude` or `long`. */ lon?: DataAccessor; /** * Accessor for the region's centroid latitude, in **degrees north**. * `d3.geoCentroid(d)[1]`, never `d3.geoPath().centroid(d)[1]`. * @default 'lat', falling back to `latitude`. */ lat?: DataAccessor; } /** * Configuration for binding a D3 contour plot. * * `d3.contours()` and `d3.contourDensity()` emit one GeoJSON `MultiPolygon` * per threshold, carrying the threshold as `.value`, so `selector` matches one * `` per level and the layer's rows are the levels. * * **The coordinates are not in data space.** `d3.contours()` emits grid * indices and `d3.contourDensity()` emits pixels, so `x` and `y` are the * transforms back onto the axes — `x: i => x0 + i * dx` for the former, * `x: px => xScale.invert(px)` for the latter. Left out, the chart announces * its positions in grid cells or screen pixels. * * A level drawn as several disjoint rings is flattened into one curve, in the * order the rings appear, since a row of the payload is a single polyline. * Every point announced is a real point of the level; what a reader cannot * hear is the jump from the end of one ring to the start of the next. */ export declare interface D3ContourConfig extends D3BinderConfig { /** CSS selector for the level paths (e.g. `'path.contour'`). One per level. */ selector: string; /** Accessor for the level's value. @default 'value', falling back to `level`, `threshold` or `z`. */ level?: DataAccessor; /** Accessor for the GeoJSON rings. @default 'coordinates'. */ coordinates?: DataAccessor; /** Maps a grid x onto the x axis. @default identity. */ x?: D3GridTransform; /** Maps a grid y onto the y axis. @default identity. */ y?: D3GridTransform; } /** * Configuration for binding a D3 dumbbell (connected-dot) chart. * * Point `selector` at the **connectors** — one `` per row — rather than * at the dots: a chart draws one segment per row and two dots, so the * connectors are the elements that map one-to-one onto the data, and the * trace highlights the same segment at both ends of a row. * * `startLabel` / `endLabel` are config rather than accessors because they * belong to the chart and not to any one row — they are what the two dots are * called ("1990" and "2020"), which is exactly what a legend gives a sighted * reader and what the announcement would otherwise have to call "start" and * "end". * * @example * ```ts * bindD3Dumbbell(svgElement, { * selector: 'line.connector', * orientation: Orientation.HORIZONTAL, * axes: { x: 'Years', y: 'Country' }, * x: 'country', * start: 'y1990', * end: 'y2020', * startLabel: '1990', * endLabel: '2020', * }); * ``` */ export declare interface D3DumbbellConfig extends D3BinderConfig { /** CSS selector for the connector elements (e.g. `'line.connector'`). */ selector: string; /** Accessor for the category value. @default 'x' */ x?: DataAccessor; /** * Accessor for the value the segment starts at. @default 'start', * falling back to `from`, `before`, or `y0`. */ start?: DataAccessor; /** * Accessor for the value the segment ends at. @default 'end', * falling back to `to`, `after`, or `y1`. */ end?: DataAccessor; /** What the starting end is called — `'1990'`, `'before'`, `'control'`. */ startLabel?: string; /** What the finishing end is called — `'2020'`, `'after'`, `'treatment'`. */ endLabel?: string; /** Chart orientation. @default Orientation.VERTICAL */ orientation?: Orientation; } /** * Configuration for binding a D3 error-bar (point-range) chart. * * The canonical D3 idiom is one `` per estimate holding a `` for the * interval and a marker for the estimate itself, so point `selector` at those * groups — one matched element per estimate, whichever mark carries it. * * The bounds are **absolute positions** on the value axis, not half-widths. * That is the one conversion this binder cannot do for you: a datum carrying * `±se` needs a function accessor (`yMin: d => d.mean - d.se`), because the * binder has no way to tell an offset from a bound by looking at it. * * @example * ```ts * bindD3ErrorBar(svgElement, { * selector: 'g.estimate', * axes: { x: 'Group', y: 'Response' }, * x: 'group', * y: 'mean', * yMin: d => d.mean - 1.96 * d.se, * yMax: d => d.mean + 1.96 * d.se, * }); * ``` */ export declare interface D3ErrorBarConfig extends D3BinderConfig { /** CSS selector for the per-estimate elements (e.g. `'g.estimate'`). */ selector: string; /** Accessor for the x-axis (category) value. @default 'x' */ x?: DataAccessor; /** * Accessor for the estimate itself. @default 'y', falling back to `value`, * `mean`, `estimate`, or `median`. */ y?: DataAccessor; /** * Accessor for the interval's absolute lower bound. @default 'yMin', * falling back to `lower`, `ciLow`, `ci_low`, `low`, or `min`. Omitted from * the payload when the datum carries none of them — a one-sided interval is * a real chart. */ yMin?: DataAccessor; /** * Accessor for the interval's absolute upper bound. @default 'yMax', * falling back to `upper`, `ciHigh`, `ci_high`, `high`, or `max`. */ yMax?: DataAccessor; /** Chart orientation. @default Orientation.VERTICAL */ orientation?: Orientation; } /** * Configuration for {@link bindD3Facets} — homogeneous small multiples * (one chart type repeated across panels inside a single SVG). * * The `chartType` / `config` pair selects the per-panel binder; the inner * `config` also carries the figure-level fields (`id`, `title`, `subtitle`, * `caption`, `autoApply`). Each matched panel element becomes the extraction * root for the per-type binder, so `config.selector` is resolved *within* * each panel. */ export declare type D3FacetsConfig = D3PanelChartSpec & { /** * CSS selector for the panel container elements inside the SVG — the * canonical D3 facet idiom is one translated `` per panel (e.g. * `'g.panel'`). Each match becomes one MAIDR subplot. */ panelSelector: string; /** * Accessor for each panel's display title, resolved against the panel * element's D3-bound `__data__` (for `d3.groups` output, the `[key, * values]` tuple — pass `d => d[0]` or rely on the automatic key * detection). Function accessors receive `(datum, index)` and are invoked * even when the panel has no bound datum (`datum` is then `undefined`), * so index-only accessors like `(_d, i) => keys[i]` work for panels * appended without a data join; string-key accessors and the automatic * key detection require a bound datum. Falls back to `Panel ` when * unresolvable. */ panelTitle?: DataAccessor; /** Explicit grid layout. When omitted, inferred from panel geometry. */ layout?: D3PanelLayout; }; /** * Configuration for binding a D3 sankey, alluvial or chord diagram. * * Point `selector` at the **ribbons** — one `` per link — rather than at * the node rectangles: the nodes are derived from the links exactly as a * network's are, so a link is what maps one-to-one onto the payload. * * `d3-sankey` **replaces** each link's `source` and `target` with the node * objects it resolved them to, the way `d3.forceLink` does, so an object end is * read through its `id`, `name`, `key` or `label`. `d3.chord()` is the one that * needs help: its ends are the matrix's row and column **indices**, which is * what {@link D3FlowConfig.names} is for. * * @example * ```ts * bindD3Sankey(svgElement, { * selector: 'path.ribbon', * axes: { x: 'Node', y: 'Petajoules' }, * }); * ``` */ export declare interface D3FlowConfig extends D3BinderConfig { /** CSS selector for the ribbon elements (e.g. `'path.ribbon'`). */ selector: string; /** * Accessor for the node a flow leaves. @default 'source', falling back to * `from` or `src`. An object end is named through its `id`, `name`, `key` or * `label`, or — for a chord — through {@link D3FlowConfig.names}. */ source?: DataAccessor; /** * Accessor for the node a flow arrives at. @default 'target', falling back * to `to` or `dst`. Named the same way as {@link D3FlowConfig.source}. */ target?: DataAccessor; /** * Accessor for how much flows. @default 'value', falling back to `weight`, * `amount`, `count`, or `y`. When the datum carries none of them, the * magnitude `d3.chord()` put on each end (`d.source.value`) is used, which * is what the ribbon's width was drawn from. */ value?: DataAccessor; /** * What the matrix's rows are called, in matrix order — the labels a chord * diagram draws around the dial. * * `d3.chord()` binds `{ index, value, … }` to each end rather than a name, * because a matrix has no names in it. Without this a chord announces its * ends as the bare indices they are; with it, the reader is told which * groups the ribbon joins. */ names?: (string | number)[]; } /** * Configuration for binding a D3 forest plot. * * Extends {@link D3ErrorBarConfig} because a forest plot *is* a point-range * chart: one row per study, an estimate and an interval read the same way. What * it adds is the part a sighted reader takes from the drawing — how much each * study weighs, which row is the pooled summary, and where the null line sits. * * The pooled row is usually a differently-shaped mark (a diamond ``, not * a whip), so it is selected separately with `pooledSelector` and appended * after the studies. A chart that draws every row alike can instead mark it * with the `pooled` accessor. * * @example * ```ts * bindD3Forest(svgElement, { * selector: 'g.study', * pooledSelector: 'path.pooled', * orientation: Orientation.HORIZONTAL, * axes: { x: 'Odds ratio', y: 'Study' }, * x: 'study', * y: 'or', * yMin: 'ciLow', * yMax: 'ciHigh', * weight: 'weight', * nullValue: 1, * }); * ``` */ export declare interface D3ForestConfig extends D3ErrorBarConfig { /** * Accessor for the study's weight in the pooled estimate, as a fraction of * one. @default 'weight', falling back to `w` or `share`. Omitted from the * payload when the datum carries none of them — a forest plot without * weights is a real chart. */ weight?: DataAccessor; /** * Accessor marking a row as the pooled summary rather than a study. * @default 'pooled', falling back to `isPooled` or `summary`. Every row * matched by `pooledSelector` is pooled regardless. */ pooled?: DataAccessor; /** * CSS selector for the pooled summary's own mark, when it is drawn * differently from the studies — the diamond a meta-analysis ends with. * Its rows are appended after the studies, in the order they are drawn. */ pooledSelector?: string; /** * The value that means "no effect" — 1 for a ratio measure, 0 for a * difference. * * Whether a study's 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, which is a * confident wrong answer given to every row. */ nullValue?: number; } /** * Configuration for binding a D3 gantt (timeline, swimlane) chart. * * Point `selector` at the interval marks — one `` per booked interval on * a band scale of lanes. The binder groups them into lanes itself: the payload * is nested by lane, and the DOM order a chart happens to draw in is not that * grouping. * * **Dates are coerced to epoch milliseconds.** A `Date` or a date string is * turned into a number so the trace can measure lengths at all; pair it with * `format: { type: 'date' }` so the ends are announced as dates rather than as * timestamps. * * @example * ```ts * bindD3Gantt(svgElement, { * selector: 'rect.task', * axes: { x: 'Day', y: 'Phase' }, * x: 'phase', * start: 'from', * end: 'to', * label: 'task', * lanes: ['Design', 'Build', 'Review', 'Launch'], * unit: 'days', * }); * ``` */ export declare interface D3GanttConfig extends D3BinderConfig { /** CSS selector for the interval elements (e.g. `'rect.task'`). */ selector: string; /** * Accessor for the lane an interval belongs to. @default 'x', falling back * to `lane`, `category`, `label`, `name`, `key`, `group`, or `task`. */ x?: DataAccessor; /** * Accessor for where the interval begins. @default 'start', falling back to * `from`, `begin`, `x0`, or `startDate`. */ start?: DataAccessor; /** * Accessor for where the interval ends. @default 'end', falling back to * `to`, `finish`, `x1`, or `endDate`. */ end?: DataAccessor; /** * Accessor for what the interval is called, when the lane is not already its * name. @default 'label', falling back to `name`, `task`, `title`, or * `activity`. Omitted from the payload when the datum carries none of them. */ label?: DataAccessor; /** * The lanes, in the order the chart draws them. * * Needed only for **empty** lanes: a lane with nothing booked has no element * in the DOM at all, so the binder cannot discover it, and an empty row is a * real statement about a schedule. Lanes carrying intervals name themselves * and need no entry here; any the binder finds and this does not declare are * appended in the order they were drawn. */ lanes?: (string | number)[]; /** What a unit of the axis is called — `'days'`, `'hours'`, `'weeks'`. */ unit?: string; /** * Chart orientation. @default Orientation.HORIZONTAL — a gantt drawn the * ordinary way runs its bars left to right, which puts the axis on x and the * lanes on y. Pass `Orientation.VERTICAL` for a schedule drawn as columns. */ orientation?: Orientation; } /** * Configuration for binding a D3 gauge or bullet chart. * * A drawn gauge binds only the measure — the dial's range, the target marker * and the qualitative bands are drawn from numbers the author holds and the * DOM does not carry, which is why they are config rather than accessors. * They are also the whole reading: "73" means nothing without the range it * sits in, the target it was aiming at, and the band it lands in. * * Point `selector` at the needle, the value arc, or the bullet's measure bar * — the mark that moves with the value. * * @example * ```ts * bindD3Gauge(svgElement, { * selector: 'rect.measure', * axes: { x: 'Measure', y: 'Percent' }, * label: 'Conversion', * min: 0, * max: 100, * target: 80, * bands: [{ to: 50, label: 'poor' }, { to: 75, label: 'ok' }, { to: 100, label: 'good' }], * }); * ``` */ export declare interface D3GaugeConfig extends D3BinderConfig { /** CSS selector for the needle or value arc (e.g. `'path.needle'`). */ selector: string; /** * Accessor for the measure. @default 'value', falling back to `y`, * `amount`, `measure`, `current`, or `actual`. A datum that is a bare * number is the measure itself. */ value?: DataAccessor; /** Lower end of the dial. */ min: number; /** Upper end of the dial. */ max: number; /** What the measure is called — `'Conversion'`. */ label?: string; /** The target marker a bullet chart draws, when it has one. */ target?: number; /** Qualitative bands, in ascending order. */ bands?: GaugeBand[]; } /** * Maps one coordinate of a contour's grid onto the data axis it stands for. * * Not a {@link DataAccessor}: the input is a single number from a coordinate * pair, not a bound datum, and there is no element index to pass. */ export declare type D3GridTransform = (gridCoordinate: number) => number; /** * Configuration for binding a D3 heatmap. */ export declare interface D3HeatmapConfig extends D3BinderConfig { /** CSS selector for the cell elements (e.g., `'rect.cell'`, `'rect'`). */ selector: string; /** Accessor for the x-axis category value. @default 'x' */ x?: DataAccessor; /** Accessor for the y-axis category value. @default 'y' */ y?: DataAccessor; /** Accessor for the cell value. @default 'value' */ value?: DataAccessor; /** * The row labels **top-first**, in the order the chart draws them. * * Supply it whenever the join does not already iterate the rows top-down: * usually `yScale.domain()`, or its reverse for a band scale whose domain * ascends up the page. Without it the rows are taken in order of appearance * in the DOM, which is the order the join happened to run in and need not be * the order anything is drawn in (#978). * * Getting it wrong is silent — a matrix of numbers looks the same either way * up — and what goes wrong is the reader's model of the chart rather than any * value: walks *down* a chart whose rows arrived bottom-first, * and the cursor enters at the top corner instead of the bottom. * * Labels the cells do not carry are ignored, and an order that does not name * every row is declined in favour of appearance order rather than dropping a * row the chart draws. */ yOrder?: string[]; /** * The column labels in the order the chart draws them, left first — * usually `xScale.domain()`, or its reverse for a band scale that runs the * other way. Without it the columns are taken in order of appearance in the * DOM, which is the order the join happened to run in and need not be the * order anything is drawn in (#1013). * * The same reasoning as {@link D3HeatmapConfig.yOrder}, and the same silence * when it is wrong — what suffers is the reader's model of the chart rather * than any value, with walking left along a grid whose columns * arrived right-first. * * Labels the cells do not carry are ignored, and an order that does not name * every column is declined in favour of appearance order rather than * dropping a column the chart draws. */ xOrder?: string[]; } /** * Configuration for binding a D3 hexbin density plot. * * The `d3-hexbin` plugin returns one bin per occupied hexagon, and each bin is * an **array** of the points that fell in it, carrying `.x` and `.y` (the * hexagon's centre, in SCREEN space) and `.length` (the count). So the default * accessors read `x`, `y` and `length` off the bin, and `x`/`y` are where the * inverse scales go: `x: d => xScale.invert(d.x)`. Passing the screen * coordinates through unchanged would announce every bin's position in pixels. * * The payload is a lattice of rows, which the binder assembles itself: a * hexbin's DOM is a flat list in whatever order the bins were generated, and * an empty bin is simply absent from it. Rows are grouped by the bins' `y` * (override with `row` when the y values do not come out identical per row), * ordered from the lowest upward, and each row is ordered left to right. */ export declare interface D3HexbinConfig extends D3BinderConfig { /** CSS selector for the hexagons (e.g. `'path.hexagon'`). One per bin. */ selector: string; /** Accessor for the bin's centre along the x axis. @default 'x', falling back to `x0` or `cx`. */ x?: DataAccessor; /** Accessor for the bin's centre along the y axis. @default 'y', falling back to `y0` or `cy`. */ y?: DataAccessor; /** Accessor for how many points fell in the bin. @default 'count', falling back to `length`, `value`, `n` or `total`. */ count?: DataAccessor; /** * Accessor for the lattice row a bin belongs to. Supply this only when the * bins' `y` centres do not come out identical within a row — `d3-hexbin`'s * do, so the default grouping by `y` is normally right. */ row?: DataAccessor; } /** * Configuration for binding a D3 histogram. */ export declare interface D3HistogramConfig extends D3BinderConfig { /** CSS selector for the histogram bar elements (e.g., `'rect.bar'`, `'rect'`). */ selector: string; /** Accessor for the x-axis (bin label) value. @default 'x' */ x?: DataAccessor; /** Accessor for the y-axis (count/frequency) value. @default 'y' */ y?: DataAccessor; /** Accessor for bin min x value. @default 'x0' */ xMin?: DataAccessor; /** Accessor for bin max x value. @default 'x1' */ xMax?: DataAccessor; /** Accessor for bin min y value (typically 0). @default 0 */ yMin?: DataAccessor; /** Accessor for bin max y value. Defaults to the y accessor. */ yMax?: DataAccessor; } /** * Configuration for binding a D3 line chart. */ export declare interface D3LineConfig extends D3BinderConfig { /** * CSS selector for the line path elements (e.g., `'path.line'`, `'.line'`). * Each matched element represents one line/series. */ selector: string; /** * CSS selector for the data point elements per line (e.g., `'circle'`). * If not provided, data is extracted from the line path `__data__` binding. */ pointSelector?: string; /** Accessor for the x-axis value of each point. @default 'x' */ x?: DataAccessor; /** Accessor for the y-axis value of each point. @default 'y' */ y?: DataAccessor; /** Accessor for the series/fill label. @default 'fill' */ fill?: DataAccessor; /** * Which way the risers of a step curve go, when the line is drawn as one. * * `'hv'` holds the level and jumps at the next sample, which is * `d3.curveStepAfter`; `'vh'` jumps at the current one, `d3.curveStepBefore`; * `'mid'` jumps halfway between the two, `d3.curveStep`. Declaring it is what * turns a `bindD3Line` bind into a step reading — navigated by transition * rather than by sample, and described in terms of its runs — and what tells * an area's trace that the extra vertices in the rendered path are risers * rather than samples. * * Left undefined by default rather than guessed, because this binder reads * its data off `__data__` and never looks at the path: which curve you drew * is your own knowledge, not something the drawing gives back. All three of * d3's step curves have a name here, so leaving it out is a caller saying * nothing rather than a convention MAIDR cannot express. */ stepDirection?: StepDirection; } /** * Configuration for binding a D3 Manhattan plot. * * Extends {@link D3ScatterConfig} because the marks are the same: one element * per point, with `x` the genomic position and `y` the transformed p-value. * What it adds is the part of the chart a sighted reader takes from the labels * and the colours — which SNP a point is, which chromosome it sits on, and * where the significance line was drawn. * * @example * ```ts * bindD3Manhattan(svgElement, { * selector: 'circle.snp', * axes: { x: 'Position', y: '-log10(p)', fill: 'Chromosome' }, * x: 'pos', * y: 'logP', * label: 'snp', * group: 'chromosome', * significance: 7.3, * }); * ``` */ export declare interface D3ManhattanConfig extends D3ScatterConfig { /** * Accessor for what each point *is* — a SNP id, a probe, a marker. * @default 'label', falling back to `snp`, `id`, `name`, `gene`, or `probe`. * Left out of the payload when the datum carries none of them. */ label?: DataAccessor; /** * Accessor for the region a point belongs to — its chromosome. * @default 'group', falling back to `chromosome`, `chrom`, `chr`, or * `region`. Left out of the payload when the datum carries none of them. */ group?: DataAccessor; /** * The significance cutoff on the y axis, on the axis the chart is drawn * against — 7.3 for genome-wide significance on a `-log10(p)` axis. * * There is deliberately no default: the conventions differ by field and by * software, and a guessed line would sort every point onto the wrong side * silently. Omit it and the trace simply reports no findings. */ significance?: number; /** * Which side of `significance` is the significant one. `'above'` (the * default) suits the transformed axes these charts usually carry; a **raw p * axis runs the other way** and needs `'below'`. */ significanceDirection?: 'above' | 'below'; } /** * Configuration for binding a D3 mosaic (marimekko) plot. * * Extends {@link D3SegmentedConfig} because a mosaic *is* a stacked bar: same * `` per cell, same `{ x, y, fill }` extraction, same DOM-order * detection. What it adds is the column **width**, which on every other chart * is how the bars were drawn and here is the second magnitude the plot exists * to show — a category of six people and one of six hundred read identically * without it. * * The width is read from the datum, never measured off the rendered ``: * a drawn width is a layout fact (padding, margins, a log scale) and turning * it back into a proportion would announce a number the data does not contain. * * @example * ```ts * bindD3Mosaic(svgElement, { * selector: 'rect.cell', * axes: { x: 'Class', y: 'Proportion', fill: 'Outcome' }, * x: 'klass', * y: 'share', * fill: 'outcome', * width: 'columnShare', * count: 'n', * }); * ``` */ export declare interface D3MosaicConfig extends D3SegmentedConfig { /** * Accessor for the column's share of all observations, as a fraction of one. * @default 'width', falling back to `share`, `proportion`, or `marginal`. * Omitted from the payload when the datum carries none of them, and when the * value read is not a finite number. */ width?: DataAccessor; /** * Accessor for the cell's own count, when the producer has the contingency * table the mosaic was drawn from. * @default 'count', falling back to `n`, `freq`, or `frequency`. Omitted * from the payload when absent — a count multiplied out of a rounded share * is a number the data does not contain. */ count?: DataAccessor; } /** * Result of a multi-panel D3 binder ({@link bindD3Facets}, * {@link bindD3Subplots}). */ export declare interface D3MultiPanelResult { /** Complete multi-subplot MAIDR JSON data. */ maidr: MaidrData; /** One generated layer per panel, in row-major (visual reading) order. */ layers: MaidrLayer[]; } /** * Configuration for binding a D3 force-directed network. * * Point `selector` at the **links** — one `` per edge — rather than at * the node circles: the nodes are derived from the links, so a link is what * maps one-to-one onto the payload, and it is what the chart draws between a * pair of nodes. * * Positions are deliberately not read. Where a force-directed node lands is a * fact about the solver's seed rather than about the data. * * @example * ```ts * bindD3Network(svgElement, { * selector: 'line.link', * axes: { x: 'Person', y: 'Links' }, * }); * ``` */ export declare interface D3NetworkConfig extends D3BinderConfig { /** CSS selector for the link elements (e.g. `'line.link'`). */ selector: string; /** * Accessor for the node a link leaves. @default 'source', falling back to * `from` or `src`. * * `d3.forceLink` **replaces** each link's `source` with the node object it * resolved the id to, so the resolved value is normalised either way: an * object end is read through its `id`, `name`, `key` or `label`. */ source?: DataAccessor; /** * Accessor for the node a link arrives at. @default 'target', falling back * to `to` or `dst`. Normalised the same way as {@link D3NetworkConfig.source}. */ target?: DataAccessor; } /** * Discriminated union pairing a chart type with its binder-specific config. * This is the per-panel unit consumed by the multi-panel binders and the * base of the React adapter's {@link D3AdapterSpec}. */ export declare type D3PanelChartSpec = { chartType: 'alluvial'; config: D3FlowConfig; } | { chartType: 'area'; config: D3AreaConfig; } | { chartType: 'bar'; config: D3BarConfig; } | { chartType: 'box'; config: D3BoxConfig; } | { chartType: 'boxen'; config: D3BoxenConfig; } | { chartType: 'bump'; config: D3LineConfig; } | { chartType: 'candlestick'; config: D3CandlestickConfig; } | { chartType: 'chord'; config: D3FlowConfig; } | { chartType: 'choropleth'; config: D3ChoroplethConfig; } | { chartType: 'contour'; config: D3ContourConfig; } | { chartType: 'diverging'; config: D3SegmentedConfig; } | { chartType: 'dot'; config: D3BarConfig; } | { chartType: 'dumbbell'; config: D3DumbbellConfig; } | { chartType: 'errorBar'; config: D3ErrorBarConfig; } | { chartType: 'forest'; config: D3ForestConfig; } | { chartType: 'funnel'; config: D3BarConfig; } | { chartType: 'gantt'; config: D3GanttConfig; } | { chartType: 'gauge'; config: D3GaugeConfig; } | { chartType: 'heatmap'; config: D3HeatmapConfig; } | { chartType: 'hexbin'; config: D3HexbinConfig; } | { chartType: 'histogram'; config: D3HistogramConfig; } | { chartType: 'icicle'; config: D3TreemapConfig; } | { chartType: 'line'; config: D3LineConfig; } | { chartType: 'lollipop'; config: D3BarConfig; } | { chartType: 'manhattan'; config: D3ManhattanConfig; } | { chartType: 'mosaic'; config: D3MosaicConfig; } | { chartType: 'network'; config: D3NetworkConfig; } | { chartType: 'parallel'; config: D3ParallelConfig; } | { chartType: 'pie'; config: D3PieConfig; } | { chartType: 'polarArea'; config: D3PolarAreaConfig; } | { chartType: 'radar'; config: D3LineConfig; } | { chartType: 'ridgeline'; config: D3RidgelineConfig; } | { chartType: 'sankey'; config: D3FlowConfig; } | { chartType: 'scatter'; config: D3ScatterConfig; } | { chartType: 'segmented'; config: D3SegmentedConfig; } | { chartType: 'smooth'; config: D3SmoothConfig; } | { chartType: 'sunburst'; config: D3TreemapConfig; } | { chartType: 'survival'; config: D3SurvivalConfig; } | { chartType: 'treemap'; config: D3TreemapConfig; } | { chartType: 'volcano'; config: D3VolcanoConfig; } | { chartType: 'waterfall'; config: D3WaterfallConfig; } | { chartType: 'wordCloud'; config: D3WordCloudConfig; }; /** * Grid layout hint for multi-panel binds. * * - `'row'` — all panels in a single row (side by side). * - `'column'` — all panels in a single column (stacked). * - `{ rows?, columns? }` — chunk panels into a grid with the given number of * columns (or `ceil(count / rows)` columns when only `rows` is set). The * last row may be shorter (ragged grids are supported). * * When omitted, the binders infer the grid from panel geometry: panel * bounding-box centers are clustered by y (rows) and sorted by x within each * row, falling back to parsing `transform="translate(x,y)"` when bounding * boxes are unavailable (e.g. jsdom), and finally to a single row in DOM * order. An explicit `layout` always wins over geometry. */ export declare type D3PanelLayout = 'row' | 'column' | { rows?: number; columns?: number; }; /** * Configuration for binding a D3 parallel coordinates plot. * * The chart draws one `` (or ``) per **observation** across * several per-variable scales, and the datum bound to it is that observation * as a whole — `{ mpg: 21, hp: 110, weight: 2600 }`. The layer's rows are the * observations and its columns are the axes, so the binder transposes: for * each observation it emits one point per entry of `dimensions`, whose `x` is * the axis' name and whose `y` is that observation's value on it. * * `dimensions` is required, and is the same list the chart already built one * scale per: the order is the order the axes are drawn in, which is the order * a reader arrows through them. Nothing on the datum says it — an object's key * order is not an axis order — and a guessed one would announce the chart's * columns in the wrong places. */ export declare interface D3ParallelConfig extends D3BinderConfig { /** * CSS selector for the observation paths (e.g. `'path.observation'`, * `'polyline.line'`). Each matched element is one observation. */ selector: string; /** The axes, in the order they are drawn. Each is a key on the observation. */ dimensions: string[]; /** * Reads one dimension off an observation. Defaults to a plain property * lookup — supply this when the values are nested (`d.values[dimension]`) * or need converting. */ value?: (datum: unknown, dimension: string, index: number) => number; /** * Accessor for the observation's name, announced as its series name. * @default 'name', falling back to `label`, `id`, `key`, `group` or `fill`. */ label?: DataAccessor; } /** * Configuration for binding a D3 pie or doughnut chart. * * The canonical D3 pie is `d3.pie()` + `d3.arc()` drawn as one `` per * slice, so `selector` should match those paths. Both accessors are read * against YOUR datum, not the arc the layout wraps it in — the binder unwraps * the arc first. * * @example * ```ts * bindD3Pie(svg, { * selector: 'path.slice', * axes: { x: 'Fruit', y: 'Units' }, * x: 'fruit', * }); * ``` */ export declare interface D3PieConfig extends D3BinderConfig { /** CSS selector for the wedge elements (e.g., `'path.slice'`, `'path.arc'`). */ selector: string; /** * Accessor for the slice label. @default 'x', falling back to `label`, * `name`, `category`, or `key` when the datum has one of those instead. * A datum that is a bare number or string labels its own slice. */ x?: DataAccessor; /** * Accessor for the slice magnitude. Defaults to the value `d3.pie()` itself * computed for the slice, which is what the drawn angle is proportional to; * supply this only for a pie drawn without the layout. */ y?: DataAccessor; /** * Axis labels. A pie has no fill axis: the share of the whole is derived * from the values themselves, so there is nothing for a third axis to name. */ axes?: { /** What the slice labels mean, e.g. `'Fruit'`. */ x?: D3AxisInput; /** What the slice values measure, e.g. `'Units'`. */ y?: D3AxisInput; }; } /** * Configuration for binding a D3 polar area (coxcomb, rose) chart. * * The wedges are drawn the way a pie's are — `d3.arc()` per category, usually * over `d3.pie()` output — so this is {@link D3PieConfig} verbatim, and the * binder unwraps the layout's arc for you the same way. What differs is what * the wedge encodes: a polar area gives every category the same angle and * varies the **radius**, so the values are read as a series around the spokes * rather than as shares of a whole. * * @example * ```ts * bindD3PolarArea(svgElement, { * selector: 'path.wedge', * axes: { x: 'Month', y: 'Deaths' }, * x: 'month', * }); * ``` */ export declare type D3PolarAreaConfig = D3PieConfig; /** * Configuration for binding a D3 ridgeline (joy) plot. * * One `d3.area()` density curve per group, the curves offset down the page so * their shapes can be compared. `selector` matches one `` per group, and * the samples come from that path's own bound array. * * **`density` is the curve's own half-width, never the drawn y.** A ridgeline * is drawn by adding the group's baseline to every density, and that baseline * is layout rather than data: fed to MAIDR it would make every group's * loudness a function of where it happened to be stacked, and the lowest ridge * the loudest. So the binder reads the kernel-density value the chart computed * before* offsetting it, and refuses to guess when the samples do not carry * one. * * The fields are named for what they mean rather than for the payload keys * they land on, because a ridgeline's value axis is usually the drawn `x` * while the payload's `y` is that same value: `group` names the ridge, * `value` is the position along the value axis, `density` is the height there. */ export declare interface D3RidgelineConfig extends D3BinderConfig { /** CSS selector for the group curves (e.g. `'path.ridge'`). One per group. */ selector: string; /** * Accessor for the sample array, when the path's datum wraps it rather than * being it. Defaults to the datum itself when it is an array, the second * item of a `d3.groups()` tuple, or a `values` / `samples` / `points` / * `curve` property. */ samples?: DataAccessor; /** * Accessor for the group's name, resolved against the path's datum. * @default 'group', falling back to `key`, `name`, `x`, `label` or * `category`; then to the group's ordinal when the datum names nothing. */ group?: DataAccessor; /** * Accessor for a sample's position along the value axis. * @default 'value', falling back to `x`, `t` or `position`. */ value?: DataAccessor; /** * Accessor for the curve's own half-width at a sample — the density before * the group's baseline was added. * @default 'density', falling back to `kde`, `width`, `p` or `estimate`. */ density?: DataAccessor; } /** * Configuration for binding a D3 scatter plot. */ export declare interface D3ScatterConfig extends D3BinderConfig { /** CSS selector for the point elements (e.g., `'circle'`, `'circle.dot'`). */ selector: string; /** Accessor for the x-axis value. @default 'x' */ x?: DataAccessor; /** Accessor for the y-axis value. @default 'y' */ y?: DataAccessor; } /** * Configuration for binding a D3 segmented bar chart (stacked, dodged, or normalized). * * Supports two common D3 patterns: * * 1. **Flat structure** (no `groupSelector`): All bar `` elements are queried * from the SVG root, and each element's datum must include `x`, `y`, and `fill`. * * 2. **`d3.stack()` structure** (with `groupSelector`): Each series lives in a * `` group element whose datum has a `.key` property identifying the series. * Use function accessors to extract values from the `d3.stack()` tuple format. * * @example * ```ts * // d3.stack() pattern * bindD3Segmented(svg, { * groupSelector: 'g.series', * selector: 'rect', * type: 'stacked_bar', * x: (d) => d.data.category, * y: (d) => d[1] - d[0], * }); * ``` */ export declare interface D3SegmentedConfig extends D3BinderConfig { /** CSS selector for all bar segment elements (e.g., `'rect.bar'`, `'rect'`). */ selector: string; /** * CSS selector for series group elements (e.g., `'g.series'`). * When provided, bar segments are queried within each group and the * fill/series key is read from each group's D3 datum `.key` property * (standard `d3.stack()` output) unless overridden by the `fill` accessor. */ groupSelector?: string; /** The type of segmented chart. @default TraceType.STACKED */ type?: SegmentedTraceType; /** * Chart orientation. Emitted only when given, so a chart that does not * declare one is read the core's way (vertical). * * Set it for the charts that are drawn on their side — a population pyramid * is the usual one: with `Orientation.HORIZONTAL`, `x` reads the (signed) * value and `y` the category, which is the order the bars are drawn in. */ orientation?: Orientation; /** Accessor for the x-axis (category) value. @default 'x' */ x?: DataAccessor; /** Accessor for the y-axis (numeric) value. @default 'y' */ y?: DataAccessor; /** Accessor for the fill/group identifier. @default 'fill' */ fill?: DataAccessor; /** * Hint for how the rendered `` elements are ordered in the DOM. * * - `'subject-major'` — rects are interleaved by category then series, * e.g. `[Cat0-A, Cat0-B, Cat0-C, Cat1-A, ...]`. This is the result of a * single flat `selectAll(...).data(flatArr).join(...)` call and matches * the typical D3 dodged-bar pattern. * - `'series-major'` — all of series 0 first, then all of series 1, etc., * e.g. `[A-Cat0..CatN, B-Cat0..CatN, ...]`. This is produced by looping * `regions.forEach(r => selectAll(...).data(byRegion[r]).join(...))` and * matches the typical D3 stacked-bar pattern, as well as `d3.stack()` * with `groupSelector`. * * When omitted, the binder auto-detects from the rendered fills and falls * back to `type`-based defaults (`stacked_bar` / `normalized_bar` → * `series-major`, `dodged_bar` → `subject-major`). */ domOrder?: 'subject-major' | 'series-major'; } /** * Configuration for binding a D3 smooth/regression curve. */ export declare interface D3SmoothConfig extends D3BinderConfig { /** CSS selector for the smooth curve point elements (e.g., `'circle.smooth'`). */ selector: string; /** Accessor for the x-axis data value. @default 'x' */ x?: DataAccessor; /** Accessor for the y-axis data value. @default 'y' */ y?: DataAccessor; /** Accessor for the SVG x coordinate. @default 'svg_x' */ svgX?: DataAccessor; /** Accessor for the SVG y coordinate. @default 'svg_y' */ svgY?: DataAccessor; } /** * One panel of a {@link bindD3Subplots} composition: which binder to run, * its config, and the DOM subtree to extract from. The entry config's * `title` becomes the panel's display name in subplot navigation summaries; * its `id`, `subtitle`, `caption`, and `autoApply` are ignored (figure-level * fields live on {@link D3SubplotsConfig}). */ export declare type D3SubplotEntry = D3PanelChartSpec & { /** * The panel's root element, or a CSS selector resolved against the outer * container passed to `bindD3Subplots`. */ root: Element | string; }; /** * Configuration for {@link bindD3Subplots} — a heterogeneous grid of * independently-drawn charts inside one SVG (or container). */ export declare interface D3SubplotsConfig { /** * The panels, either as an explicit 2D grid (row-major, ragged rows * allowed, empty rows not) or as a flat array arranged via `layout` / * geometry inference. */ subplots: D3SubplotEntry[][] | D3SubplotEntry[]; /** Grid layout for a flat `subplots` array. Ignored for 2D arrays. */ layout?: D3PanelLayout; /** Unique identifier for the figure. Auto-generated when omitted. */ id?: string; /** Figure title displayed in text descriptions. */ title?: string; /** Figure subtitle. */ subtitle?: string; /** Figure caption. */ caption?: string; /** * When `true` (the default), writes the generated MAIDR schema to the * container as a `maidr-data` attribute. See {@link D3BinderConfig.autoApply}. */ autoApply?: boolean; } /** * Configuration for binding a D3 Kaplan-Meier survival curve. * * A survival curve is a step line — `d3.line().curve(d3.curveStepAfter)` over * one `` per arm — so `selector`, `pointSelector` and the `x`/`y`/`fill` * accessors are {@link D3LineConfig}'s, unchanged. What a survival figure * carries beyond a step chart is the two things it is read for: which times * were **censored**, and how wide the **confidence band** is. * * Censoring marks are drawn as ticks from their own data join, not as vertices * of the curve, so they are usually a separate selection: point * `censoredSelector` at them and the binder merges each tick into its arm by * time — flagging the vertex already at that time, or inserting one carrying * the probability the curve holds there. When the curve's own samples already * say (a `censored` column), leave `censoredSelector` unset and let the * `censored` accessor read it. */ export declare interface D3SurvivalConfig extends D3LineConfig { /** * Accessor for whether a sample is a censored time. @default 'censored', * falling back to `censor` or `isCensored`. `true`, `1`, `'1'` and `'true'` * count as censored; anything else does not. * * Deliberately NOT aliased to `event`, which most survival datasets carry * with the opposite meaning — a 1 there is the event happening, which is * exactly the times that are not censored. */ censored?: DataAccessor; /** * CSS selector for the censoring tick marks, when the chart draws them from * a separate data join (e.g. `'line.censor'`). Each tick is merged into the * arm its `fill` names — or the only arm, on a single-curve chart — at the * time its `x` gives. */ censoredSelector?: string; /** Accessor for the confidence band's lower bound. @default 'yMin', falling back to `lower`, `lo`, `ciLower` or `low`. */ yMin?: DataAccessor; /** Accessor for the confidence band's upper bound. @default 'yMax', falling back to `upper`, `hi`, `ciUpper` or `high`. */ yMax?: DataAccessor; } /** * Configuration for binding a D3 treemap or sunburst. * * The canonical layout is `d3.treemap()` / `d3.partition()` over a * `d3.hierarchy()`, so the datum bound to each mark is a **hierarchy node**: * the binder recognises it and reads the node's own `value` plus its ancestor * chain, exactly as the pie binder unwraps a `d3.pie()` arc. Both accessors * are then read against YOUR datum (`node.data`), not against the node. * * Every matched element becomes one point, in DOM order — nothing is filtered. * A treemap draws only its leaves and a sunburst draws its interior nodes too; * whichever you select is what the reader navigates, and the counts have to * match for highlighting to survive. * * @example * ```ts * // svg.selectAll('rect.leaf').data(d3.treemap()(root).leaves()) * bindD3Treemap(svgElement, { * selector: 'rect.leaf', * axes: { x: 'Region', y: 'Population, millions' }, * }); * ``` */ export declare interface D3TreemapConfig extends D3BinderConfig { /** CSS selector for the node elements (e.g. `'rect.leaf'`, `'path.arc'`). */ selector: string; /** * Accessor for the node's own name, read against your datum. * @default 'name', falling back to `id`, `label`, `key`, or `x`. A datum * that is a bare string or number names its own node. * * The same accessor names every ancestor when `path` is derived, so the * breadcrumb and the node agree about what things are called. */ x?: DataAccessor; /** * Accessor for the node's magnitude. Defaults to the `value` that * `d3.hierarchy().sum(...)` computed — which is what the rectangle's area * was drawn from — falling back to `value` or `size` on your datum. */ y?: DataAccessor; /** * Accessor for the node's ancestors, root first and **excluding the node * itself**. Defaults to the hierarchy node's own ancestor chain, so a layout * built with `d3.hierarchy()` needs nothing here. * * Supply it for a tree drawn without `d3.hierarchy()`: `[]` (or an omitted * value) marks a top-level node. */ path?: DataAccessor<(string | number)[]>; } /** * Configuration for binding a D3 violin plot. * * A violin is `d3.area()` over the KDE bins, mirrored about each category's * centre, so `selector` matches one `` per category with that category's * bin array bound to it — the same shape {@link D3LineConfig} reads off a line * path. The box overlay, when the chart draws one, is the per-category `` * {@link D3BoxConfig} reads, and is named separately by `boxSelector`. */ export declare interface D3ViolinConfig extends D3BinderConfig { /** * CSS selector for the violin outline paths — one per category * (e.g. `'path.violin'`). */ selector: string; /** Accessor for the category label. @default 'fill' */ fill?: DataAccessor; /** * Accessor for the array of KDE samples bound to each violin, when the datum * is an object wrapping them rather than the array itself. * @default 'kde', falling back to `density`, `samples`, `bins` or `values` */ kde?: DataAccessor; /** * Accessor for one KDE sample's position on the value axis. * @default 'value', falling back to `v`, `y` or `x` */ value?: DataAccessor; /** * Accessor for one KDE sample's density. * @default 'density', falling back to `estimate`, `d` or `count` */ density?: DataAccessor; /** * CSS selector for the box overlay's groups, one per category, when the chart * draws one. Without it — or when the summary cannot be read — the violin is * emitted as its KDE curves alone. */ boxSelector?: string; /** Selector for the IQR box rectangle within each box group. @default 'rect' */ boxRectSelector?: string; /** Accessor for the min value. @default 'min' */ min?: DataAccessor; /** Accessor for q1 value. @default 'q1' */ q1?: DataAccessor; /** Accessor for median (q2) value. @default 'q2' */ q2?: DataAccessor; /** Accessor for q3 value. @default 'q3' */ q3?: DataAccessor; /** Accessor for the max value. @default 'max' */ max?: DataAccessor; /** Accessor for lower outlier values. @default 'lowerOutliers' */ lowerOutliers?: DataAccessor; /** Accessor for upper outlier values. @default 'upperOutliers' */ upperOutliers?: DataAccessor; /** Chart orientation. @default Orientation.VERTICAL */ orientation?: Orientation; } /** * Configuration for binding a D3 volcano plot. * * Extends {@link D3ManhattanConfig} because the two are the same chart read * the same way: `label` names the gene the way it names the SNP, and * `significance` is the same cutoff on the same axis. What a volcano adds is * the **second** cutoff — the x axis is an effect size rather than a position, * so a point is a finding only when it clears both. * * @example * ```ts * bindD3Volcano(svgElement, { * selector: 'circle.gene', * axes: { x: 'log2 fold change', y: '-log10(p)' }, * x: 'lfc', * y: 'logP', * label: 'gene', * significance: 1.3, * effect: 1, * }); * ``` */ export declare interface D3VolcanoConfig extends D3ManhattanConfig { /** * 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. * * Like `significance`, there is no default: the conventions differ by field, * and a guessed line would sort every gene onto the wrong side silently. */ effect?: number; } /** * Configuration for binding a D3 waterfall (bridge) chart. * * A waterfall draws each step as a bar floating between the running total * before it and the running total after it, so `start` and `end` are the two * numbers the rect is already drawn from. The contribution (`delta`) is * derived from them. * * `kind` is the one thing the binder cannot infer: an opening, closing or * subtotal bar is drawn exactly like a step but contributes nothing, and only * the author knows which bars those are. Supply the accessor for them; every * other bar is classified from the sign of its contribution. * * @example * ```ts * bindD3Waterfall(svgElement, { * selector: 'rect.step', * axes: { x: 'Step', y: 'Amount' }, * x: 'label', * kind: d => (d.isTotal ? 'total' : undefined), * }); * ``` */ export declare interface D3WaterfallConfig extends D3BinderConfig { /** CSS selector for the per-step elements (e.g. `'rect.step'`). */ selector: string; /** Accessor for the step's label. @default 'x' */ x?: DataAccessor; /** * Accessor for the running total before the step. @default 'start', * falling back to `from`, `y0`, or `base`. */ start?: DataAccessor; /** * Accessor for the running total after the step. @default 'end', * falling back to `to`, `y1`, or `cumulative`. */ end?: DataAccessor; /** * Accessor marking a bar as an opening, closing or subtotal (`'total'`). * Returning `undefined` falls back to the derived kind, so * `d => (d.isTotal ? 'total' : undefined)` marks only the totals. * * When omitted, a step is an `'increase'` unless its contribution is * negative, in which case it is a `'decrease'`. */ kind?: DataAccessor; } /** * Configuration for binding a D3 word cloud. * * The layout — `d3-cloud`'s `cloud().words(...)`, or any other — is * deliberately not read: where a term landed carries no data, so the payload * is the term and its weight alone. Point `selector` at the `` glyphs. * * The default accessors are `d3-cloud`'s own datum keys (`text` and `size`), * since that is what all but hand-rolled clouds are laid out with. * * @example * ```ts * bindD3WordCloud(svgElement, { * selector: 'text.term', * axes: { x: 'Term', y: 'Occurrences' }, * }); * ``` */ export declare interface D3WordCloudConfig extends D3BinderConfig { /** CSS selector for the term elements (e.g. `'text.term'`). */ selector: string; /** * Accessor for the term. @default 'text', falling back to `word`, * `term`, `label`, `name`, or `x`. */ x?: DataAccessor; /** * Accessor for the term's weight. @default 'size', falling back to * `value`, `weight`, `count`, `frequency`, or `y`. */ y?: DataAccessor; } /** * Data accessor function or property name for extracting a value from a D3 datum. * If a string is provided, it's used as a property key on the datum object. * If a function is provided, it receives the datum and its index, returning the value. */ export declare type DataAccessor = string | ((datum: unknown, index: number) => T); /** * 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; } /** * 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; } /** * Trace types that share the flow extraction: one ribbon per weighted link, * named by the pair of nodes it joins. * * A sankey runs its ribbons left to right, an alluvial repeats the node columns * and a chord wraps them around a circle; all three are the same weighted graph, * so all three are built by {@link buildFlowLayer} and differ only in the type * the layer announces. */ export declare type FlowTraceType = typeof TraceType.ALLUVIAL | typeof TraceType.CHORD | typeof TraceType.SANKEY; /** * 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; } /** * Trace types that share the line extraction: one series per path, one value * per sample. * * An area fills the band under the line, a bump chart plots ranks instead of * magnitudes, a radar wraps the samples around a circle, and a survival curve * steps down them; all of them are navigated as a multi-line grid, so all of * them are built by {@link buildLineLayer} and differ only in the type the * layer announces — which is what makes the trace read the values correctly * (an area reports its stack total, a bump inverts its pitch, a radar pans by * the spoke's angle, a survival curve finds its median). */ export declare type LineMarkTraceType = typeof TraceType.AREA | typeof TraceType.BUMP | typeof TraceType.LINE | typeof TraceType.NORMALIZED_AREA | typeof TraceType.RADAR | typeof TraceType.STACKED_AREA | typeof TraceType.STEP | typeof TraceType.SURVIVAL; /** * 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 }], * }], * }]], * }; * ``` */ export declare interface MaidrData { /** 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. */ export 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: [...] }, * ], * }; * ``` */ export 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. */ export 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; } /** * Trace types that share the scatter extraction: one x and one y per point. * * A Manhattan plot is a scatter read almost entirely through a threshold — * `-log10(p)` against genomic position — and a volcano is the same reading * with effect size on the x axis. Both carry two things a scatter does not * (what each point *is*, and which region it belongs to) but are extracted the * same way, by {@link buildScatterLayer}. */ export declare type ScatterMarkTraceType = typeof TraceType.MANHATTAN | typeof TraceType.SCATTER | typeof TraceType.VOLCANO; /** * 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; } /** * Trace types that share the segmented extraction: one category, one value and * one series key per mark. * * A diverging bar chart (a population pyramid, a Likert scale) is two series * drawn back to back rather than one on top of the other, which changes how the * values are read but not how they are extracted — so it is built by * {@link buildSegmentedLayer} like the other three, selected through * `config.type`. * * A mosaic is a stacked bar whose column widths carry a second magnitude. That * width is the one thing the segmented extraction does not already read, so a * mosaic is the same core with two extra accessors ({@link D3MosaicConfig}). */ export declare type SegmentedTraceType = typeof TraceType.STACKED | typeof TraceType.DODGED | typeof TraceType.NORMALIZED | typeof TraceType.DIVERGING | typeof TraceType.MOSAIC; /** * 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', ... }; * ``` */ export 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)[]; } /** * Trace types that share the hierarchy extraction: one node per mark, named by * the path from the root down to it. * * A treemap lays the tree out as nested rectangles, a sunburst as concentric * arcs and an icicle as depth-ordered bands; the tree is the same, so all three * are built by {@link buildTreemapLayer} and differ only in the type the layer * announces — which is what makes the sunburst pan by the node's angle around * the dial. */ export declare type TreemapTraceType = typeof TraceType.ICICLE | typeof TraceType.SUNBURST | typeof TraceType.TREEMAP; /** * 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 { }