From d8abec538094466b7ca6b12c15c56aa24b8fd32a Mon Sep 17 00:00:00 2001 From: Xavier Leune Date: Wed, 7 May 2025 14:57:09 +0200 Subject: [PATCH] Fix: display stacked bar with multiple x-Axis --- src/controllers/controller.bar.js | 28 +++++++- .../stacking/stacked-and-multiple-axis.js | 64 ++++++++++++++++++ .../stacking/stacked-and-multiple-axis.png | Bin 0 -> 14649 bytes 3 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 test/fixtures/controller.bar/stacking/stacked-and-multiple-axis.js create mode 100644 test/fixtures/controller.bar/stacking/stacked-and-multiple-axis.png diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index 82138f3fb74..4bab7cb00ff 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -486,6 +486,25 @@ export default class BarController extends DatasetController { return this._getStacks(undefined, index).length; } + _getAxisCount() { + return this._getAxis().length; + } + + getFirstScaleIdForXAxis() { + const scales = this.chart.scales; + return Object.keys(scales).filter(key => scales[key].axis === 'x').shift(); + } + + _getAxis() { + const axis = {}; + const firstScaleAxisId = this.getFirstScaleIdForXAxis(); + for (const dataset of this.chart.data.datasets) { + axis[valueOrDefault(dataset.xAxisID, firstScaleAxisId)] = true; + } + + return Object.keys(axis); + } + /** * Returns the stack index for the given dataset based on groups and bar visibility. * @param {number} [datasetIndex] - The dataset index @@ -618,13 +637,15 @@ export default class BarController extends DatasetController { const skipNull = options.skipNull; const maxBarThickness = valueOrDefault(options.maxBarThickness, Infinity); let center, size; + const axisCount = this._getAxisCount(); if (ruler.grouped) { const stackCount = skipNull ? this._getStackCount(index) : ruler.stackCount; const range = options.barThickness === 'flex' - ? computeFlexCategoryTraits(index, ruler, options, stackCount) - : computeFitCategoryTraits(index, ruler, options, stackCount); + ? computeFlexCategoryTraits(index, ruler, options, stackCount * axisCount) + : computeFitCategoryTraits(index, ruler, options, stackCount * axisCount); - const stackIndex = this._getStackIndex(this.index, this._cachedMeta.stack, skipNull ? index : undefined); + const axisNumber = this._getAxis().indexOf(valueOrDefault(this.getDataset().xAxisID, this.getFirstScaleIdForXAxis())); + const stackIndex = this._getStackIndex(this.index, this._cachedMeta.stack, skipNull ? index : undefined) + axisNumber; center = range.start + (range.chunk * stackIndex) + (range.chunk / 2); size = Math.min(maxBarThickness, range.chunk * range.ratio); } else { @@ -633,6 +654,7 @@ export default class BarController extends DatasetController { size = Math.min(maxBarThickness, ruler.min * ruler.ratio); } + return { base: center - size / 2, head: center + size / 2, diff --git a/test/fixtures/controller.bar/stacking/stacked-and-multiple-axis.js b/test/fixtures/controller.bar/stacking/stacked-and-multiple-axis.js new file mode 100644 index 00000000000..ca5490a9291 --- /dev/null +++ b/test/fixtures/controller.bar/stacking/stacked-and-multiple-axis.js @@ -0,0 +1,64 @@ +module.exports = { + config: { + type: 'bar', + data: { + labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], + datasets: [ + { + label: 'Dataset 1', + data: [100, 90, 100, 50, 99, 87, 34], + backgroundColor: 'rgba(255,99,132,0.8)', + stack: 'a', + xAxisID: 'x' + }, + { + label: 'Dataset 2', + data: [20, 25, 30, 32, 58, 14, 12], + backgroundColor: 'rgba(54,162,235,0.8)', + stack: 'b', + xAxisID: 'x2' + }, + { + label: 'Dataset 3', + data: [80, 30, 40, 60, 70, 80, 47], + backgroundColor: 'rgba(75,192,192,0.8)', + stack: 'a', + xAxisID: 'x3' + }, + { + label: 'Dataset 4', + data: [80, 30, 40, 60, 70, 80, 47], + backgroundColor: 'rgba(54,162,235,0.8)', + stack: 'a', + xAxisID: 'x3' + }, + ] + }, + options: { + plugins: false, + barThickness: 'flex', + scales: { + x: { + stacked: true, + display: false, + }, + x2: { + labels: ['January 2024', 'February 2024', 'March 2024', 'April 2024', 'May 2024', 'June 2024', 'July 2024'], + stacked: true, + display: false, + }, + x3: { + labels: ['January 2025', 'February 2025', 'March 2025', 'April 2025', 'May 2025', 'June 2025', 'July 2025'], + stacked: true, + display: false, + }, + y: { + stacked: true, + display: false, + } + } + } + }, + options: { + } +}; diff --git a/test/fixtures/controller.bar/stacking/stacked-and-multiple-axis.png b/test/fixtures/controller.bar/stacking/stacked-and-multiple-axis.png new file mode 100644 index 0000000000000000000000000000000000000000..ea571109e90cad2295defb015a131c52b009c9a3 GIT binary patch literal 14649 zcmeHNX;_nImwpm90dd2s1r=IUv@W=yhyqE~A}zAC_7a6+P}`zJh-DEGLZTg|s0eCX zRD@tr(9wVtLkb9qbpu5r27$7K)kMUwB_w2@^9HcmUNiH}HS^84@yEQxC-1YIbMAAW z`#g!h-Wx5righ%r1m%(Z?G;{QSUn zaWm~zPjFyMe|H4?^30XT>NAR$5-pE5ez(c<5bg*5<`WGMzgB;{qvNyH2|McAJUq4t z@r&=>IniqK$m%pM-`@4?d)B7y>wGl%;lA_d&0JeQ;}_-~V)nj#aw;PtrrE|u!|qi{ij=nm=S}2t_1T7#TFHgRo|2)imcE;@RrmUB1ZjDySEs&} zrymcFIf^%%pUL%e*r(`M<(2gvHW#o$8AU7;Z;tROnN6)C4f1?StPt8iD7+AWGt&P1 zG(^u2n~nU*Ww;sS1?lcMqaU{+biddS!GElJx@W3yP`(igx_-?Fp&4X5Y{0YtY=HmR zfU$EJ^M`-%xTvqjJ7~E!fwJ;DG-VyzRr@kOLHDALBtM}m6GynKUC@cBg#d)RLND>> z-^en`{iP#1kK`q>hM_L3SRWFt%c9XO_%6?YyOj<2xwO`OO%i@BfWyE&95JjW0=~-> zmBpmuk1s|@5rVUIdg+Wq=t)G&Ezd*u0Da7X}; z!%a@TpJ51OOInON-CgbByMsMx(F<~J%9~7!;dX*?DWQbZ!9$aTVUCLlN+sVY@x}d z0CzkDM z6rC7~LFE9GZvfP5s5Bh=R-ZD)&S5xpj-A8UIRJqf!-rua_m`YQ;yAj(Ml)nyRdGlw z@1^ZfKH(_&5d+t28zRC=dfRhoclbTd(|NH6C}lkb8z%^&mt;JDmsHA)lh(6Y&(u+k z@}gFDHY5H~?_S+ZpRGB-9Q}bH=y~i;5~|EQ$Uh_-^wHgQc7Bp2e#TEs;~enLFhZga zcCkg38j5Q3SIg_Cv&;~&$_N!r9wx8cWK{gh8bwyb3AW>rGnRQ? zGmxJt=*e73Ol-w`be0s`?Zh_srWMiP+)vyhL6t!O3^pnBEmyMR9#wsKmveU` zBA%Rx`NdQ#O{D>^Xdguxlu>Cp@l{~>^VE}@CZ>l@0m+F{?R*x|BPR7b^iM`99R|cO zLv#dO#?Dn#s9F-QbaD}Az!bAbC7|aYTc$xx<@G43-x-z5(eMN~;z`IR)gG2;>C@2b zv}vgPF<1;wUk3&~1y5Fc@W{P5X^;oZVT8h0VD6Op1YB?qJh`7o^iyHGGzGw~jQ@hoxZ}BT z8YYk8vn}v@pIP%}H>=tsA24Y*1|y8)5E_4A0U2A#qStd=x`_5k>jOhWQiYon5@UL^ zyM`LQLp2w0HV7GPe`p0;ooW96y@|h#MUwDz#Y%$q@cs`~r{dZjmua+-YN3ti=5BQ? zDrYS*`Lhiz4Cr;Gns=B?!8xA`^RiVrjV;QDj;tz<3H=K=v@BzE#KRy7afzwV|8sS2&wiQ2aQtcCuwa%VbC{ncRAD_uJk*%)or{3Q?(|8JqCUa;;jfZqL({nmfb| zrN@Np5=�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 literal 0 HcmV?d00001