Skip to content

Commit

Permalink
[IMP] charts: add show values options for charts
Browse files Browse the repository at this point in the history
Task Description

This task aims to add the possibility to show the value of the
serie on the chart (above the point for scatter/line chart, above
the bar for bar/combo chart and inside part for pie chart).

Related Task

closes #4324

Task: 3953835
Signed-off-by: Adrien Minne (adrm) <[email protected]>
  • Loading branch information
anhe-odoo committed Jul 11, 2024
1 parent e0f506b commit 117a5e5
Show file tree
Hide file tree
Showing 33 changed files with 685 additions and 28 deletions.
2 changes: 2 additions & 0 deletions src/components/figures/chart/chartJs/chartjs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { Chart, ChartConfiguration } from "chart.js/auto";
import { deepCopy } from "../../../../helpers";
import { Figure, SpreadsheetChildEnv } from "../../../../types";
import { ChartJSRuntime } from "../../../../types/chart/chart";
import { chartShowValuesPlugin } from "./chartjs_show_values_plugin";
import { waterfallLinesPlugin } from "./chartjs_waterfall_plugin";

interface Props {
figure: Figure;
}

window.Chart?.register(waterfallLinesPlugin);
window.Chart?.register(chartShowValuesPlugin);

export class ChartJsComponent extends Component<Props, SpreadsheetChildEnv> {
static template = "o-spreadsheet-ChartJsComponent";
Expand Down
96 changes: 96 additions & 0 deletions src/components/figures/chart/chartJs/chartjs_show_values_plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { ChartType, Plugin } from "chart.js";
import { chartFontColor } from "../../../../helpers/figures/charts/chart_common";
import { Color } from "../../../../types";

interface ChartShowValuesPluginOptions {
showValues: boolean;
background?: Color;
horizontal?: boolean;
callback: (value: number | string) => string;
}

declare module "chart.js" {
interface PluginOptionsByType<TType extends ChartType> {
chartShowValuesPlugin?: ChartShowValuesPluginOptions;
}
}

/** This is a chartJS plugin that will draw the values of each data next to the point/bar/pie slice */
export const chartShowValuesPlugin: Plugin = {
id: "chartShowValuesPlugin",
afterDatasetsDraw(chart: any, args, options: ChartShowValuesPluginOptions) {
if (!options.showValues) {
return;
}
const drawData = chart._metasets?.[0]?.data;
if (!drawData) {
return;
}
const ctx = chart.ctx;
ctx.save();

ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = chartFontColor(options.background);
ctx.strokeStyle = chartFontColor(ctx.fillStyle);

chart._metasets.forEach(function (dataset) {
switch (dataset.type) {
case "doughnut":
case "pie": {
for (let i = 0; i < dataset._parsed.length; i++) {
const bar = dataset.data[i];
const { startAngle, endAngle, innerRadius, outerRadius } = bar;
const midAngle = (startAngle + endAngle) / 2;
const midRadius = (innerRadius + outerRadius) / 2;
const x = bar.x + midRadius * Math.cos(midAngle);
const y = bar.y + midRadius * Math.sin(midAngle) + 7;

ctx.fillStyle = chartFontColor(bar.options.backgroundColor);
ctx.strokeStyle = chartFontColor(ctx.fillStyle);

const value = options.callback(dataset._parsed[i]);
ctx.strokeText(value, x, y);
ctx.fillText(value, x, y);
}
break;
}
case "bar":
case "line": {
const yOffset = dataset.type === "bar" && !options.horizontal ? 0 : 3;
for (let i = 0; i < dataset._parsed.length; i++) {
const point = dataset.data[i];
const value = options.horizontal ? dataset._parsed[i].x : dataset._parsed[i].y;
const displayedValue = options.callback(value - 0);
let xPosition = 0,
yPosition = 0;
if (options.horizontal) {
yPosition = point.y;
if (value < 0) {
ctx.textAlign = "right";
xPosition = point.x - yOffset;
} else {
ctx.textAlign = "left";
xPosition = point.x + yOffset;
}
} else {
xPosition = point.x;
if (value < 0) {
ctx.textBaseline = "top";
yPosition = point.y + yOffset;
} else {
ctx.textBaseline = "bottom";
yPosition = point.y - yOffset;
}
}
ctx.strokeText(displayedValue, xPosition, yPosition);
ctx.fillText(displayedValue, xPosition, yPosition);
}
break;
}
}
});

ctx.restore();
},
};
11 changes: 11 additions & 0 deletions src/components/side_panel/chart/chart_with_axis/design_panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
SpreadsheetChildEnv,
UID,
} from "../../../../types/index";
import { ChartTerms } from "../../../translations_terms";
import { Checkbox } from "../../components/checkbox/checkbox";
import { SidePanelCollapsible } from "../../components/collapsible/side_panel_collapsible";
import { RoundColorPicker } from "../../components/round_color_picker/round_color_picker";
import { Section } from "../../components/section/section";
Expand All @@ -32,6 +34,7 @@ export class ChartWithAxisDesignPanel extends Component<Props, SpreadsheetChildE
Section,
AxisDesignEditor,
RoundColorPicker,
Checkbox,
};
static props = {
figureId: String,
Expand Down Expand Up @@ -131,4 +134,12 @@ export class ChartWithAxisDesignPanel extends Component<Props, SpreadsheetChildE
const dataSets = this.props.definition.dataSets;
return dataSets[this.state.index]?.label || this.getDataSeries()[this.state.index];
}

