6.10.94-stable

<gui-chart />

Simple in-memory usage

const el = document.createElement('gui-chart');
el.value = {
  cols: [
    [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
  core.nodeTime.sample([timeSeries], from, to, 100, core.SamplingMode.adaptative(), null, null).then((table) => {
    el.value = table
  }) 
}

//Fetch the node containing the time series
runtime.Runtime.readModVar("project.exampleTimeSeries").then((node) => {
  timeSeries = node;
  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 = core.time.fromMs(from);
  to = core.time.fromMs(to);

  sample(from, to)
});
el.addEventListener('reset-selection', () => {
  sample(null, null)
});

Multiple series

const el = document.createElement('gui-chart');
el.value = {
  cols: [
    [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 = {
  cols: [
    [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 = {
  cols: [
    [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 = {
  cols: [
    [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 = {
  cols: [
    [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 = {
  cols: [
    [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 = {
  cols: [
    [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 = {
  cols: [
    [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 = {
  cols: [
    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 = {
  cols: [
    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,
      styleMapping: (v) => {
        if (v < 0) {
          return {
            fill: 'red',

          };
        }
        return {
          color: 'green',
        };
      }
    },
  ],
};

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.config = {
  table: { cols: histogram },
  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.config = {
  table: { cols: histogram },
  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);
});
input

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'
    }
  },
}

function sampleHistogram(from, to) {
  return greycat.default.call('project::histogramExample', [from, to, parseInt(input.value)]).then((table) => {
    chart.value = table;
  })
}

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::new();

  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 = {
  table: { cols: [[]] },
  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 = {
  table: { cols: [[]] },
  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.

The canvas context is our internal context, it is a wrapper around the native canvas context. It exposes functions to help you draw. If you need to use a native canvas function, you can access the native context with `ctx.ctx`.
const el = document.createElement('gui-chart');
el.value = {
  rows: [
    [new Date('2024-01-01T15:15:00Z'), -2.5],
    [new Date('2024-01-02T15:15:00Z'), 1.458],
    [new Date('2024-01-03T15:15:00Z'), 0.009],
    [new Date('2024-01-04T15:15:00Z'), 5.64],
  ],
  meta: ['Time', 'Value'],
};
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
selection GuiChartSelectionEventDetail Triggered when selecting an area on the canvas
reset-selection Triggered when canvas is double-clicked
cursor GuiChartCursorEventDetail Triggered when hoovering over the canvas
gui-enter Triggered when entering the canvas area
gui-leave Triggered when leaving the canvas area

GuiChartSelectionEventDetail

Useful if you want to resample the serie when the user selects an area, just 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.

detail {
  from : unknown,
  to: unknown
}

GuiChartCursorEventDetail

detail {
  data : SerieData[]
  cursor: Cursor
}

export type Cursor = {
  x: number;
  y: number;
  startX: number;
  startY: number;
  selection: boolean;
}

Config

export interface ChartConfig<K = { [keys: string]: never }> {
  table: TableLike;
  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
   *
   * If undefined, the array index will be used
   */
  xCol?: number;
  /**
   * offset of the y column in the given table
   */
  yCol: number;
  /**
   * must refer to a defined 'key' in `config.yAxes`
   * and will be used as the y-axis for this serie
   */
  yAxis: K;
  /**
   * offset of the column in the table to use to read
   * lineType values for each x
   */
  lineTypeCol?: number;
  /**
   * offset of the column in the table to use to read
   * the line color values for segment
   */
  colorCol?: number;
  /**
   * Optional title used to name the serie.
   */
  title?: 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;
}

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 = {
  position: TooltipPosition;
  /**
   * Called whenever the tooltip should update its content.
   *
   * *If this is defined, the default tooltip will not display.*
   *
   * @param data
   * @returns
   */
  render?: (data: SerieData[]) => void;
};

export type CommonAxis = {
  title?: string;
  min?: number | Date | core.time | core.Date;
  max?: number | Date | core.time | core.Date;
  /**
   * Formats the ticks on the axis
   * See https://d3js.org/d3-format#format
   *
   * If the `scale` is `'time'` and `format` is `undefined`, the display is defaulting to ISO.
   * See https://d3js.org/d3-time-format#utcFormat
   */
  format?: string;
  /**
   * Formats the cursor hover text on the axis
   *
   * See https://d3js.org/d3-format#format for number values
   * See https://d3js.org/d3-time-format#utcFormat for time values in UTC
   */
  cursorFormat?: string;
  /**
   * Zoom ratio
   */
  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 interface LinearAxis extends CommonAxis {
  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[];
}

export interface LogAxis extends CommonAxis {
  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[];
}

export interface TimeAxis extends CommonAxis {
  scale: 'time';
  /**
   * Time axis can also leverage `d3.TimeInterval` by specifying for instance `d3.utcHour.every(24)`
   */
  ticks?: d3.TimeInterval | (core.time | core.Date | Date | number)[] | null;
}

export type Axis = LinearAxis | LogAxis | TimeAxis;

export type Ordinate = Axis & { position?: AxisPosition };

export type SerieOptions = {
  color: string;
  width: number;
  markerWidth: number;
  markerShape: MarkerShape;
  markerColor: string;
  opacity: number;
  fillOpacity: number;
  /**
   * - `'min'`: draws the area from `yCol` to the bottom
   * - `'max'`: draws the area from `yCol` to the top
   * - `<number>`: draws the area from `yCol` to the column at offset `<number>`
   */
  yCol2: SecondOrdinate;
  /**
   * Maps the col values (from `colorCol`) to a color definition.
   *
   * *Returning `null` or `undefined` will make the painting use the default color of the serie*
   *
   * *Not defining a `colorMapping` will use the value as-is for coloring, meaning the serie's column can contain color codes directly*
   *
   * @param v the current cell value
   * @returns the color used for canvas painting
   */
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  colorMapping?: (v: any) => Color | null | undefined;
};

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-heatmap {
  /** The margins */
  --m-left: 60px;
  --m-right: 60px;
  --m-top: 10px;
  --m-bottom: 25px;
}