diff --git a/packages/o-spreadsheet-engine/src/types/chart/chart.ts b/packages/o-spreadsheet-engine/src/types/chart/chart.ts index d5aa66502e..dd96a6da67 100644 --- a/packages/o-spreadsheet-engine/src/types/chart/chart.ts +++ b/packages/o-spreadsheet-engine/src/types/chart/chart.ts @@ -235,6 +235,7 @@ export interface ChartCreationContext { readonly treemapColoringOptions?: TreeMapColoringOptions; readonly zoomable?: boolean; readonly humanize?: boolean; + readonly slicesColors?: string[]; } export type ChartAxisFormats = { [axisId: string]: Format | undefined } | undefined; diff --git a/packages/o-spreadsheet-engine/src/types/chart/pie_chart.ts b/packages/o-spreadsheet-engine/src/types/chart/pie_chart.ts index 6070949b93..0f75b4e856 100644 --- a/packages/o-spreadsheet-engine/src/types/chart/pie_chart.ts +++ b/packages/o-spreadsheet-engine/src/types/chart/pie_chart.ts @@ -4,6 +4,7 @@ import { CommonChartDefinition } from "./common_chart"; export interface PieChartDefinition extends CommonChartDefinition { readonly type: "pie"; + readonly slicesColors?: string[]; readonly aggregated?: boolean; readonly isDoughnut?: boolean; readonly showValues?: boolean; diff --git a/src/components/side_panel/chart/pie_chart/pie_chart_design_panel.ts b/src/components/side_panel/chart/pie_chart/pie_chart_design_panel.ts index b2fe4ddfe2..8cc2339c2c 100644 --- a/src/components/side_panel/chart/pie_chart/pie_chart_design_panel.ts +++ b/src/components/side_panel/chart/pie_chart/pie_chart_design_panel.ts @@ -1,8 +1,11 @@ -import { PieChartDefinition } from "@odoo/o-spreadsheet-engine/types/chart"; +import { deepCopy } from "@odoo/o-spreadsheet-engine"; +import { PieChartDefinition, PieChartRuntime } from "@odoo/o-spreadsheet-engine/types/chart"; import { SpreadsheetChildEnv } from "@odoo/o-spreadsheet-engine/types/spreadsheet_env"; import { DEFAULT_DOUGHNUT_CHART_HOLE_SIZE } from "@odoo/o-spreadsheet-engine/xlsx/constants"; -import { Component } from "@odoo/owl"; +import { Component, useState } from "@odoo/owl"; 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"; import { GeneralDesignEditor } from "../building_blocks/general_design/general_design_editor"; import { ChartHumanizeNumbers } from "../building_blocks/humanize_numbers/humanize_numbers"; @@ -24,9 +27,24 @@ export class PieChartDesignPanel extends Component< PieHoleSize, Checkbox, ChartHumanizeNumbers, + SidePanelCollapsible, + RoundColorPicker, }; static props = ChartSidePanelPropsObject; + protected state = useState({ index: 0 }); + + private runtime!: PieChartRuntime; + + setup() { + super.setup(); + this.runtime = this.env.model.getters.getChartRuntime(this.props.chartId) as PieChartRuntime; + } + + getLabels() { + return this.runtime.chartJsConfig.data.labels; + } + onPieHoleSizeChange(pieHolePercentage: number) { this.props.updateChart(this.props.chartId, { ...this.props.definition, @@ -36,4 +54,30 @@ export class PieChartDesignPanel extends Component< get defaultHoleSize() { return DEFAULT_DOUGHNUT_CHART_HOLE_SIZE; } + + updateEditedValues(ev: Event) { + this.state.index = (ev.target as HTMLSelectElement).selectedIndex; + } + + updateSliceColor(color: string) { + let slicesColors = deepCopy(this.props.definition.slicesColors); + if (!slicesColors) { + slicesColors = Array(this.getLabels()?.length).fill(""); + } + slicesColors[this.state.index] = color; + this.props.updateChart(this.props.chartId, { + ...this.props.definition, + slicesColors, + }); + } + + getSliceColor() { + const slicesColors = this.props.definition.slicesColors; + if (slicesColors?.[this.state.index]) { + return slicesColors?.[this.state.index]; + } + const dataSets = this.runtime.chartJsConfig.data.datasets; + const color = dataSets[0]?.backgroundColor?.[this.state.index]; + return color; + } } diff --git a/src/components/side_panel/chart/pie_chart/pie_chart_design_panel.xml b/src/components/side_panel/chart/pie_chart/pie_chart_design_panel.xml index 7201dcb750..8e78aece27 100644 --- a/src/components/side_panel/chart/pie_chart/pie_chart_design_panel.xml +++ b/src/components/side_panel/chart/pie_chart/pie_chart_design_panel.xml @@ -16,5 +16,29 @@ /> + + + +
+ +
+
+
+ Series color + +
+
+
+
diff --git a/src/helpers/figures/charts/pie_chart.ts b/src/helpers/figures/charts/pie_chart.ts index 164ad4116c..f599104f86 100644 --- a/src/helpers/figures/charts/pie_chart.ts +++ b/src/helpers/figures/charts/pie_chart.ts @@ -51,6 +51,7 @@ export class PieChart extends AbstractChart { readonly background?: Color; readonly legendPosition: LegendPosition; readonly type = "pie"; + readonly slicesColors?: string[]; readonly aggregated?: boolean; readonly dataSetsHaveTitle: boolean; readonly isDoughnut?: boolean; @@ -73,6 +74,7 @@ export class PieChart extends AbstractChart { this.isDoughnut = definition.isDoughnut; this.showValues = definition.showValues; this.pieHolePercentage = definition.pieHolePercentage; + this.slicesColors = definition.slicesColors; } static transformDefinition( @@ -104,6 +106,7 @@ export class PieChart extends AbstractChart { pieHolePercentage: context.pieHolePercentage, showValues: context.showValues, humanize: context.humanize, + slicesColors: context.slicesColors, }; } @@ -145,6 +148,7 @@ export class PieChart extends AbstractChart { showValues: this.showValues, pieHolePercentage: this.pieHolePercentage, humanize: this.humanize, + slicesColors: this.slicesColors, }; } diff --git a/src/helpers/figures/charts/runtime/chartjs_dataset.ts b/src/helpers/figures/charts/runtime/chartjs_dataset.ts index 6a8142b9d8..20be2d90f2 100644 --- a/src/helpers/figures/charts/runtime/chartjs_dataset.ts +++ b/src/helpers/figures/charts/runtime/chartjs_dataset.ts @@ -277,7 +277,10 @@ export function getPieChartDatasets( const { dataSetsValues } = args; const dataSets: ChartDataset<"pie">[] = []; const dataSetsLength = Math.max(0, ...dataSetsValues.map((ds) => ds?.data?.length ?? 0)); - const backgroundColor = getPieColors(new ColorGenerator(dataSetsLength), dataSetsValues); + const backgroundColor = getPieColors( + new ColorGenerator(dataSetsLength, definition.slicesColors), + dataSetsValues + ); for (const { label, data, hidden } of dataSetsValues) { if (hidden) { continue; diff --git a/src/helpers/figures/charts/runtime/chartjs_legend.ts b/src/helpers/figures/charts/runtime/chartjs_legend.ts index bd0f621581..076dcdec59 100644 --- a/src/helpers/figures/charts/runtime/chartjs_legend.ts +++ b/src/helpers/figures/charts/runtime/chartjs_legend.ts @@ -17,6 +17,7 @@ import { ChartWithDataSetDefinition, GenericDefinition, LineChartDefinition, + PieChartDefinition, SunburstChartDefinition, SunburstChartJSDataset, WaterfallChartDefinition, @@ -70,12 +71,15 @@ export function getLineChartLegend( } export function getPieChartLegend( - definition: GenericDefinition, + definition: GenericDefinition, args: ChartRuntimeGenerationArgs ): ChartLegend { const { dataSetsValues } = args; const dataSetsLength = Math.max(0, ...dataSetsValues.map((ds) => ds?.data?.length ?? 0)); - const colors = getPieColors(new ColorGenerator(dataSetsLength), dataSetsValues); + const colors = getPieColors( + new ColorGenerator(dataSetsLength, definition.slicesColors), + dataSetsValues + ); const fontColor = chartFontColor(definition.background); return { ...getLegendDisplayOptions(definition, args), diff --git a/tests/figures/chart/charts_component.test.ts b/tests/figures/chart/charts_component.test.ts index b88574fc34..63f5de28ff 100644 --- a/tests/figures/chart/charts_component.test.ts +++ b/tests/figures/chart/charts_component.test.ts @@ -731,6 +731,41 @@ describe("charts", () => { ]); }); + test("can edit pie chart slices color", async () => { + createChart( + model, + { + dataSets: [ + { dataRange: "B1:B4", label: "serie_1" }, + { dataRange: "C1:C4", label: "serie_2" }, + ], + labelRange: "A2:A4", + type: "pie", + }, + chartId + ); + await mountChartSidePanel(); + await openChartDesignSidePanel(model, env, fixture, chartId); + + const color_menu = fixture.querySelectorAll(".o-round-color-picker-button")[1]; + + await click(color_menu); + await click(fixture, ".o-color-picker-line-item[data-color='#EFEFEF'"); + //@ts-ignore + expect(model.getters.getChartDefinition(chartId).slicesColors).toEqual(["#EFEFEF", "", ""]); + setInputValueAndTrigger(".pie-slice-selector", "P3"); + + await click(color_menu); + await click(fixture, ".o-color-picker-line-item[data-color='#FF0000'"); + //@ts-ignore + + expect(model.getters.getChartDefinition(chartId).slicesColors).toEqual([ + "#EFEFEF", + "", + "#FF0000", + ]); + }); + test("can edit chart data series vertical axis", async () => { createChart( model, diff --git a/tests/figures/chart/pie_chart_plugin.test.ts b/tests/figures/chart/pie_chart_plugin.test.ts index b8d405b9e2..eb4bc0a068 100644 --- a/tests/figures/chart/pie_chart_plugin.test.ts +++ b/tests/figures/chart/pie_chart_plugin.test.ts @@ -28,6 +28,7 @@ describe("pie chart", () => { pieHolePercentage: 0, showValues: false, humanize: false, + slicesColors: [], }); }); diff --git a/tests/test_helpers/chart_helpers.ts b/tests/test_helpers/chart_helpers.ts index b29a87f445..d82c427300 100644 --- a/tests/test_helpers/chart_helpers.ts +++ b/tests/test_helpers/chart_helpers.ts @@ -136,4 +136,5 @@ export const GENERAL_CHART_CREATION_CONTEXT: Required = { showHeaders: true, zoomable: false, humanize: false, + slicesColors: [], };