get showValuesLabel(): string {
return ChartTerms.ShowValues;
}

updateShowValues(showValues: boolean) {
this.props.updateChart(this.props.figureId, { showValues });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@
<option value="right">Right</option>
</select>
</Section>
<Section class="'pt-0'">
<Checkbox
name="'showValues'"
label="showValuesLabel"
value="props.definition.showValues"
onChange="showValues => props.updateChart(this.props.figureId, {showValues})"
/>
</Section>
</t>
</GeneralDesignEditor>
<SidePanelCollapsible collapsedAtInit="true">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Component } from "@odoo/owl";
import { DispatchResult, SpreadsheetChildEnv, UID } from "../../../../types";
import { PieChartDefinition } from "../../../../types/chart";
import { ChartTerms } from "../../../translations_terms";
import { Checkbox } from "../../components/checkbox/checkbox";
import { Section } from "../../components/section/section";
import { GeneralDesignEditor } from "../building_blocks/general_design/general_design_editor";

Expand All @@ -16,6 +18,7 @@ export class PieChartDesignPanel extends Component<Props, SpreadsheetChildEnv> {
static components = {
GeneralDesignEditor,
Section,
Checkbox,
};
static props = {
figureId: String,
Expand All @@ -29,4 +32,8 @@ export class PieChartDesignPanel extends Component<Props, SpreadsheetChildEnv> {
legendPosition: ev.target.value,
});
}

get showValuesLabel(): string {
return ChartTerms.ShowValues;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@
<option value="right">Right</option>
</select>
</Section>
<Section>
<Checkbox
name="'showValues'"
label="showValuesLabel"
value="props.definition.showValues"
onChange="showValues => props.updateChart(this.props.figureId, {showValues})"
/>
</Section>
</t>
</GeneralDesignEditor>
</t>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
import { _t } from "../../../../translation";
import { Color, DispatchResult, SpreadsheetChildEnv, UID } from "../../../../types";
import { WaterfallChartDefinition } from "../../../../types/chart/waterfall_chart";
import { ChartTerms } from "../../../translations_terms";
import { SidePanelCollapsible } from "../../components/collapsible/side_panel_collapsible";
import { RoundColorPicker } from "../../components/round_color_picker/round_color_picker";
import { Section } from "../../components/section/section";
Expand Down Expand Up @@ -96,4 +97,12 @@ export class WaterfallChartDesignPanel extends Component<Props, SpreadsheetChild
verticalAxisPosition: ev.target.value,
});
}

get showValuesLabel(): string {
return ChartTerms.ShowValues;
}

