UNPKG

chartjs-chart-funnel

Version:
171 lines (159 loc) 4.76 kB
import { Chart, ChartItem, ScriptableAndArrayOptions, ControllerDatasetOptions, CommonHoverOptions, UpdateMode, ChartConfiguration, Scale, ScriptableContext, CategoryScale, BarController, CoreChartOptions, CartesianScaleTypeRegistry, LinearScale, } from 'chart.js'; import { merge } from 'chart.js/helpers'; import { pickForegroundColorToBackgroundColor, blues } from '../color'; import { TrapezoidElement, TrapezoidElementOptions } from '../elements'; import patchController from './patchController'; export interface FunnelChartOptions { /** * alignment of the elements one of center, left, right * @default center */ align: 'center' | 'left' | 'right'; } export class FunnelController extends BarController { /** * @hidden */ declare options: FunnelChartOptions; static readonly id: string = 'funnel'; /** * @hidden */ static readonly defaults: any = /* #__PURE__ */ merge({}, [ BarController.defaults, { dataElementType: TrapezoidElement.id, barPercentage: 1, align: 'center', categoryPercentage: 0.98, }, ]); /** * @hidden */ static readonly overrides: any = /* #__PURE__ */ merge({}, [ (BarController as any).overrides, { plugins: { legend: { display: false, }, colors: { enabled: false, }, datalabels: { anchor: 'start', // align: 'start', textAlign: 'center', font: { size: 20, }, color: (context: { chart: Chart<'funnel'>; dataset: Chart['data']['datasets'][0]; dataIndex: number; datasetIndex: number; }) => { // auto pick color based on background color const bgColor = context.chart.getDatasetMeta(context.datasetIndex).data[context.dataIndex].options .backgroundColor as string; return pickForegroundColorToBackgroundColor(bgColor, Chart.defaults.color as string, '#ffffff'); }, formatter: (value: number, context: { chart: Chart<'funnel'>; dataIndex: number }) => { const label = context.chart.data.labels?.[context.dataIndex] ?? ''; return `${label}\n${(value * 100).toLocaleString()}%`; }, }, }, scales: { _index_: { display: false, padding: 10, grid: { display: false, }, }, _value_: { display: false, beginAtZero: false, grace: 0, grid: { display: false, }, }, }, elements: { trapezoid: { backgroundColor(context: ScriptableContext<'funnel'>) { const nData = context.chart.data.datasets[context.datasetIndex].data.length; return blues(context.dataIndex, nData); }, }, }, }, ]); /** * @hidden */ getMinMax(scale: Scale, canStack?: boolean | undefined): { min: number; max: number } { const { max } = super.getMinMax(scale, canStack); const r = { // fake mirroring the scale center: { min: -max, max }, left: { min: 0, max }, right: { min: -max, max: 0 }, }[this.options.align]; return r; } /** * @hidden */ update(mode: UpdateMode): void { super.update(mode); const meta = this._cachedMeta; // create a link const elements = (meta.data || []) as unknown as TrapezoidElement[]; for (let i = 0; i < elements.length; i++) { elements[i].align = this.options.align; elements[i].next = elements[i + 1]; elements[i].previous = elements[i - 1]; } } } export interface FunnelControllerDatasetOptions extends ControllerDatasetOptions, ScriptableAndArrayOptions<TrapezoidElementOptions, ScriptableContext<'funnel'>>, ScriptableAndArrayOptions<CommonHoverOptions, ScriptableContext<'funnel'>> {} declare module 'chart.js' { interface ChartTypeRegistry { funnel: { chartOptions: FunnelChartOptions & CoreChartOptions<'funnel'>; datasetOptions: FunnelControllerDatasetOptions; defaultDataPoint: number; metaExtensions: Record<string, never>; parsedDataType: { x: number; y: number }; scales: keyof CartesianScaleTypeRegistry; }; } } export class FunnelChart<DATA extends unknown[] = number[], LABEL = string> extends Chart<'funnel', DATA, LABEL> { static id = FunnelController.id as 'funnel'; constructor(item: ChartItem, config: Omit<ChartConfiguration<'funnel', DATA, LABEL>, 'type'>) { super(item, patchController('funnel', config, FunnelController, TrapezoidElement, [CategoryScale, LinearScale])); } }