# Scene (Chartist API)

> The scene a chart composes before rendering: nodes, their roles and data, and how to find and change them.

## node

```ts
function node<R extends keyof NodeRoles>(name: string, attributes: Record<string, AttrValue | undefined | null>, className: string, meta: RoleMeta<R>): RoleNode<R>
function node(name: string, attributes?: Record<string, AttrValue | null | undefined>, className?: string, meta?: { readonly key?: string; readonly ctx?: NodeContext }): PlainNode
```

Creates a scene node: an SVG element with attributes and classes, and, for a node with a role, its data context. Use it in `compose` listeners and custom layers to add or replace elements. With a `role` in `meta`, the node is typed as that role's node and the context is checked.

Parameters:

- `name: string` SVG element name, e.g. `circle`, `g` or `text`
- `attributes: Record<string, AttrValue | undefined | null>` Attributes; `undefined` and `null` values are skipped
- `className: string` Space-separated class names
- `meta: RoleMeta<R>` Role, data context and key of the node

Returns: A new node without children

```ts
import { lineChart, node } from 'chartist';

// Circles instead of the line chart's points.
lineChart(
  '#chart',
  { series: [[1, 3, 2, 4]] },
  {
    plugins: [
      {
        name: 'circles',
        install(chart) {
          chart.on('compose', current => {
            if (current.role !== 'point') {
              return;
            }
            const { x, y } = current.ctx;
            return node('circle', { cx: x, cy: y, r: 5 }, 'ct-circle', {
              role: 'point',
              ctx: current.ctx,
              key: current.key
            });
          });
        }
      }
    ]
  }
);
```

## findAll

*experimental*

```ts
function findAll<R extends keyof NodeRoles>(scene: SceneNode, query: NodeQuery & { readonly role: R }): RoleNode<R>[]
function findAll(scene: SceneNode, query?: NodeQuery): SceneNode[]
```

Finds all nodes of a scene that match the query, in document order, the root included. With a `role`, the nodes are typed as that role's nodes.

Parameters:

- `scene: SceneNode` The root of the tree to search, e.g. the `scene` payload
- `query: NodeQuery & { readonly role: R }` The fields to match; without one, every node matches

```ts
import { barChart, findAll } from 'chartist';

barChart(
  '#chart',
  { labels: ['Q1', 'Q2', 'Q3', 'Q4'], series: [[5, 4, 3, 7]] },
  {
    plugins: [
      {
        name: 'log-bars',
        install(chart) {
          chart.on('scene', ({ scene }) => {
            for (const bar of findAll(scene, { role: 'bar' })) {
              console.log(bar.key, bar.ctx.value, bar.ctx.y2);
            }
          });
        }
      }
    ]
  }
);
```

## mapNodes

*experimental*

```ts
function mapNodes(scene: SceneNode, mapper: (current: SceneNode) => void | SceneNode | null | undefined): SceneNode | null
```

Maps a tree bottom-up: children first, then their parent with the mapped children. Return a node to replace, `null` to drop, or `undefined` to keep. Subtrees without changes are reused, not copied.

Parameters:

- `scene: SceneNode` The root of the tree to map
- `mapper: (current: SceneNode) => void | SceneNode | null | undefined` Called once per node, with the node's mapped children

Returns: The mapped tree, or `null` if the mapper dropped the root

```ts
import {
  autoScaleAxis,
  axisGrid,
  cartesianAxes,
  line,
  mapNodes,
  renderToString,
  stepAxis,
  toScene
} from 'chartist';

const labels = ['Mon', 'Tue', 'Wed'];
const scene = toScene(
  {
    data: { labels, series: [[1, 3, 2]] },
    axes: cartesianAxes([
      { id: 'x', side: 'bottom', type: stepAxis(), ticks: labels },
      { id: 'y', side: 'left', type: autoScaleAxis() }
    ]),
    layers: [axisGrid(), line()]
  },
  { width: 600, height: 300 }
);

// No grid lines, and the points marked for the stylesheet.
const mapped = mapNodes(scene, current => {
  if (current.role === 'grid') {
    return null;
  }
  if (current.role === 'point') {
    return current.addClass('is-marked');
  }
});
const markup = mapped ? renderToString(mapped) : '';
```