updateShowValues(showValues: boolean) {
this.props.updateChart(this.props.figureId, { showValues });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@
<option value="right">Right</option>
</select>
</Section>
<Section>
<Checkbox
name="'showValues'"
label="showValuesLabel"
value="props.definition.showValues"
onChange="showValues => props.updateChart(this.props.figureId, {showValues})"
/>
</Section>
</t>
</GeneralDesignEditor>
<SidePanelCollapsible collapsedAtInit="true">
Expand Down
1 change: 1 addition & 0 deletions src/components/translations_terms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export const ChartTerms = {
CumulativeData: _t("Cumulative data"),
TreatLabelsAsText: _t("Treat labels as text"),
AggregatedChart: _t("Aggregate"),
ShowValues: _t("Show values"),
Errors: {
Unexpected: _t("The chart definition is invalid for an unknown reason"),
// BASIC CHART ERRORS (LINE | BAR | PIE)
Expand Down
30 changes: 21 additions & 9 deletions src/helpers/figures/charts/bar_chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export class BarChart extends AbstractChart {
readonly dataSetDesign?: DatasetDesign[];
readonly axesDesign?: AxesDesign;
readonly horizontal?: boolean;
readonly showValues?: boolean;

constructor(definition: BarChartDefinition, sheetId: UID, getters: CoreGetters) {
super(definition, sheetId, getters);
Expand All @@ -85,6 +86,7 @@ export class BarChart extends AbstractChart {
this.dataSetDesign = definition.dataSets;
this.axesDesign = definition.axesDesign;
this.horizontal = definition.horizontal;
this.showValues = definition.showValues;
}

static transformDefinition(
Expand Down Expand Up @@ -113,6 +115,7 @@ export class BarChart extends AbstractChart {
type: "bar",
labelRange: context.auxiliaryRange || undefined,
axesDesign: context.axesDesign,
showValues: context.showValues,
};
}

Expand Down Expand Up @@ -179,6 +182,7 @@ export class BarChart extends AbstractChart {
aggregated: this.aggregated,
axesDesign: this.axesDesign,
horizontal: this.horizontal,
showValues: this.showValues,
};
}

Expand Down Expand Up @@ -243,21 +247,23 @@ function getBarConfiguration(
};
config.options.indexAxis = chart.horizontal ? "y" : "x";

const formatCallback = (value) => {
value = Number(value);
if (isNaN(value)) return value;
const { locale, format } = localeFormat;
return formatValue(value, {
locale,
format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
});
};

config.options.scales = {};
const labelsAxis = { ticks: { padding: 5, color: fontColor } };
const valuesAxis = {
beginAtZero: true, // the origin of the y axis is always zero
ticks: {
color: fontColor,
callback: (value) => {
value = Number(value);
if (isNaN(value)) return value;
const { locale, format } = localeFormat;
return formatValue(value, {
locale,
format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
});
},
callback: formatCallback,
},
};

Expand Down Expand Up @@ -292,6 +298,12 @@ function getBarConfiguration(
config.options.scales!.y1!.stacked = true;
}
}
config.options.plugins!.chartShowValuesPlugin = {
showValues: chart.showValues,
background: chart.background,
horizontal: chart.horizontal,
callback: formatCallback,
};
return config;
}

Expand Down
24 changes: 15 additions & 9 deletions src/helpers/figures/charts/chart_common_line_scatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,19 +150,20 @@ function getLineOrScatterConfiguration(
title: getChartAxisTitleRuntime(chart.axesDesign?.x),
},
};
const formatCallback = (value) => {
value = Number(value);
if (isNaN(value)) return value;
const { locale, format } = options;
return formatValue(value, {
locale,
format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
});
};
const yAxis = {
beginAtZero: true, // the origin of the y axis is always zero
ticks: {
color: fontColor,
callback: (value) => {
value = Number(value);
if (isNaN(value)) return value;
const { locale, format } = options;
return formatValue(value, {
locale,
format: !format && Math.abs(value) >= 1000 ? "#,##" : format,
});
},
callback: formatCallback,
},
};
const { useLeftAxis, useRightAxis } = getDefinedAxis(chart.getDefinition());
Expand Down Expand Up @@ -190,6 +191,11 @@ function getLineOrScatterConfiguration(
config.options.scales!.y1!.stacked = true;
}
}
config.options.plugins!.chartShowValuesPlugin = {
showValues: chart.showValues,
background: chart.background,
callback: formatCallback,
};
return config;
}

Expand Down
Loading

0 comments on commit 117a5e5

Please sign in to comment.