Skip to content

Commit

Permalink
Support multiple axis #92
Browse files Browse the repository at this point in the history
  • Loading branch information
y-takey committed Apr 24, 2023
1 parent 4210846 commit 0ddc361
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 21 deletions.
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Requires **Chart.js v3 or higher**.

Demo: https://y-takey.github.io/chartjs-plugin-stacked100

![Result image](https://raw.githubusercontent.com/y-takey/chartjs-plugin-stacked100/master/100%25stacked-bar-chart.png)

## Installation

### npm
Expand Down Expand Up @@ -40,13 +42,14 @@ Chart.register(ChartjsPluginStacked100.default);

## Options

| Name | Type | Default | Description |
| :------------------ | :------ | :------ | :----------------------------------------------------------------------------------------------------------------------------------------- |
| enable | boolean | - | |
| replaceTooltipLabel | boolean | true | replace tooltip label automatically. |
| fixNegativeScale | boolean | true | when datasets has negative value and `fixNegativeScale` is `false`, the nagative scale is variable. (the range is between `-100` and `-1`) |
| individual | boolean | false | scale the highest bar to 100%, and the rest would be proportional to the highest bar of a stack. |
| precision | number | 1 | precision of percentage. the range is between `0` and `16` |
| Name | Type | Default | Description |
| :------------------ | :------ | :------ | :------------------------------------------------------------------------------------------------------------------------------------------ |
| enable | boolean | false | |
| replaceTooltipLabel | boolean | true | replace tooltip label automatically. |
| fixNegativeScale | boolean | true | when datasets has negative value and `fixNegativeScale` is `false`, the nagative scale is variable. (the range is between `-100` and `-1`) |
| individual | boolean | false | scale the highest bar to 100%, and the rest would be proportional to the highest bar of a stack. |
| precision | number | 1 | precision of percentage. the range is between `0` and `16` |
| axisId | string | - | This option allows you to stack only the axis in a chart that includes multiple axes. By default, the plugin will attempt to stack all axes |

## Usage

Expand Down Expand Up @@ -115,8 +118,6 @@ new Chart(document.getElementById("my-chart"), {
});
```

![Result image](https://raw.githubusercontent.com/y-takey/chartjs-plugin-stacked100/master/100%25stacked-bar-chart.png)

#### Datapoints can be Objects

```javascript
Expand Down
83 changes: 83 additions & 0 deletions examples/demo/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,3 +304,86 @@ createChart("Case.11 Relative percentage with stack", {
},
},
});

createChart("Case.12 Multiple axis(combo horizontal bar/line)", {
type: "bar",
data: {
labels: ["Foo", "Bar"],
datasets: [
{
label: "bad",
data: [5, 25],
backgroundColor: COLORS.red,
stack: "stack1",
xAxisID: "axis1",
},
{
label: "better",
data: [15, 10],
backgroundColor: COLORS.yellow,
stack: "stack1",
xAxisID: "axis1",
},
{
type: "line",
label: "good",
data: [43, 24],
backgroundColor: COLORS.blue,
xAxisID: "axis2",
},
],
},
options: {
indexAxis: "y",
plugins: { stacked100: { enable: true, axisId: "axis1" } },
scales: {
axis1: {
position: "top",
},
axis2: {
position: "bottom",
},
},
},
});

createChart("Case.13 Multiple axis(combo vertical bar/line)", {
type: "bar",
data: {
labels: ["Foo", "Bar"],
datasets: [
{
label: "bad",
data: [5, 25],
backgroundColor: COLORS.red,
stack: "stack1",
yAxisID: "axis1",
},
{
label: "better",
data: [15, 10],
backgroundColor: COLORS.yellow,
stack: "stack1",
yAxisID: "axis1",
},
{
type: "line",
label: "good",
data: [43, 24],
backgroundColor: COLORS.blue,
yAxisID: "axis2",
},
],
},
options: {
plugins: { stacked100: { enable: true, axisId: "axis1" } },
scales: {
axis1: {
position: "left",
},
axis2: {
position: "right",
},
},
},
});
61 changes: 49 additions & 12 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,42 @@ export const summarizeValues = (
});
};

const isTargetDataset = (dataset: ChartData["datasets"][0], targetAxisId?: string) => {
if (!targetAxisId) return true;

// FIXME: avoid type error without any cast.
const axisId = (dataset as any).xAxisID || (dataset as any).yAxisID;
return axisId === targetAxisId;
};

