Documentation

Chart states

Immutable, chainable builders that describe one chart. Every visual mapping is explicit.

A chart state is an immutable description of one chart: its data, field mappings, transforms, layout, attention and appearance. Builders such as bar() create one, and every chained call returns a new state.

import { bar } from 'visdelta/bar';

const base = bar(rows).x('category').y('sales');
const next = base.y('profit');

base === next;   // false: chaining never mutates
base.toSpec();   // a detached, serializable view spec

Chaining does not render anything. A state becomes visible only when it is an endpoint of transition() or sequence().

Builders

BuilderImportReader question
bar()visdelta/barHow do categories compare?
line()visdelta/lineHow does a value change in order?
area()visdelta/areaHow does magnitude or composition change?
point()visdelta/pointHow do two quantities relate?
unit()visdelta/unitHow many countable items are there?
pie()visdelta/pieWhat share of one total does each part take?

Each builder accepts a data source and adds chart-specific methods on top of the shared grammar below.

Shared grammar

Every chart state supports these methods:

MethodMeaning
.data(source)Replace the data source
.x(field, options?), .y(field, options?)Map fields to position
.channel(name, field, options?)Declare another channel
.color(valueOrField, options?)Declare a constant colour or a data-driven colour
.size(field, options?)Declare a quantitative size mapping
.key(fieldOrFields)Identify the same mark across states
.datumKey(fieldOrFields)Override inferred source-record identity
.tooltip(fieldOrFields)Declare tooltip fields
.sort(field, order?)Sort the current rows
.where(selector)Keep matching rows; other marks exit
.highlight(selector, options?)Keep all rows; dim non-matching marks
.focus(selector)Keep all rows; fit the camera around matching marks
.reset()Return to the declaration before the first semantic operation
.axis(config)Change the coordinate view or axis order
.transition({ duration?, ease?, stagger? })Set timing for this state's transitions
.repeat(field, options?)Partition into small multiples
.rows(source?)Read the tidy rows after all declared transforms
.toSpec()Return a detached, serializable view spec

Mappings are explicit

VisDelta never decides that a field should mean x, y, colour, size or detail. For example, .breakdown('region') declares detail but does not silently add .color('region'). Presentation defaults such as radius, line width, margins and duration are supplied for you. Field meanings are not.

Without a declared colour channel, marks use the chart style's neutral ink and there is no colour legend.

// A constant colour is Appearance.
base.color('#4269d0');

// A field-driven colour is Encoding.
base.color('region');

Channel options

Channel methods accept options such as type, label, format, domain, scale, sort, aggregate, timeUnit and bin.

Explicit numeric and temporal position domain bounds are kept exactly; only inferred domains are rounded. Log axes use ordinary D3 log ticks without adding data-endpoint ticks.

line(rows)
  .x('date', { type: 'temporal', label: 'Date' })
  .y('close', { label: 'Close (USD)', format: '$.2f' });
  • Type. Core detects quantitative, temporal and nominal from the data and fills only missing types. An authored type always wins.
  • Format. A D3 format specifier for a quantitative axis, such as "~s".
  • Scale. Quantitative position channels support { type: 'linear' }, { type: 'sqrt' } and { type: 'log', base? }. Log scales default to base 10.

Colour

For a colour field, omit range to use VisDelta's default categorical palette, provide range for exact colours, or name a D3 Scale Chromatic scheme:

bar(rows).color('region', { scheme: 'Tableau10' });
point(rows).color('score', { type: 'quantitative', scheme: 'Viridis' });

An explicit range takes precedence over scheme. Colours follow an explicit domain order when you supply one, otherwise the data-domain order. Filtering keeps every category's colour assignment and legend entry.

Plain specs

toSpec() returns plain, serializable data. You can store it, send it and compare it. A plain spec can also be passed to delta() and transition(). Because a plain object cannot carry a reference to its chart module, rendering it requires registering that module first. Builders carry their module automatically.

import { barModule } from 'visdelta/bar';
import { registerChartModule } from 'visdelta/plugins';

registerChartModule(barModule);
const change = await transition(savedSpecA, savedSpecB, { target: '#chart' });

Each focused entry exports its module: barModule, lineModule, areaModule, pointModule, unitModule and pieModule.

On this page