## AttrValue

```ts
type AttrValue = string | number
```

The value of a node attribute. Numbers are written as they are, e.g. `x1="12.5"`.

## NodeContext

```ts
interface NodeContext
```

Data attached to a node without a role, e.g. by a plugin. Never rendered. Any fields are allowed; the ones below have a meaning to Chartist.

Properties:

- `seriesIndex?: number` The index of the series the node belongs to. With it, the node gets a `key` and [`findAll`](https://chartist.dev/api/scene/#findAll) can find it by series.
- `valueIndex?: number` The index of the value in its series the node stands for.
- `value?: unknown` The value the node stands for.
- `meta?: unknown` The `meta` of the value or series, as given in the data.

## NodeMeta

```ts
interface NodeMeta
```

What a node means, next to how it looks: its key, role and data context. None of it is rendered.

Properties:

- `key?: string` A stable id, e.g. `0/1/3` (layer, series, value).
- `role?: keyof NodeRoles` What the node draws, e.g. `point` or `bar`: a key of [`NodeRoles`](https://chartist.dev/api/scene/#NodeRoles).
- `ctx?: object` The node's data: what its role declares, or anything for plain nodes.

## SceneNodeFields

```ts
interface SceneNodeFields
```

The fields every node has, whatever its role.

Properties:

- `name: string` SVG element name, e.g. `g`, `path`, `line`.
- `attrs: Readonly<Record<string, AttrValue>>` The element's attributes, without `class`. Names with a `ct:` prefix (`ct:value`, `ct:meta`) are written in the Chartist namespace.
- `classes: readonly string[]` The element's class names, each once, in the order they were added.
- `children: readonly SceneNode[]` The child nodes, in document order.
- `content?: string` Text content, set with `text()`.
- `key?: string` A stable id that survives updates. The pipeline keys data nodes that have none: `layer/series/value` for a value's node (e.g. `0/1/3`), `layer/series` for a series' node.

## SceneNodeMethods

```ts
interface SceneNodeMethods
```

The builder methods of every node. Each returns a changed copy of the same type and leaves the node it's called on as it was.

Properties:

- `attr: (attributes: Record<string, AttrValue | undefined | null>) => this` Sets attributes; `undefined` or `null` removes one.
- `addClass: (names: string) => this` Adds space-separated class names that aren't present yet.
- `removeClass: (names: string) => this` Removes space-separated class names.
- `append: (child: SceneNode, insertFirst?: boolean) => this` Adds a child at the end, or at the start with `insertFirst`.
- `remove: (child: SceneNode) => this` Removes a direct child (by identity).
- `replace: (child: SceneNode, next: SceneNode) => this` Replaces a direct child (by identity).
- `text: (content: string) => this` Sets the text content.
- `empty: () => this` Removes all children and text content.

## RoleNode

```ts
type RoleNode<R extends NodeRole> = SceneNodeFields & SceneNodeMethods & { … }
```

A node of a role, such as `RoleNode<'point'>`: its context and attributes are the ones [`NodeRoles`](https://chartist.dev/api/scene/#NodeRoles) declares for that role. [`findAll`](https://chartist.dev/api/scene/#findAll) with a `role` returns these.

Properties:

- `name: string` SVG element name, e.g. `g`, `path`, `line`.
- `attrs: Readonly<Record<string, AttrValue>> & NodeRoles[R]['attrs']` The element's attributes, typed as the built-in layers set them; `attr()` can change them.
- `classes: readonly string[]` The element's class names, each once, in the order they were added.
- `children: readonly SceneNode[]` The child nodes, in document order.
- `content?: string` Text content, set with `text()`.
- `key?: string` A stable id that survives updates. The pipeline keys data nodes that have none: `layer/series/value` for a value's node (e.g. `0/1/3`), `layer/series` for a series' node.
- `attr: (attributes: Record<string, AttrValue | undefined | null>) => this` Sets attributes; `undefined` or `null` removes one.
- `addClass: (names: string) => this` Adds space-separated class names that aren't present yet.
- `removeClass: (names: string) => this` Removes space-separated class names.
- `append: (child: SceneNode, insertFirst?: boolean) => this` Adds a child at the end, or at the start with `insertFirst`.
- `remove: (child: SceneNode) => this` Removes a direct child (by identity).
- `replace: (child: SceneNode, next: SceneNode) => this` Replaces a direct child (by identity).
- `text: (content: string) => this` Sets the text content.
- `empty: () => this` Removes all children and text content.
- `role: R` What the node draws, e.g. `point`.
- `ctx: NodeRoles[R]['ctx'] & { … }` The node's data, as its role declares it. `seriesIndex` and `valueIndex` read as `number | undefined` on any node, declared or not.

## PlainNode

```ts
type PlainNode = SceneNodeFields & SceneNodeMethods & { … }
```

A node without a role: groups, decorations, anything a plugin adds.

Properties:

- `name: string` SVG element name, e.g. `g`, `path`, `line`.
- `attrs: Readonly<Record<string, AttrValue>>` The element's attributes, without `class`. Names with a `ct:` prefix (`ct:value`, `ct:meta`) are written in the Chartist namespace.
- `classes: readonly string[]` The element's class names, each once, in the order they were added.
- `children: readonly SceneNode[]` The child nodes, in document order.
- `content?: string` Text content, set with `text()`.
- `key?: string` A stable id that survives updates. The pipeline keys data nodes that have none: `layer/series/value` for a value's node (e.g. `0/1/3`), `layer/series` for a series' node.
- `attr: (attributes: Record<string, AttrValue | undefined | null>) => this` Sets attributes; `undefined` or `null` removes one.
- `addClass: (names: string) => this` Adds space-separated class names that aren't present yet.
- `removeClass: (names: string) => this` Removes space-separated class names.
- `append: (child: SceneNode, insertFirst?: boolean) => this` Adds a child at the end, or at the start with `insertFirst`.
- `remove: (child: SceneNode) => this` Removes a direct child (by identity).
- `replace: (child: SceneNode, next: SceneNode) => this` Replaces a direct child (by identity).
- `text: (content: string) => this` Sets the text content.
- `empty: () => this` Removes all children and text content.
- `role?: undefined` Always absent: plain nodes have no role.
- `ctx?: NodeContext` Data you attach to the node, if any.

## SceneNode

```ts
type SceneNode = { [R in NodeRole]: RoleNode<R> }[NodeRole] | PlainNode
```

A scene node: a node of one of the roles in [`NodeRoles`](https://chartist.dev/api/scene/#NodeRoles), or a plain one. Check `role` to narrow it, e.g. `if (node.role === 'point') node.ctx.x`.

Nodes are immutable: the builder methods ([`SceneNodeMethods`](https://chartist.dev/api/scene/#SceneNodeMethods)) return a new node. The `compose` and `scene` phase events pass nodes to your listeners, and the renderers turn them into SVG.

## RoleMeta

```ts
type RoleMeta<R extends NodeRole> = { … } & object extends NodeRoles[R]['ctx'] ? { … } : { … }
```

What [`node`](https://chartist.dev/api/scene/#node) takes as `meta` for a node of role `R`: the role, a key, and the role's context (required when the role declares fields).

Properties:

- `role: R` The node's role, e.g. `point`.
- `key?: string` A stable id; the pipeline sets one for data nodes that have none.

## Point

```ts
interface Point
```

A position in the chart's SVG, in px from its top left corner.

Properties:

- `x: number` Px from the left edge of the SVG.
- `y: number` Px from the top edge of the SVG.

## NoSeries

```ts
interface NoSeries
```

The context of nodes that belong to no series, such as grid lines. It says so, so `ctx.seriesIndex` reads as `number | undefined` on any node.

Properties:

- `seriesIndex?: undefined` Always absent: the node belongs to no series.
- `valueIndex?: undefined` Always absent: the node stands for no value.

## SeriesContext

```ts
interface SeriesContext
```

What every node of a series knows about its series: series groups, points, lines, areas, bars and slices.

Properties:

- `series: unknown` The series as given in the data (array, or object with `data`).
- `seriesIndex: number` The series' index in the chart's data, across layers.
- `meta: unknown` The series' `meta`, if it's a series object with one.

## SeriesGroupContext

```ts
interface SeriesGroupContext extends SeriesContext
```

The context of a `series` node: the `g` element that holds one series' lines, areas, points, bars or slice. It knows its series, but no value.

Properties:

- `series: unknown` The series as given in the data (array, or object with `data`).
- `seriesIndex: number` The series' index in the chart's data, across layers.
- `meta: unknown` The series' `meta`, if it's a series object with one.
- `valueIndex?: undefined` Always absent: the group stands for the whole series.

## ValueContext

```ts
interface ValueContext
```

What a node of one value knows about it: its index and its value.

Properties:

- `valueIndex: number` The value's index in its series, from 0.
- `value: NormalizedSeriesPrimitiveValue` The normalized value: a number, `{ x, y }` numbers, or a hole.

## PointContext

```ts
interface PointContext extends SeriesContext, ValueContext, Point
```

The context of a `point` node: one value of a line chart's series, and where it's drawn. `x` and `y` are the point's position in px in the SVG. Line values are normalized to `{ x, y }` (or a hole), so the value is `ctx.value.y`.

Properties:

- `x: number` Px from the left edge of the SVG.
- `y: number` Px from the top edge of the SVG.
- `series: unknown` The series as given in the data (array, or object with `data`).
- `seriesIndex: number` The series' index in the chart's data, across layers.
- `meta: unknown` The series' `meta`, if it's a series object with one.
- `valueIndex: number` The value's index in its series, from 0.
- `value: NormalizedSeriesPrimitiveValue` The normalized value: a number, `{ x, y }` numbers, or a hole.

## BarContext

```ts
interface BarContext extends SeriesContext, ValueContext
```

The context of a `bar` node: one value of a bar chart's series, and the line it's drawn as, from the base (`x1`, `y1`) to the value (`x2`, `y2`), in px in the SVG. A stacked bar starts where the one below it ends. Bars are clipped to the chart area.

Properties:

- `series: unknown` The series as given in the data (array, or object with `data`).
- `seriesIndex: number` The series' index in the chart's data, across layers.
- `meta: unknown` The series' `meta`, if it's a series object with one.
- `valueIndex: number` The value's index in its series, from 0.
- `value: NormalizedSeriesPrimitiveValue` The normalized value: a number, `{ x, y }` numbers, or a hole.
- `x1: number` The X of the bar's base, in px.
- `y1: number` The Y of the bar's base, in px.
- `x2: number` The X of the bar's end (the value), in px.
- `y2: number` The Y of the bar's end (the value), in px.

## PathContext

```ts
interface PathContext extends SeriesContext
```

The context of a `line` or `area` node: the path of a whole series of a line chart, with all its values.

Properties:

- `series: unknown` The series as given in the data (array, or object with `data`).
- `seriesIndex: number` The series' index in the chart's data, across layers.
- `meta: unknown` The series' `meta`, if it's a series object with one.
- `valueIndex?: undefined` Always absent: the path stands for the whole series.
- `values: NormalizedSeries` All values of the series.
- `path: SvgPath` The path as drawn (a copy, safe to change).

## SliceContext

```ts
interface SliceContext extends SeriesContext
```

The context of a `slice` node: a pie or donut slice, which is one series with one value, and its geometry in px and degrees.

Properties:

- `series: unknown` The series as given in the data (array, or object with `data`).
- `seriesIndex: number` The series' index in the chart's data, across layers.
- `meta: unknown` The series' `meta`, if it's a series object with one.
- `value: number` The slice's value.
- `valueIndex: 0` Always 0: a slice's series has one value.
- `index: number` Same as `seriesIndex`, as in 1.x draw events.
- `totalDataSum: number` The sum of all slices (or the `total` option).
- `path: SvgPath` The slice's outline as drawn (a copy, safe to change).
- `center: Point` The center of the pie, in px in the SVG.
- `radius: number` The pie's radius in px (for a donut, to the middle of its ring).
- `startAngle: number` Where the slice starts, in degrees clockwise from the top.
- `endAngle: number` Where the slice ends, in degrees clockwise from the top.

## GridContext

```ts
interface GridContext extends NoSeries
```

The context of a `grid` node: the grid line of an axis at one tick, across the chart area. Its ends are in px in the SVG.

Properties:

- `seriesIndex?: undefined` Always absent: the node belongs to no series.
- `valueIndex?: undefined` Always absent: the node stands for no value.
- `axis: ScaleAxis` The axis the line belongs to; `axis.dimension` is `x` or `y`.
- `index: number` The tick's index.
- `x1: number` The X of the line's start, in px.
- `y1: number` The Y of the line's start, in px.
- `x2: number` The X of the line's end, in px.
- `y2: number` The Y of the line's end, in px.

## AxisLabelContext

```ts
interface AxisLabelContext extends NoSeries
```

The context of an axis `label` node: the label of an axis at one tick, and the box it's laid out in, in px in the SVG.

Properties:

- `seriesIndex?: undefined` Always absent: the node belongs to no series.
- `valueIndex?: undefined` Always absent: the node stands for no value.
- `kind: 'axis'` `axis` for axis labels; check it to tell them from pie labels.
- `axis: ScaleAxis` The axis the label belongs to; `axis.dimension` is `x` or `y`.
- `index: number` The tick's index.
- `text: Label` The label after `labelInterpolationFnc`.
- `x: number` The left edge of the label's box, in px.
- `y: number` The top edge of the label's box, in px.
- `width: number` The width of the label's box in px. On the X axis, the space to the next tick.
- `height: number` The height of the label's box in px. On the Y axis, the space to the next tick.

## PieLabelContext

```ts
interface PieLabelContext extends SeriesContext, Point
```

The context of a pie `label` node: a slice's label on a pie or donut. `x` and `y` are where the label is anchored, in px in the SVG.

Properties:

- `x: number` Px from the left edge of the SVG.
- `y: number` Px from the top edge of the SVG.
- `series: unknown` The series as given in the data (array, or object with `data`).
- `seriesIndex: number` The series' index in the chart's data, across layers.
- `meta: unknown` The series' `meta`, if it's a series object with one.
- `kind: 'pie'` `pie` for slice labels; check it to tell them from axis labels.
- `valueIndex: 0` Always 0, as for the slice.
- `index: number` Same as `seriesIndex`, as in 1.x draw events.
- `text: string` The label as drawn.

## LineAttrs

```ts
interface LineAttrs
```

The attributes of a `line` element: points, bars and grid lines. A point is a line of 0.01 px, which the CSS draws as a dot with a round cap.

Properties:

- `x1: number` The X of the line's start, in px.
- `y1: number` The Y of the line's start, in px.
- `x2: number` The X of the line's end, in px.
- `y2: number` The Y of the line's end, in px.
- `ct:value?: string | number` The value as written to the output, for points and bars.
- `ct:meta?: string` The value's `meta`, serialized, for points and bars that have one.

## PathAttrs

```ts
interface PathAttrs
```

The attributes of a `path` element: lines, areas and slices.

Properties:

- `d: string` The path data, as in SVG's `d` attribute.
- `ct:value?: string | number` The value as written to the output, for slices.
- `ct:meta?: string` The series' `meta`, serialized, for slices that have one.

## SeriesAttrs

```ts
interface SeriesAttrs
```

The attributes of a series' `g` element.

Properties:

- `ct:series-name?: string` The series' `name`, if it has one.
- `ct:meta?: string` The series' `meta`, serialized, if it has one (line and bar charts).

## RectAttrs

```ts
interface RectAttrs
```

The attributes of a `rect` element: the grid background.

Properties:

- `x: number` The left edge, in px.
- `y: number` The top edge, in px.
- `width: number` The width in px.
- `height: number` The height in px.

## NodeRoles

```ts
interface NodeRoles
```

The roles of scene nodes, and for each its data context (`ctx`) and attributes (`attrs`). Checking `node.role` narrows a [`SceneNode`](https://chartist.dev/api/scene/#SceneNode) to its role, so `ctx` has that role's fields.

The built-in layers and axes use the roles below; the class names are their defaults. A plugin that adds nodes with a role of its own declares it here with TypeScript's module augmentation; [`node`](https://chartist.dev/api/scene/#node) and [`findAll`](https://chartist.dev/api/scene/#findAll) then accept it, and its `compose` listeners see it.

Properties:

- `point: { … }` A value of a line chart: a `line` of 0.01 px (class `ct-point`).
- `bar: { … }` A value of a bar chart: a `line` (class `ct-bar`).
- `line: { … }` A series' line in a line chart: a `path` (class `ct-line`).
- `area: { … }` A series' filled area in a line chart: a `path` (class `ct-area`).
- `slice: { … }` A pie or donut slice: a `path` (class `ct-slice-pie` or `ct-slice-donut`).
- `series: { … }` The `g` element that holds a series' nodes (class `ct-series`).
- `grid: { … }` A grid line of an axis at a tick: a `line` (class `ct-grid`).
- `gridBackground: { … }` The `rect` behind the chart area (class `ct-grid-background`).
- `label: { … }` An axis label (a `foreignObject` with a `span`, or SVG `text`) or a pie label (SVG `text`). Check `ctx.kind` to tell them apart.

```ts
import { node, type ChartPlugin } from 'chartist';

declare module 'chartist' {
  interface NodeRoles {
    marker: {
      ctx: { readonly seriesIndex: number; readonly label: string };
      attrs: { readonly cx: number; readonly cy: number };
    };
  }
}

// A marker on the last point of each series.
export const markers: ChartPlugin = {
  name: 'markers',
  install(chart) {
    chart.on('compose', current => {
      if (current.role !== 'series') {
        return;
      }
      const last = current.children.at(-1);
      if (last?.role !== 'point') {
        return;
      }
      const { x, y, seriesIndex } = last.ctx;
      return current.append(
        node('circle', { cx: x, cy: y, r: 6 }, 'ct-marker', {
          role: 'marker',
          ctx: { seriesIndex, label: 'last' }
        })
      );
    });
  }
};
```

## NodeRole

```ts
type NodeRole = keyof NodeRoles
```

A role name, such as `point` or `bar`: a key of [`NodeRoles`](https://chartist.dev/api/scene/#NodeRoles).

## NodeQuery

```ts
interface NodeQuery
```

What [`findAll`](https://chartist.dev/api/scene/#findAll) looks for: nodes by role, key and data context fields. A node matches when it matches every field given; an empty query matches every node.

Properties:

- `role?: keyof NodeRoles` Only nodes of this role, e.g. `bar`.
- `key?: string` Only the node with this key, e.g. `0/1/3` (layer, series, value).
- `seriesIndex?: number` Only nodes of the series with this index (`ctx.seriesIndex`).
- `valueIndex?: number` Only nodes of the value with this index (`ctx.valueIndex`).
