Skip to content

Commit

Permalink
[IMP] charts: allow to invert columns/rows
Browse files Browse the repository at this point in the history
Task Description

This task add the possibility for the user to invert the columns
 and rows of a chart dataset.

Related Task

Task: 3693182
  • Loading branch information
anhe-odoo committed Jan 14, 2025
1 parent 28722d2 commit 73cd6de
Show file tree
Hide file tree
Showing 31 changed files with 223 additions and 156 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@
onSelectionChanged="(ranges) => this.onDataSeriesRangesChanged(ranges)"
onSelectionConfirmed="() => this.onDataSeriesConfirmed()"
/>
<Section class="'pt-0'">
<Checkbox
name="'invertAxes'"
label="chartTerms.InvertAxes"
value="props.definition.invertAxes"
onChange.bind="onInvertAxesToggled"
className="'pt-0'"
/>
</Section>
<ChartLabelRange
range="this.getLabelRange()"
isInvalid="isLabelInvalid"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { Component, useState } from "@odoo/owl";
import { createValidRange, spreadRange } from "../../../../../helpers";
import {
createValidRange,
mergeContiguousZones,
numberToLetters,
splitReference,
spreadRange,
toZone,
zoneToXc,
} from "../../../../../helpers";
import { createDataSets } from "../../../../../helpers/figures/charts";
import { _t } from "../../../../../translation";
import {
Expand Down Expand Up @@ -30,6 +38,7 @@ interface Props {
interface ChartPanelState {
datasetDispatchResult?: DispatchResult;
labelsDispatchResult?: DispatchResult;
invertAxesForDataSets?: boolean;
}

export class GenericChartConfigPanel extends Component<Props, SpreadsheetChildEnv> {
Expand Down Expand Up @@ -82,7 +91,13 @@ export class GenericChartConfigPanel extends Component<Props, SpreadsheetChildEn
}

get dataSetsHaveTitleLabel(): string {
return _t("Use row %s as headers", this.calculateHeaderPosition() || "");
return this.invertAxes
? _t("Use col %s as headers", numberToLetters(this.calculateHeaderPosition() || 0))
: _t("Use row %s as headers", this.calculateHeaderPosition() || "");
}

get invertAxes(): boolean {
return this.props.definition.invertAxes ?? false;
}

getLabelRangeOptions() {
Expand All @@ -108,6 +123,60 @@ export class GenericChartConfigPanel extends Component<Props, SpreadsheetChildEn
});
}

onInvertAxesToggled(invertAxes: boolean) {
const OldDataSets = this.props.definition.dataSets;
const dataRanges = OldDataSets.map((d) => d.dataRange);
const dataSets = this.transposeDataSet([this.props.definition.labelRange, ...dataRanges]);
const labelRange = dataSets.shift().dataRange;

this.props.updateChart(this.props.figureId, {
invertAxes,
labelRange,
dataSets,
});
this.dataSeriesRanges = dataSets;
this.labelRange = labelRange;
}

private transposeDataSet(dataRanges: any[]): any[] {
const zonesBySheetName = {};
const transposedDatasets: any[] = [];
for (const dataRange of dataRanges) {
let { sheetName, xc } = splitReference(dataRange);
sheetName = sheetName ?? "";
if (!zonesBySheetName[sheetName]) {
zonesBySheetName[sheetName] = [];
}
zonesBySheetName[sheetName].push(toZone(xc));
}
for (const sheetName in zonesBySheetName) {
const zones = zonesBySheetName[sheetName];
const contiguousZones = mergeContiguousZones(zones);
if (this.invertAxes) {
for (const zone of contiguousZones) {
for (let col = zone.left; col <= zone.right; col++) {
let newRange = zoneToXc({ top: zone.top, bottom: zone.bottom, left: col, right: col });
if (sheetName) {
newRange = `${sheetName}!${newRange}`;
}
transposedDatasets.push({ dataRange: newRange });
}
}
} else {
for (const zone of contiguousZones) {
for (let row = zone.top; row <= zone.bottom; row++) {
let newRange = zoneToXc({ top: row, bottom: row, left: zone.left, right: zone.right });
if (sheetName) {
newRange = `${sheetName}!${newRange}`;
}
transposedDatasets.push({ dataRange: newRange });
}
}
}
}
return transposedDatasets;
}

