In this page
- Simple in-memory usage
- nodeTime usage with sampling
- Multiple series
- Series type
- Line Series Curve style
- Series dynamic styling
- Multiple y axes
- Draw area between columns
- Specify axis range manually
- Format axis ticks
- Leverage axis hook for more customization
- Bar Series with a baseline
- Rendering a Histogram
- Rendering a Boxplot
- Writing a custom serie (manually drawing)
- Events
- Config
- Misc
- Layout
<gui-chart />
Simple in-memory usage
const el = document.createElement('gui-chart');
el.value = gc.core.Table.create([[0, 2, 5, 8, 12]]);
el.config = {
xAxis: {},
yAxes: {
y: {},
},
series: [
{
type: 'line',
title: 'Value',
yCol: 0,
yAxis: 'y',
},
],
};
nodeTime usage with sampling
Click and drag to select a time range. The chart will resample the time series to fit the selected range.
Double click to reset the selection.
const el = document.createElement('gui-chart');
let timeSeries = null;
el.config = {
xAxis: {
scale: 'time',
},
yAxes: {
y: {},
},
series: [
{
type: 'line',
title: 'Value',
yCol: 1,
yAxis: 'y',
xCol: 0,
},
],
};
function sample(from, to) {
// If from and to are null, we sample the whole time series
gc.core.nodeTime
.sample([timeSeries], from, to, 100, gc.core.SamplingMode.adaptative, null, null)
.then((table) => {
el.value = table;
});
}
// Fetch the node containing the time series
gc.$.default.root().then((root) => {
timeSeries = root['project::exampleTimeSeries'];
sample(null, null);
});
el.addEventListener('selection', (ev) => {
let from = ev.detail.from; // timestamp in ms
let to = ev.detail.to; // timeStamp in ms
// convert to core.time
from = gc.core.time.fromMs(from);
to = gc.core.time.fromMs(to);
sample(from, to);
});
el.addEventListener('reset-selection', () => {
sample(null, null);
});
Multiple series
const el = document.createElement('gui-chart');
el.value = gc.core.Table.create([[0, 2, 5, 8, 12], [0, 2, 5, 8, 12].reverse()]);
el.config = {
xAxis: {},
yAxes: {
y: {},
},
series: [
{
type: 'line',
title: 'Value',
yCol: 0,
yAxis: 'y',
},
{
type: 'line',
title: 'Reversed',
yCol: 1,
yAxis: 'y',
},
],
};
Series type
const el = document.createElement('div');
const select = document.createElement('select');
['line', 'bar', 'area', 'scatter', 'line+scatter', 'line+area'].forEach((type) => {
const option = document.createElement('option');
option.value = type;
option.textContent = type;
select.append(option);
});
select.addEventListener('change', () => {
chart.config.series[0].type = select.value;
switch (select.value) {
case 'line':
chart.config.series[0].width = 1;
break;
case 'bar':
chart.config.series[0].width = 4;
break;
case 'area':
chart.config.series[0].width = 1;
break;
case 'scatter':
chart.config.series[0].width = 2;
break;
case 'line+scatter':
chart.config.series[0].width = 1;
chart.config.series[0].plotRadius = 2;
break;
case 'line+area':
chart.config.series[0].width = 1;
break;
}
chart.update();
});
const chart = document.createElement('gui-chart');
chart.value = gc.core.Table.create([[0, 2, 5, 8, 12]]);
chart.config = {
xAxis: {},
yAxes: {
y: {},
},
series: [
{
type: 'line',
title: 'Value',
yCol: 0,
plotRadius: 2,
yAxis: 'y',
},
],
};
el.append(select, chart);
Line Series Curve style
const el = document.createElement('div');
const select = document.createElement('select');
['step-after', 'linear'].forEach((type) => {
const option = document.createElement('option');
option.value = type;
option.textContent = type;
select.append(option);
});
select.addEventListener('change', () => {
switch (select.value) {
case 'linear':
chart.config.series[0].curve = 'linear';
break;
case 'step-after':
chart.config.series[0].curve = 'step-after';
break;
}
chart.update();
});
const chart = document.createElement('gui-chart');
chart.value = gc.core.Table.create([[0, 2, 5, 8, 12]]);
chart.config = {
xAxis: {},
yAxes: {
y: {},
},
series: [
{
type: 'line',
title: 'Value',
yCol: 0,
yAxis: 'y',
curve: 'step-after',
},
],
};
el.append(select, chart);
Series dynamic styling
const el = document.createElement('div');
const chart = document.createElement('gui-chart');
chart.value = gc.core.Table.create([
[0, 2, 5, 8, 12, 5, 2],
['normal', 'normal', 'normal', 'warning', 'danger', 'normal', 'normal'], // enum describing state
]);
// we map all states to specific stylings
const customStyles = {
normal: {
dash: [3, 3],
transparency: 0.5,
},
warning: {
color: 'orange',
},
danger: {
color: 'red',
width: 3,
},
};
chart.config = {
xAxis: {},
yAxes: {
y: {},
},
series: [
{
type: 'line',
title: 'Value',
yCol: 0,
yAxis: 'y',
curve: 'step-after',
// Column containing value to use to style the curve
styleCol: 1,
// We could very well also have used a condition on the line value itself as "if > 10 : style"
styleMapping: (v) => {
return customStyles[v];
},
},
],
};
el.append(chart);
Multiple y axes
const el = document.createElement('gui-chart');
el.value = gc.core.Table.create([
[0, 2, 5, 8, 12],
[0, 2, 5, 8, 12].reverse().map((v) => v * 1000),
]);
el.config = {
xAxis: {},
yAxes: {
value: {},
big: {
position: 'right',
},
},
series: [
{
type: 'line',
title: 'Value',
yCol: 0,
yAxis: 'value',
},
{
type: 'line',
title: 'Big numbers',
yCol: 1,
yAxis: 'big',
},
],
};
Draw area between columns
const el = document.createElement('gui-chart');
el.value = gc.core.Table.create([
[0, 2, 5, 8, 12], // serie 0
[4, 2, 8, 5, 10], // serie 1
]);
el.config = {
xAxis: {},
yAxes: {
y: {},
},
series: [
{
type: 'line',
title: 'Serie 0',
yCol: 0,
yAxis: 'y',
},
{
type: 'line',
title: 'Serie 1',
yCol: 1,
yAxis: 'y',
},
{
type: 'area',
yCol: 0,
yCol2: 1,
yAxis: 'y',
hideInTooltip: true, // do not show info in tooltip for this serie
},
],
};
Specify axis range manually
const el = document.createElement('gui-chart');
el.value = gc.core.Table.create([
[0, 2, 5, 8, 12], // serie 0
]);
el.config = {
xAxis: {
min: -1,
max: 5,
},
yAxes: {
y: {
min: -5,
max: 15,
},
},
series: [
{
type: 'line',
title: 'Serie 0',
yCol: 0,
yAxis: 'y',
},
],
};
Format axis ticks
We leverage the d3-format library to format the ticks on the axis.
But you can also use a custom function to format the ticks.
const el = document.createElement('div');
const chart = document.createElement('gui-chart');
const select = document.createElement('select');
['s', '~s', '~e'].forEach((type) => {
const option = document.createElement('option');
option.value = type;
option.textContent = type;
select.append(option);
});
select.addEventListener('change', () => {
chart.config.yAxes.y.format = select.value;
chart.update();
});
chart.value = gc.core.Table.create([
[110000, 120000, 150000], // serie 0
]);
chart.config = {
xAxis: {
// Can also use a custom format function
format: (v) => `${v / 1000}k`,
},
yAxes: {
y: {
format: select.value,
},
},
series: [
{
type: 'line',
title: 'Value',
yCol: 0,
yAxis: 'y',
},
],
};
el.append(select, chart);
Leverage axis hook for more customization
The hook function is called right before rendering the axis onto the chart.
We expose the d3 axis object to allow you to customize the axis rendering.
For all the available options, see the d3 axis documentation
const el = document.createElement('gui-chart');
el.value = gc.core.Table.create([
Array.from({ length: 100 }, (_, i) => new Date(Date.now() - i * 24 * 60 * 60 * 1000)),
Array.from({ length: 100 }, () => Math.random() - 0.5 - 10),
]);
el.config = {
xAxis: {
scale: 'time',
hook(axis) {
axis.ticks(6);
},
},
yAxes: {
y: {
hook(axis) {
axis.ticks(5);
},
},
},
series: [
{
type: 'line',
title: 'Value',
yCol: 1,
xCol: 0,
yAxis: 'y',
},
],
};
Bar Series with a baseline
Useful to show a threshold or a reference value.
Example: Monetary value with a reference value of 0.
const el = document.createElement('gui-chart');
el.value = gc.core.Table.create([
Array.from({ length: 100 }, (_, i) => new Date(Date.now() - i * 24 * 60 * 60 * 1000)),
Array.from({ length: 100 }, () => (Math.random() - 0.5) * 1000),
]);
el.config = {
xAxis: {
scale: 'time',
},
yAxes: {
y: {},
},
series: [
{
type: 'bar',
title: 'Value',
xCol: 0,
yCol: 1,
yAxis: 'y',
baseLine: 0,
styleCol: 1,
},
],
};
Rendering a Histogram
const el = document.createElement('gui-chart');
const histogram = [
[153, 156, 160, 164, 168, 171, 175, 179, 183, 187], // from
[156, 160, 164, 168, 171, 175, 179, 183, 187, 190], // to
[9, 135, 999, 3246, 6645, 7406, 4552, 1683, 291, 34], // count
[0.036, 0.54, 3.996, 12.984, 26.58, 29.624, 18.208, 6.732, 1.164, 0.136], // percentage
];
el.value = gc.core.Table.create(histogram);
el.config = {
series: [
{
type: 'bar',
yAxis: 'left',
spanCol: [0, 1],
yCol: 2,
},
],
xAxis: {
scale: 'linear',
min: histogram[0][0],
max: histogram[1][histogram[1].length - 1],
},
yAxes: {
left: {
position: 'left',
},
},
};
Show percentage and cumulative percentage
const el = document.createElement('gui-chart');
const histogram = [
[153, 156, 160, 164, 168, 171, 175, 179, 183, 187], // from
[156, 160, 164, 168, 171, 175, 179, 183, 187, 190], // to
[9, 135, 999, 3246, 6645, 7406, 4552, 1683, 291, 34], // count
[0.036, 0.54, 3.996, 12.984, 26.58, 29.624, 18.208, 6.732, 1.164, 0.136], // percentage
];
// Compute cumulative percentage and middle of the bucket
const cumulativePercentage = [];
const middle = [];
for (let i = 0; i < histogram[3].length; i++) {
cumulativePercentage.push(histogram[3][i] + (cumulativePercentage[i - 1] ?? 0));
middle.push((histogram[0][i] + histogram[1][i]) / 2);
}
// Add the cumulative percentage and the middle of the bucket to the histogram
histogram.push(cumulativePercentage);
histogram.push(middle);
el.value = gc.core.Table.create(histogram);
el.config = {
series: [
{
type: 'line',
yAxis: 'left',
yCol: 3,
xCol: 5,
title: 'Percentage',
},
{
type: 'line',
yAxis: 'right',
yCol: 4,
xCol: 5,
title: 'Cumulative %',
},
],
xAxis: {
scale: 'linear',
min: middle[0],
max: middle[middle.length - 1],
},
yAxes: {
left: {
position: 'left',
},
right: {
// Add a second y axis for the cumulative percentage
position: 'right',
min: 0,
max: 100,
},
},
};
End to end example with sampling and buckets
el = document.createElement('div');
const label = document.createElement('label');
label.textContent = 'Bucket size';
const input = document.createElement('select');
['10', '15', '20', '25', '50'].forEach((type) => {
const option = document.createElement('option');
option.value = type;
option.textContent = type;
input.append(option);
});
chart = document.createElement('gui-chart');
chart.config = {
series: [
{
type: 'bar',
yAxis: 'left',
spanCol: [0, 1],
yCol: 2,
xCol: 0,
},
],
xAxis: {
scale: 'linear',
},
yAxes: {
left: {
position: 'left',
},
},
};
async function sampleHistogram(from, to) {
chart.value = await gc.project.histogramExample(from, to, parseInt(input.value));
}
input.addEventListener('change', () => {
sampleHistogram(null, null);
});
el.addEventListener('selection', (e) => {
sampleHistogram(e.detail.from, e.detail.to);
});
el.addEventListener('reset-selection', () => {
sampleHistogram(null, null);
});
sampleHistogram(null, null);
el.append(label, input, chart);
@expose
fn histogramExample(from: float?, to: float?, buckets: int?): Table {
var rng = Random{};
var histogram = HistogramFloat {};
// Fill the histogram with 1000 random values
for (var i = 0; i < 1000; i++) {
histogram.add(rng.normal(5.0, 1.0));
}
// leverage native histogram function to sample the histogram
return histogram.sample(from ?? histogram.min()!!, to ?? histogram.max()!!, buckets ?? 10);
}
Rendering a Boxplot
const el = document.createElement('gui-chart');
const greycatBoxplot = {
min: -97.5334031495887,
max: 89.57955808824838,
percentile25: -45.66794336316546,
percentile50: 1.783184836759773,
percentile75: 49.330077170287296,
};
el.config = {
series: [
{
type: 'custom',
yAxis: 'left',
yCol: 0,
hideInTooltip: true,
draw(ctx, s, xScale, yScale) {
const boxPlotCanvas = {
max: xScale(greycatBoxplot.max),
median: xScale(greycatBoxplot.percentile50),
min: xScale(greycatBoxplot.min),
q1: xScale(greycatBoxplot.percentile25),
q3: xScale(greycatBoxplot.percentile75),
crossValue: yScale(5),
};
const boxPlotOptions = {
width: 200,
iqrColor: s.color,
whiskerColor: s.color,
medianColor: s.color,
orientation: 'horizontal',
};
ctx.boxPlot(boxPlotCanvas, boxPlotOptions);
},
},
],
xAxis: {
scale: 'linear',
min: greycatBoxplot.min,
max: greycatBoxplot.max,
},
yAxes: {
left: {
min: 0,
max: 10,
ticks: [],
},
},
selection: false,
cursor: false,
};
Rendering a series of Boxplots (vertically)
const el = document.createElement('gui-chart');
const greycatBoxplot = {
min: -97.5334031495887,
max: 89.57955808824838,
percentile25: -45.66794336316546,
percentile50: 1.783184836759773,
percentile75: 49.330077170287296,
};
const greycatBoxplot2 = {
min: -117.5334031495887,
max: 95.57955808824838,
percentile25: -48.66794336316546,
percentile50: 5.783184836759773,
percentile75: 40.330077170287296,
};
const series = [greycatBoxplot, greycatBoxplot2];
el.config = {
value: gc.core.Table.create([[]]),
series: [
{
type: 'custom',
yAxis: 'left',
yCol: 0,
hideInTooltip: true,
draw(ctx, s, xScale, yScale) {
series.forEach((boxplot, i) => {
const boxPlotCanvas = {
max: yScale(boxplot.max),
median: yScale(boxplot.percentile50),
min: yScale(boxplot.min),
q1: yScale(boxplot.percentile25),
q3: yScale(boxplot.percentile75),
crossValue: xScale(i),
};
const width = xScale.range()[1] - xScale.range()[0];
const boxPlotOptions = {
width: width / (series.length + 2),
iqrColor: s.color,
whiskerColor: s.color,
medianColor: s.color,
orientation: 'vertical',
};
ctx.boxPlot(boxPlotCanvas, boxPlotOptions);
});
},
},
],
xAxis: {
scale: 'linear',
min: -1,
max: series.length,
ticks: [],
},
yAxes: {
left: {
scale: 'linear',
min: Math.min(...series.map((v) => v.min)),
max: Math.max(...series.map((v) => v.max)),
},
},
selection: false,
cursor: false,
};
Writing a custom serie (manually drawing)
We provide a way to draw custom series by providing a custom
Series. This is useful if you want to draw a specific shape or a custom line.
The draw function exposes our canvas context, the serie options, and the x and y scales.
const el = document.createElement('gui-chart');
el.value = gc.core.Table.fromObjects([
{ Time: new Date('2024-01-01T15:15:00Z'), Value: -2.5 },
{ Time: new Date('2024-01-02T15:15:00Z'), Value: 1.458 },
{ Time: new Date('2024-01-03T15:15:00Z'), Value: 0.009 },
{ Time: new Date('2024-01-04T15:15:00Z'), Value: 5.64 },
]);
el.config = {
cursor: true,
xAxis: { scale: 'time' },
yAxes: {
y: {},
},
series: [
{
type: 'line',
xCol: 0,
yCol: 1,
yAxis: 'y',
},
{
// a custom serie to always draw a "top" line
// eg. to show a threshold to avoid
type: 'custom',
xCol: 0, // we specify the column we want to get the proper scale for x
yCol: 1, // we specify the column we want to get the proper scale for y
yAxis: 'y', // we bind the serie to the only y axis
hideInTooltip: true, // we do not want it to show in the tooltip
// we provide our own drawing function
draw(ctx, _, xScale, yScale) {
const [xMin, xMax] = xScale.range();
// compute the threshold to pixels coordinate
const fixedY = yScale(4);
// use the internal `gui-chart` draw API to actually draw the threshold line
ctx.simpleLine(xMin, fixedY, xMax, fixedY, { color: 'red', dashed: true, opacity: 0.7 });
},
},
],
};
Events
Name | Detail | Description |
---|---|---|
gui-selection |
GuiChartSelectionEventDetail |
Triggered when selecting an area on the canvas, or when resetting |
gui-chart-enter |
Triggered when entering the canvas area | |
gui-chart-leave |
Triggered when leaving the canvas area |
GuiChartSelectionEventDetail
Useful if you want to resample the serie when the user selects an area,
listen to the event send to your server the specified from and to and update the charts value
prop with the new table.
Don’t forget to do the same with the reset-selection event.
type GuiChartSelectionEvent = CustomEvent<Selection | null>;
interface Selection {
from: unknown;
to: unknown;
}
Config
export interface ChartConfig<K = { [keys: string]: never }> {
series: Serie<Extract<keyof K, string>>[];
/**
* The x-axis definition
*/
xAxis: Axis;
/**
* One or more axes that will be used for y-axes.
*
* This is a key-value object for the series to be able
* to refer to them by the 'key' name in `yAxis`
*/
yAxes: {
[name in keyof K]: Ordinate;
};
cursor?: boolean;
/**
* Tooltip position, defaults to 'top-left'
*/
tooltip?: Partial<Tooltip>;
selection?: Partial<SelectionOptions>;
/**
* Delta in milliseconds between two `touchend` event.
*
* If under this threshold the touch event is processed
* as a `dbltap` rather than a `touchend`
*
* Defaults: `500`
*/
dblTapThreshold?: number;
}
Serie
export interface CustomSerie<K> extends CommonSerie<K> {
type: 'custom';
draw: (ctx: CanvasContext, serie: SerieWithOptions, xScale: Scale, yScale: Scale) => void;
}
export interface LineSerie<K> extends CommonSerie<K> {
type: 'line';
}
export interface StepSerie<K> extends CommonSerie<K> {
type: 'step';
}
export interface BarSerie<K> extends CommonSerie<K> {
type: 'bar';
}
export interface ScatterSerie<K> extends CommonSerie<K> {
type: 'scatter';
}
export interface LineScatterSerie<K> extends CommonSerie<K> {
type: 'line+scatter';
}
export interface AreaSerie<K> extends CommonSerie<K> {
type: 'area';
}
export interface LineAreaSerie<K> extends CommonSerie<K> {
type: 'line+area';
}
export type Serie<K extends string = string> =
| LineSerie<K>
| BarSerie<K>
| ScatterSerie<K>
| LineScatterSerie<K>
| AreaSerie<K>
| LineAreaSerie<K>
| CustomSerie<K>
| StepSerie<K>;
Common Serie
export interface CommonSerie<K> extends Partial<SerieOptions> {
/**
* optional offset of the x column in the given table (see [SerieTableColumn](#SerieTableColumn) for in-depth explaination)
*
* If undefined, the array index will be used
*/
xCol?: SerieTableColumn;
/**
* offset of the y column in the given table (see [SerieTableColumn](#SerieTableColumn) for in-depth explaination)
*/
yCol: SerieTableColumn;
/**
* must refer to a defined 'key' in `config.yAxes` and will be used as the y-axis for this serie
*/
yAxis: K;
/**
* Optional title used to name the serie.
*/
title?: string;
/**
* Overrides the tooltip value
*/
value?: { toString(): string };
/**
* A hook to customize canvas drawing. This is called before the serie has been drawn.
*/
drawBefore?: (ctx: CanvasContext, serie: SerieWithOptions, xScale: Scale, yScale: Scale) => void;
/**
* A hook to customize canvas drawing. This is called after the serie has been drawn.
*/
drawAfter?: (ctx: CanvasContext, serie: SerieWithOptions, xScale: Scale, yScale: Scale) => void;
}
export type SerieOptions = {
/** Whether or not to display the serie */
hide: boolean;
color: string;
width: number;
markerWidth: number;
markerShape: MarkerShape;
markerColor: string;
/**
* When defined, this value is used to control the marker drawing logic.
*
* All values are in pixels.
*
* If only `markerThreshold.x` is defined, then the marker will be drawn if `Math.abs(cursor.x - closestValue.x) <= markerThreshold.x`
*
* If only `markerThreshold.y` is defined, then the marker will be drawn if `Math.abs(cursor.y - closestValue.y) <= markerThreshold.y`
*
* If both `markerThreshold.x` and `markerThreshold.y` the same logic applies but both must be `true` for the marker to be drawn.
*/
markerThreshold?: { x?: number; y?: number };
opacity: number;
fillOpacity: number;
/**
* - `'min'`: draws the area from `yCol` to the bottom
* - `'max'`: draws the area from `yCol` to the top
* - `SerieTableColumn`: draws the area from `yCol` to the column specified by the `SerieTableColumn`
*/
yCol2: SecondOrdinate;
/**
* If `true` this serie value won't show in the tooltip.
*
* *This only works when using the native tooltip*
*/
hideInTooltip: boolean;
/**
* Maps the `col` values to a style definition.
*
* *Returning `null` will make the painting use the default style of the serie*
*
* *Not defining a `styleMapping` will use the actual column value as-is for styling,
* meaning the serie's column can contain style codes directly*
*/
styleMapping?: {
/**
* The index of the column to use for the mapping. The parameter `v` in `mapping(v)` will
* be the cells of that `col`.
*
* (see [SerieTableColumn](#SerieTableColumn) for in-depth explaination)
*/
col: SerieTableColumn;
/**
* @param v the column (`col`) value
* @returns the style used for canvas painting, or `null` to get the default style of the serie
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
mapping?: (v: any) => SerieStyle | null;
};
};
Misc
export type Scale =
| d3.ScaleLinear<number, number, never>
| d3.ScaleTime<number, number, never>
| d3.ScaleLogarithmic<number, number, never>;
export type Color = string | CanvasGradient | CanvasPattern;
export type SerieType = Serie['type'];
export type ScaleType = Extract<Axis['scale'], string>;
export type SecondOrdinate = 'min' | 'max' | number;
export type AxisPosition = 'left' | 'right';
export type MarkerShape = 'circle' | 'square' | 'triangle';
export type TooltipPosition = 'top-left' | 'top-right' | 'bottom-right' | 'bottom-left';
export type SerieWithOptions = Serie & SerieOptions;
// we don't care about the type here, it is user-defined
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type SerieData = Serie & SerieOptions & { xValue?: any; yValue?: any; rowIdx: number };
export type SelectionOptions = {
/**
* If a selection is smallr than this value in pixels it will be ignored.
*
* Defaults: `10`
*/
threshold: number;
/**
* - `'vertical'` means only selectable according to y axes
* - `'horizontal'` means only selectable according to x axis
* - `'both'` means selectable on y & x axes
*
* Defaults to 'horizontal'
*/
orientation: 'vertical' | 'horizontal' | 'both';
};
export type Tooltip = {
/** Always display the tooltip when `true`. Defaults to `false` */
always?: boolean;
position: TooltipPosition;
/**
* Called whenever the tooltip should update its content.
*
* *If this is defined, the default tooltip will not display.*
*
* **This method will be called in the `requestAnimationFrame(loop)` therefore the less work it does the better**
*
* @param data
* @returns
*/
render?: (data: SerieData[], cursor: Cursor) => void;
};
export type CommonAxis = {
title?: string;
min?: number | Date | gc.core.time | gc.core.Date;
max?: number | Date | gc.core.time | gc.core.Date;
/** Disables the cursor marker for that axis if `false`. Defaults to `true` */
cursor?: boolean;
cursorAlign?: 'start' | 'center' | 'end';
cursorBaseline?: CanvasTextBaseline;
cursorPadding?: number;
/**
* Zoom ratio on wheel events on the axis.
*
* Setting this to `0` disables the behavior completely.
*/
ratio?: number;
/**
* This is called right before rendering the axis onto the chart.
*
* **When defined, no other axis properties will be applied `format`, `ticks`, etc.**
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
hook?: (axis: d3.Axis<any>) => void;
};
export type LinearAxis = {
scale?: 'linear';
/**
* If specified, the values are used for ticks rather than the scale’s automatic tick generator.
*
* However, any tick arguments will still be passed to the scale’s tickFormat function if a tick format is not also set.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
ticks?: any[];
/**
* Formats the ticks text depending on the axis type and this parameter type:
*
* - When `format: string` the value is formatted with `d3.format` (see https://d3js.org/d3-format#format).
* - When `format: (value: unknown) => string`, delegates formatting to that function entirely.
* - When `format: undefined` the value is stringified and displayed as-is.
*/
format?: ((value: unknown) => string) | string;
/**
* Formats the cursor text depending on the axis type and this parameter type:
*
* - When `cursorFormat: string` the value is formatted with `d3.format` (see https://d3js.org/d3-format#format).
* - When `cursorFormat: (value: unknown) => string`, delegates formatting to that function entirely.
* - When `cursorFormat: undefined` the value is stringified and displayed as-is.
*/
cursorFormat?: ((value: unknown) => string) | string;
};
export type LogAxis = {
scale: 'log';
/**
* If specified, the values are used for ticks rather than the scale’s automatic tick generator.
*
* However, any tick arguments will still be passed to the scale’s tickFormat function if a tick format is not also set.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
ticks?: any[];
/**
* Formats the cursor text on the axis depending on the axis type and this parameter type:
*
* - When `cursorFormat: string` the value is formatted with `d3.format` (see https://d3js.org/d3-format#format).
* - When `cursorFormat: (value: unknown) => string`, delegates formatting to that function entirely.
* - When `cursorFormat: undefined` the value is stringified and displayed as-is.
*/
format?: ((value: unknown) => string) | string;
/**
* Formats the cursor text on the axis depending on the axis type and this parameter type:
*
* - When `cursorFormat: string` the value is formatted with `d3.format` (see https://d3js.org/d3-format#format).
* - When `cursorFormat: (value: unknown) => string`, delegates formatting to that function entirely.
* - When `cursorFormat: undefined` the value is stringified and displayed as-is.
*/
cursorFormat?: ((value: unknown) => string) | string;
};
export type TimeAxis = {
scale: 'time';
/**
* Time axis can also leverage `d3.TimeInterval` by specifying for instance `d3.utcHour.every(24)`
*/
ticks?: d3.TimeInterval | (gc.core.time | gc.core.Date | Date | number)[] | null;
/**
* Formats the ticks text on the axis depending on the axis type and this parameter type:
*
* - When `format: string` the value is formatted with `d3.utcFormat` (see https://d3js.org/d3-time-format#utcFormat).
* - When `format: (value: number, specifier: string) => string`, delegates formatting to that function entirely.
* The `specifier` parameter is set to be the best possible specifier for the range.
* - When `format: undefined` the value is formatted with `d3.isoFormat` (see https://d3js.org/d3-time-format#isoFormat)
*/
format?: ((value: number, specifier: string) => string) | string;
/**
* Formats the cursor text on the axis depending on the axis type and this parameter type:
*
* - When `cursorFormat: string` the value is formatted with `d3.utcFormat` (see https://d3js.org/d3-time-format#utcFormat).
* - When `cursorFormat: (value: number, specifier: string) => string`, delegates formatting to that function entirely.
* The `specifier` parameter is set to be the best possible specifier for the range.
* - When `cursorFormat: undefined` the value is formatted with `d3.isoFormat` (see https://d3js.org/d3-time-format#isoFormat)
*/
cursorFormat?: ((value: number, specifier: string) => string) | string;
};
export type Axis = LinearAxis | LogAxis | TimeAxis;
export type Ordinate = Axis & { position?: AxisPosition };
export type SerieOptions = {
/** Whether or not to display the serie */
hide: boolean;
color: string;
width: number;
markerWidth: number;
markerShape: MarkerShape;
markerColor: string;
/**
* When defined, this value is used to control the marker drawing logic.
*
* All values are in pixels.
*
* If only `markerThreshold.x` is defined, then the marker will be drawn if `Math.abs(cursor.x - closestValue.x) <= markerThreshold.x`
*
* If only `markerThreshold.y` is defined, then the marker will be drawn if `Math.abs(cursor.y - closestValue.y) <= markerThreshold.y`
*
* If both `markerThreshold.x` and `markerThreshold.y` the same logic applies but both must be `true` for the marker to be drawn.
*/
markerThreshold?: { x?: number; y?: number };
opacity: number;
fillOpacity: number;
/**
* - `'min'`: draws the area from `yCol` to the bottom
* - `'max'`: draws the area from `yCol` to the top
* - `SerieTableColumn`: draws the area from `yCol` to the column specified by the `SerieTableColumn`
*/
yCol2: SecondOrdinate;
/**
* If `true` this serie value won't show in the tooltip.
*
* *This only works when using the native tooltip*
*/
hideInTooltip: boolean;
/**
* Maps the `col` values to a style definition.
*
* *Returning `null` will make the painting use the default style of the serie*
*
* *Not defining a `styleMapping` will use the actual column value as-is for styling,
* meaning the serie's column can contain style codes directly*
*/
styleMapping?: {
/**
* The index of the column to use for the mapping. The parameter `v` in `mapping(v)` will
* be the cells of that `col`.
*
* (see [SerieTableColumn](#SerieTableColumn) for in-depth explaination)
*/
col: SerieTableColumn;
/**
* @param v the column (`col`) value
* @returns the style used for canvas painting, or `null` to get the default style of the serie
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
mapping?: (v: any) => SerieStyle | null;
};
};
Layout
The chart will by default take the height and width of its parent container. So make sure to set the height and width of the parent container explicitly.
If you wish to modify any other sizing properties, you can do so by setting the following CSS variables:
gui-chart {
/** The margins */
--m-left: 60px;
--m-right: 60px;
--m-top: 10px;
--m-bottom: 25px;
}