Skip to content

Commit 7f81b39

Browse files
Core | Component | Stacked Bar: Add stacked spacing option
1 parent a73eb71 commit 7f81b39

File tree

1 file changed

+39
-30
lines changed
  • packages/ts/src/components/stacked-bar

1 file changed

+39
-30
lines changed

packages/ts/src/components/stacked-bar/index.ts

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export class StackedBar<Datum> extends XYComponentCore<Datum, StackedBarConfigIn
3232
getAccessors = (): NumericAccessor<Datum>[] => (isArray(this.config.y) ? this.config.y : [this.config.y])
3333
stacked = true
3434
events = {}
35-
private _prevNegative: boolean[] | undefined // To help guessing the bar direction when an accessor was set to null or 0
35+
private _prevNegative: boolean[] | undefined
3636
private _barData: Datum[] = []
3737

3838
constructor (config?: StackedBarConfigInterface<Datum>) {
@@ -124,18 +124,38 @@ export class StackedBar<Datum> extends XYComponentCore<Datum, StackedBarConfigIn
124124
)
125125

126126
// Render Bars
127+
const stackExtents = this._barData.map((_, j) => {
128+
const values = stacked.map(s => s[j]) // Get all [y0, y1] pairs for stack j
129+
const positiveY1 = values.filter(v => v[1] >= v[0]).map(v => v[1])
130+
const negativeY1 = values.filter(v => v[1] < v[0]).map(v => v[1])
131+
return {
132+
maxPositive: positiveY1.length ? max(positiveY1) : undefined,
133+
minNegative: negativeY1.length ? min(negativeY1) : undefined,
134+
}
135+
})
136+
127137
const bars = barGroupsMerged
128138
.selectAll<SVGPathElement, StackedBarDataRecord<Datum>>(`.${s.bar}`)
129-
.data((d, j) => stacked.map((s, stackIndex) =>
130-
({
131-
...d,
132-
_index: j,
133-
_stacked: s[j],
134-
// Ending bar if the next stack is not the same as the current one
135-
_ending: (stackIndex === stacked.length - 1) ||
136-
((stackIndex <= stacked.length - 1) && stacked[stackIndex + 1][j][0] !== s[j][1]),
137-
}))
138-
)
139+
.data((d, j) => {
140+
const { maxPositive, minNegative } = stackExtents[j]
141+
return stacked.map((s) => {
142+
const y0 = s[j][0]
143+
const y1 = s[j][1]
144+
const isSegmentPositive = y1 >= y0
145+
146+
// A bar is "ending" if it's the outermost in its respective direction (positive or negative)
147+
const isEnding = isSegmentPositive
148+
? y1 === maxPositive
149+
: y1 === minNegative
150+
151+
return {
152+
...d,
153+
_index: j,
154+
_stacked: s[j],
155+
_ending: isEnding,
156+
}
157+
})
158+
})
139159

140160
const barsEnter = bars.enter().append('path')
141161
.attr('class', s.bar)
@@ -224,27 +244,16 @@ export class StackedBar<Datum> extends XYComponentCore<Datum, StackedBarConfigIn
224244
const stackSpacing = config.stackSpacing || 0
225245
let hWithSpacing = h
226246
let yWithSpacing = y
227-
// Always apply spacing between all stacked segments if config.stackSpacing is set
228-
// Only apply the zero-axis gap if there are actual negative values in the data
229-
const hasNegative = stackSpacing > 0 && this._barData && this._barData.length > 0 && this._barData.some(barDataItem =>
230-
this.getAccessors().some(accessor => getNumber(barDataItem, accessor) < 0)
231-
)
247+
// For negative bars, shift y position by stackSpacing so the base remains at the correct value
232248
if (!isEntering && stackSpacing > 0) {
233-
if (this.isVertical()) {
234-
hWithSpacing = Math.max(0, h - stackSpacing)
235-
// For negative bars, spacing should be applied in the same direction as positive, but the sign of the offset should be flipped
236-
yWithSpacing = y + (isNegative ? stackSpacing / 2 : stackSpacing / 2)
237-
// Apply gap at zero axis for both positive and negative bars
238-
if (hasNegative && (d._stacked[0] === 0 || d._stacked[1] === 0)) {
239-
// Always add half the spacing away from zero, regardless of sign
240-
yWithSpacing += (isNegative ? stackSpacing / 2 : -stackSpacing / 2)
241-
}
249+
hWithSpacing = h - stackSpacing
250+
if (hWithSpacing <= 0 && isFinite(value) && (value !== config.barMinHeightZeroValue)) {
251+
hWithSpacing = 1
242252
} else {
243-
hWithSpacing = Math.max(0, h - stackSpacing)
244-
yWithSpacing = y + (isNegative ? stackSpacing / 2 : stackSpacing / 2)
245-
if (hasNegative && (d._stacked[0] === 0 || d._stacked[1] === 0)) {
246-
yWithSpacing += (isNegative ? stackSpacing / 2 : -stackSpacing / 2)
247-
}
253+
hWithSpacing = Math.max(0, hWithSpacing)
254+
}
255+
if (this.isVertical() && isNegative) {
256+
yWithSpacing = y + stackSpacing
248257
}
249258
}
250259

0 commit comments

Comments
 (0)