/**
* Change the local dataSeriesRanges. The model should be updated when the
* button "confirm" is clicked
Expand Down Expand Up @@ -174,7 +243,7 @@ export class GenericChartConfigPanel extends Component<Props, SpreadsheetChildEn
this.props.definition.dataSetsHaveTitle
);
if (dataSets.length) {
return dataSets[0].dataRange.zone.top + 1;
return this.invertAxes ? dataSets[0].dataRange.zone.left : dataSets[0].dataRange.zone.top + 1;
} else if (labelRange) {
return labelRange.zone.top + 1;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@
onSelectionChanged="(ranges) => this.onDataSeriesRangesChanged(ranges)"
onSelectionConfirmed="() => this.onDataSeriesConfirmed()"
/>
<Section class="'pt-0'">
<Checkbox
name="'invertAxes'"
label="chartTerms.InvertAxes"
value="invertAxes"
onChange.bind="onInvertAxesToggled"
className="'pt-0'"
/>
</Section>
<ChartLabelRange
range="this.getLabelRange()"
isInvalid="isLabelInvalid"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@ import { GenericChartConfigPanel } from "../building_blocks/generic_side_panel/c

export class ComboChartConfigPanel extends GenericChartConfigPanel {
static template = "o-spreadsheet-ComboChartConfigPanel";

onInvertAxesToggled(invertAxes: boolean) {
this.props.updateChart(this.props.figureId, {
invertAxes,
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@
onSelectionChanged="(ranges) => this.onDataSeriesRangesChanged(ranges)"
onSelectionConfirmed="() => this.onDataSeriesConfirmed()"
/>
<Section class="'pt-0'">
<Checkbox
name="'invertAxes'"
label="chartTerms.InvertAxes"
value="props.definition.invertAxes"
onChange.bind="onInvertAxesToggled"
className="'pt-0'"
/>
</Section>
<ChartLabelRange
range="this.getLabelRange()"
isInvalid="isLabelInvalid"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@
onSelectionChanged="(ranges) => this.onDataSeriesRangesChanged(ranges)"
onSelectionConfirmed="() => this.onDataSeriesConfirmed()"
/>
<Section class="'pt-0'">
<Checkbox
name="'invertAxes'"
label="chartTerms.InvertAxes"
value="props.definition.invertAxes"
onChange.bind="onInvertAxesToggled"
className="'pt-0'"
/>
</Section>
<ChartLabelRange
range="this.getLabelRange()"
isInvalid="isLabelInvalid"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@
onSelectionChanged="(ranges) => this.onDataSeriesRangesChanged(ranges)"
onSelectionConfirmed="() => this.onDataSeriesConfirmed()"
/>
<Section class="'pt-0'">
<Checkbox
name="'invertAxes'"
label="chartTerms.InvertAxes"
value="props.definition.invertAxes"
onChange.bind="onInvertAxesToggled"
className="'pt-0'"
/>
</Section>
<ChartLabelRange
range="this.getLabelRange()"
isInvalid="isLabelInvalid"
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 @@ -61,6 +61,7 @@ export const ChartTerms = {
CumulativeData: _t("Cumulative data"),
TreatLabelsAsText: _t("Treat labels as text"),
AggregatedChart: _t("Aggregate"),
InvertAxes: _t("Switch rows/columns"),
Errors: {
Unexpected: _t("The chart definition is invalid for an unknown reason"),
// BASIC CHART ERRORS (LINE | BAR | PIE)
Expand Down
7 changes: 6 additions & 1 deletion src/helpers/figures/charts/bar_chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export class BarChart extends AbstractChart {
readonly axesDesign?: AxesDesign;
readonly horizontal?: boolean;
readonly showValues?: boolean;
readonly invertAxes?: boolean;

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

static transformDefinition(
Expand Down Expand Up @@ -114,6 +116,7 @@ export class BarChart extends AbstractChart {
labelRange: context.auxiliaryRange || undefined,
axesDesign: context.axesDesign,
showValues: context.showValues,
invertAxes: context.invertAxes,
};
}

Expand Down Expand Up @@ -181,6 +184,7 @@ export class BarChart extends AbstractChart {
axesDesign: this.axesDesign,
horizontal: this.horizontal,
showValues: this.showValues,
invertAxes: this.invertAxes,
};
}

Expand Down Expand Up @@ -228,7 +232,8 @@ export function createBarChartRuntime(chart: BarChart, getters: Getters): BarCha
const config: ChartConfiguration = {
type: "bar",
data: {
labels: chartData.labels.map(truncateLabel),
labels:
chartData.axisType !== "time" ? chartData.labels.map(truncateLabel) : chartData.labels,
datasets: getBarChartDatasets(definition, chartData),
},
options: {
Expand Down
4 changes: 4 additions & 0 deletions src/helpers/figures/charts/combo_chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export class ComboChart extends AbstractChart {
readonly axesDesign?: AxesDesign;
readonly type = "combo";
readonly showValues?: boolean;
readonly invertAxes?: boolean;

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

static transformDefinition(
Expand Down Expand Up @@ -147,6 +149,7 @@ export class ComboChart extends AbstractChart {
aggregated: this.aggregated,
axesDesign: this.axesDesign,
showValues: this.showValues,
invertAxes: this.invertAxes,
};
}

Expand Down Expand Up @@ -204,6 +207,7 @@ export class ComboChart extends AbstractChart {
type: "combo",
axesDesign: context.axesDesign,
showValues: context.showValues,
invertAxes: context.invertAxes,
};
}

Expand Down
4 changes: 4 additions & 0 deletions src/helpers/figures/charts/line_chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export class LineChart extends AbstractChart {
readonly axesDesign?: AxesDesign;
readonly fillArea?: boolean;
readonly showValues?: boolean;
readonly invertAxes?: boolean;

constructor(definition: LineChartDefinition, sheetId: UID, getters: CoreGetters) {
super(definition, sheetId, getters);
Expand All @@ -90,6 +91,7 @@ export class LineChart extends AbstractChart {
this.axesDesign = definition.axesDesign;
this.fillArea = definition.fillArea;
this.showValues = definition.showValues;
this.invertAxes = definition.invertAxes;
}

static validateChartDefinition(
Expand Down Expand Up @@ -122,6 +124,7 @@ export class LineChart extends AbstractChart {
axesDesign: context.axesDesign,
fillArea: context.fillArea,
showValues: context.showValues,
invertAxes: context.invertAxes,
};
}

Expand Down Expand Up @@ -158,6 +161,7 @@ export class LineChart extends AbstractChart {
axesDesign: this.axesDesign,
fillArea: this.fillArea,
showValues: this.showValues,
invertAxes: this.invertAxes,
};
}

Expand Down
4 changes: 4 additions & 0 deletions src/helpers/figures/charts/pie_chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export class PieChart extends AbstractChart {
readonly dataSetsHaveTitle: boolean;
readonly isDoughnut?: boolean;
readonly showValues?: boolean;
readonly invertAxes?: boolean;

constructor(definition: PieChartDefinition, sheetId: UID, getters: CoreGetters) {
super(definition, sheetId, getters);
Expand All @@ -74,6 +75,7 @@ export class PieChart extends AbstractChart {
this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
this.isDoughnut = definition.isDoughnut;
this.showValues = definition.showValues;
this.invertAxes = definition.invertAxes;
}

static transformDefinition(
Expand Down Expand Up @@ -102,6 +104,7 @@ export class PieChart extends AbstractChart {
aggregated: context.aggregated ?? false,
isDoughnut: false,
showValues: context.showValues,
invertAxes: context.invertAxes,
};
}

Expand Down Expand Up @@ -141,6 +144,7 @@ export class PieChart extends AbstractChart {
aggregated: this.aggregated,
isDoughnut: this.isDoughnut,
showValues: this.showValues,
invertAxes: this.invertAxes,
};
}

Expand Down
4 changes: 4 additions & 0 deletions src/helpers/figures/charts/radar_chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export class RadarChart extends AbstractChart {
readonly dataSetDesign?: DatasetDesign[];
readonly fillArea?: boolean;
readonly showValues?: boolean;
readonly invertAxes?: boolean;

constructor(definition: RadarChartDefinition, sheetId: UID, getters: CoreGetters) {
super(definition, sheetId, getters);
Expand All @@ -81,6 +82,7 @@ export class RadarChart extends AbstractChart {
this.dataSetDesign = definition.dataSets;
this.fillArea = definition.fillArea;
this.showValues = definition.showValues;
this.invertAxes = definition.invertAxes;
}

static transformDefinition(
Expand Down Expand Up @@ -110,6 +112,7 @@ export class RadarChart extends AbstractChart {
labelRange: context.auxiliaryRange || undefined,
fillArea: context.fillArea ?? false,
showValues: context.showValues ?? false,
invertAxes: context.invertAxes,
};
}

Expand Down Expand Up @@ -176,6 +179,7 @@ export class RadarChart extends AbstractChart {
aggregated: this.aggregated,
fillArea: this.fillArea,
showValues: this.showValues,
invertAxes: this.invertAxes,
};
}

Expand Down
2 changes: 1 addition & 1 deletion src/helpers/figures/charts/runtime/chartjs_dataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ export function getLineChartDatasets(
export function getScatterChartDatasets(
definition: GenericDefinition<ScatterChartDefinition>,
args: ChartRuntimeGenerationArgs
): ChartDataset[] {
): ChartDataset<"line">[] {
const dataSets: ChartDataset<"line">[] = getLineChartDatasets(definition, args);
for (const dataSet of dataSets) {
if (dataSet.xAxisID !== TREND_LINE_XAXIS_ID) {
Expand Down
Loading

0 comments on commit 73cd6de

Please sign in to comment.