const calculateRate = (
data: ExtendedChartData,
visibles: number[],
isHorizontal: boolean,
precision: number,
individual: boolean,
targetAxisId?: string,
) => {
const totals = summarizeValues(data?.datasets, visibles, isHorizontal, individual);

return data.datasets.map((dataset) => {
const isTarget = isTargetDataset(dataset, targetAxisId);

return dataset.data.map((val, j) => {
const total = totals[j][dataset.stack || defaultStackKey];
const dv = dataValue(val, isHorizontal);
if (!isTarget) return dv;

const total = totals[j][dataset.stack || defaultStackKey];

return dv && total ? round(dv / total, precision) : 0;
});
});
};

const tooltipLabel = (isHorizontal: boolean): TooltipCallbacks<ChartType>["label"] => {
const tooltipLabel = (
isHorizontal: boolean,
targetAxisId?: string,
): TooltipCallbacks<ChartType>["label"] => {
return (tooltipItem: TooltipItem<ChartType>) => {
const data = tooltipItem.chart.data as ExtendedChartData;
const datasetIndex = tooltipItem.datasetIndex;
Expand All @@ -69,6 +86,9 @@ const tooltipLabel = (isHorizontal: boolean): TooltipCallbacks<ChartType>["label
const rateValue = data.calculatedData[datasetIndex][index];
const value = dataValue(originalValue, isHorizontal);

if (!isTargetDataset(data.datasets[datasetIndex], targetAxisId)) {
return `${datasetLabel}: ${rateValue}`;
}
return `${datasetLabel}: ${rateValue}% (${value})`;
};
};
Expand All @@ -92,6 +112,20 @@ const getTickOption = (hasNegative: boolean, fixNegativeScale: boolean) => {
return baseOption;
};

const setScaleOption = (
chartInstance: Chart,
axisId: string,
stacked: boolean,
tickOption: Record<string, any>,
) => {
const scaleOption = {
stacked,
...tickOption,
...chartInstance.options.scales[axisId],
};
chartInstance.options.scales[axisId] = scaleOption;
};

export const beforeInit: ExtendedPlugin["beforeInit"] = (chartInstance, args, pluginOptions) => {
if (!pluginOptions.enable) return;
const { replaceTooltipLabel = true, fixNegativeScale = true, individual = false } = pluginOptions;
Expand All @@ -101,20 +135,22 @@ export const beforeInit: ExtendedPlugin["beforeInit"] = (chartInstance, args, pl
const hasNegative = chartInstance.data.datasets.some((dataset) => {
return dataset.data.some((value) => (dataValue(value, isHorizontal) || 0) < 0);
});
["x", "y"].forEach((axis) => {
const tickOption = axis === targetAxis ? getTickOption(hasNegative, fixNegativeScale) : {};
const scaleOption = {
stacked: !individual,
...tickOption,
...chartInstance.options.scales[axis],
};
chartInstance.options.scales[axis] = scaleOption;
});
const tickOption = getTickOption(hasNegative, fixNegativeScale);
if (pluginOptions.axisId) {
setScaleOption(chartInstance, pluginOptions.axisId, !individual, tickOption);
} else {
["x", "y"].forEach((axis) => {
setScaleOption(chartInstance, axis, !individual, axis === targetAxis ? tickOption : {});
});
}

// Replace tooltips
if (!replaceTooltipLabel) return;

chartInstance.options.plugins.tooltip.callbacks.label = tooltipLabel(isHorizontal);
chartInstance.options.plugins.tooltip.callbacks.label = tooltipLabel(
isHorizontal,
pluginOptions.axisId,
);
};

export const beforeUpdate: ExtendedPlugin["beforeUpdate"] = (
Expand All @@ -137,6 +173,7 @@ export const beforeUpdate: ExtendedPlugin["beforeUpdate"] = (
isHorizontalChart(chartInstance),
precision,
pluginOptions.individual,
pluginOptions.axisId,
);
reflectData(data.calculatedData, data.datasets);
};
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface PluginOptions {
fixNegativeScale?: boolean;
individual?: boolean;
precision?: number;
axisId?: string;
}

declare module "chart.js" {
Expand Down

0 comments on commit 0ddc361

Please sign in to comment.