Skip to content

Commit 65febba

Browse files
committed
feat: accumulate-relative stacked bar chart stack mode
new stack mode was implemented closes #1167
1 parent 1438bad commit 65febba

File tree

3 files changed

+50
-15
lines changed

3 files changed

+50
-15
lines changed

src/charts/BarChart/BarChart.stories.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,3 +424,24 @@ export function PeakCircles() {
424424

425425
return root;
426426
}
427+
428+
export function AccumulateRelativeStack() {
429+
const root = document.createElement('div');
430+
431+
new BarChart(
432+
root,
433+
{
434+
labels: ['Monday', 'Tuesday', 'Wednesday', 'Thursday'],
435+
series: [
436+
[5, 4, -3, -5],
437+
[5, -4, 3, -5]
438+
]
439+
},
440+
{
441+
stackBars: true,
442+
stackMode: 'accumulate-relative'
443+
}
444+
);
445+
446+
return root;
447+
}

src/charts/BarChart/BarChart.ts

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,9 @@ const defaultOptions = {
116116
seriesBarDistance: 15,
117117
// If set to true this property will cause the series bars to be stacked. Check the `stackMode` option for further stacking options.
118118
stackBars: false,
119-
// If set to 'overlap' this property will force the stacked bars to draw from the zero line.
119+
// If set to true this property will force the stacked bars to draw from the zero line.
120120
// If set to 'accumulate' this property will form a total for each series point. This will also influence the y-axis and the overall bounds of the chart. In stacked mode the seriesBarDistance property will have no effect.
121+
// If set to 'accumulate-relative' positive and negative values will be handled separately.
121122
stackMode: 'accumulate' as const,
122123
// Inverts the axes of the bar chart in order to draw a horizontal bar chart. Be aware that you also need to invert your axis settings as the Y Axis will now display the labels and the X Axis the values.
123124
horizontalBars: false,
@@ -344,8 +345,13 @@ export class BarChart extends BaseChart<BarChartEventsTypes> {
344345
const zeroPoint = options.horizontalBars
345346
? chartRect.x1 + valueAxis.projectValue(0)
346347
: chartRect.y1 - valueAxis.projectValue(0);
348+
const isAccumulateStackMode = options.stackMode === 'accumulate';
349+
const isAccumulateRelativeStackMode =
350+
options.stackMode === 'accumulate-relative';
347351
// Used to track the screen coordinates of stacked bars
348-
const stackedBarValues: number[] = [];
352+
const posStackedBarValues: number[] = [];
353+
const negStackedBarValues: number[] = [];
354+
let stackedBarValues = posStackedBarValues;
349355

350356
labelAxis.createGridAndLabels(
351357
gridGroup,
@@ -428,6 +434,8 @@ export class BarChart extends BaseChart<BarChartEventsTypes> {
428434
);
429435

430436
normalizedData.series[seriesIndex].forEach((value, valueIndex) => {
437+
const valueX = safeHasProperty(value, 'x') && value.x;
438+
const valueY = safeHasProperty(value, 'y') && value.y;
431439
let labelAxisValueIndex;
432440
// We need to set labelAxisValueIndex based on some options combinations
433441
if (options.distributeSeries && !options.stackBars) {
@@ -450,14 +458,14 @@ export class BarChart extends BaseChart<BarChartEventsTypes> {
450458
x:
451459
chartRect.x1 +
452460
valueAxis.projectValue(
453-
safeHasProperty(value, 'x') ? value.x : 0,
461+
valueX || 0,
454462
valueIndex,
455463
normalizedData.series[seriesIndex]
456464
),
457465
y:
458466
chartRect.y1 -
459467
labelAxis.projectValue(
460-
safeHasProperty(value, 'y') ? value.y : 0,
468+
valueY || 0,
461469
labelAxisValueIndex,
462470
normalizedData.series[seriesIndex]
463471
)
@@ -467,14 +475,14 @@ export class BarChart extends BaseChart<BarChartEventsTypes> {
467475
x:
468476
chartRect.x1 +
469477
labelAxis.projectValue(
470-
safeHasProperty(value, 'x') ? value.x : 0,
478+
valueX || 0,
471479
labelAxisValueIndex,
472480
normalizedData.series[seriesIndex]
473481
),
474482
y:
475483
chartRect.y1 -
476484
valueAxis.projectValue(
477-
safeHasProperty(value, 'y') ? value.y : 0,
485+
valueY || 0,
478486
valueIndex,
479487
normalizedData.series[seriesIndex]
480488
)
@@ -500,6 +508,14 @@ export class BarChart extends BaseChart<BarChartEventsTypes> {
500508
(options.horizontalBars ? -1 : 1);
501509
}
502510

511+
// distinguish between positive and negative values in relative stack mode
512+
if (isAccumulateRelativeStackMode) {
513+
stackedBarValues =
514+
valueY >= 0 || valueX >= 0
515+
? posStackedBarValues
516+
: negStackedBarValues;
517+
}
518+
503519
// Enter value in stacked bar values used to remember previous screen value for stacking up bars
504520
const previousStack = stackedBarValues[valueIndex] || zeroPoint;
505521
stackedBarValues[valueIndex] =
@@ -517,7 +533,9 @@ export class BarChart extends BaseChart<BarChartEventsTypes> {
517533

518534
if (
519535
options.stackBars &&
520-
(options.stackMode === 'accumulate' || !options.stackMode)
536+
(isAccumulateStackMode ||
537+
isAccumulateRelativeStackMode ||
538+
!options.stackMode)
521539
) {
522540
// Stack mode: accumulate (default)
523541
// If bars are stacked we use the stackedBarValues reference and otherwise base all bars off the zero line
@@ -558,12 +576,7 @@ export class BarChart extends BaseChart<BarChartEventsTypes> {
558576
const bar = seriesElement
559577
.elem('line', positions, options.classNames.bar)
560578
.attr({
561-
'ct:value': [
562-
safeHasProperty(value, 'x') && value.x,
563-
safeHasProperty(value, 'y') && value.y
564-
]
565-
.filter(isNumeric)
566-
.join(','),
579+
'ct:value': [valueX, valueY].filter(isNumeric).join(','),
567580
'ct:meta': serialize(metaData)
568581
});
569582

src/charts/BarChart/BarChart.types.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,11 @@ export interface BarChartOptions<
4343
*/
4444
stackBars?: boolean;
4545
/**
46-
* If set to 'overlap' this property will force the stacked bars to draw from the zero line.
46+
* If set to true this property will force the stacked bars to draw from the zero line.
4747
* If set to 'accumulate' this property will form a total for each series point. This will also influence the y-axis and the overall bounds of the chart. In stacked mode the seriesBarDistance property will have no effect.
48+
* If set to 'accumulate-relative' positive and negative values will be handled separately.
4849
*/
49-
stackMode?: 'accumulate' | boolean;
50+
stackMode?: 'accumulate' | 'accumulate-relative' | boolean;
5051
/**
5152
* Inverts the axes of the bar chart in order to draw a horizontal bar chart. Be aware that you also need to invert your axis settings as the Y Axis will now display the labels and the X Axis the values.
5253
*/

0 commit comments

Comments
 (0)