@coinbase/cds-web-visualization 3.4.0-beta.23 → 3.4.0-beta.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dts/chart/Path.d.ts +4 -2
  3. package/dts/chart/Path.d.ts.map +1 -1
  4. package/dts/chart/PeriodSelector.d.ts +1 -1
  5. package/dts/chart/bar/Bar.d.ts +18 -54
  6. package/dts/chart/bar/Bar.d.ts.map +1 -1
  7. package/dts/chart/bar/BarPlot.d.ts.map +1 -1
  8. package/dts/chart/bar/BarStack.d.ts +4 -4
  9. package/dts/chart/bar/BarStack.d.ts.map +1 -1
  10. package/dts/chart/bar/DefaultBar.d.ts.map +1 -1
  11. package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
  12. package/dts/chart/point/Point.d.ts +2 -1
  13. package/dts/chart/point/Point.d.ts.map +1 -1
  14. package/dts/chart/scrubber/Scrubber.d.ts +4 -2
  15. package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
  16. package/dts/chart/utils/bar.d.ts +153 -0
  17. package/dts/chart/utils/bar.d.ts.map +1 -1
  18. package/dts/chart/utils/chart.d.ts +2 -1
  19. package/dts/chart/utils/chart.d.ts.map +1 -1
  20. package/dts/chart/utils/path.d.ts.map +1 -1
  21. package/dts/sparkline/Sparkline.d.ts +2 -1
  22. package/dts/sparkline/Sparkline.d.ts.map +1 -1
  23. package/dts/sparkline/SparklineArea.d.ts +2 -1
  24. package/dts/sparkline/SparklineArea.d.ts.map +1 -1
  25. package/dts/sparkline/SparklineGradient.d.ts +2 -1
  26. package/dts/sparkline/SparklineGradient.d.ts.map +1 -1
  27. package/dts/sparkline/sparkline-interactive/SparklineInteractive.d.ts +2 -1
  28. package/dts/sparkline/sparkline-interactive/SparklineInteractive.d.ts.map +1 -1
  29. package/esm/chart/Path.js +2 -1
  30. package/esm/chart/bar/Bar.js +4 -6
  31. package/esm/chart/bar/BarChart.js +7 -7
  32. package/esm/chart/bar/BarPlot.js +17 -27
  33. package/esm/chart/bar/BarStack.js +90 -295
  34. package/esm/chart/bar/DefaultBar.js +13 -19
  35. package/esm/chart/bar/DefaultBarStack.js +12 -19
  36. package/esm/chart/utils/bar.js +813 -0
  37. package/esm/chart/utils/chart.js +2 -1
  38. package/esm/chart/utils/path.js +5 -13
  39. package/esm/sparkline/Sparkline.js +2 -1
  40. package/esm/sparkline/SparklineArea.js +2 -1
  41. package/esm/sparkline/SparklineGradient.js +2 -1
  42. package/esm/sparkline/sparkline-interactive/SparklineInteractive.js +2 -1
  43. package/package.json +5 -5
@@ -33,6 +33,7 @@ export const Bar = /*#__PURE__*/memo(_ref => {
33
33
  borderRadius = 4,
34
34
  roundTop = true,
35
35
  roundBottom = true,
36
+ minSize,
36
37
  transitions,
37
38
  transition
38
39
  } = _ref;
@@ -42,12 +43,8 @@ export const Bar = /*#__PURE__*/memo(_ref => {
42
43
  const barPath = useMemo(() => {
43
44
  return getBarPath(x, y, width, height, borderRadius, !!roundTop, !!roundBottom, layout);
44
45
  }, [x, y, width, height, borderRadius, roundTop, roundBottom, layout]);
45
- const origin = useMemo(() => {
46
- return originProp !== null && originProp !== void 0 ? originProp : layout === 'horizontal' ? x : y + height;
47
- }, [originProp, layout, x, y, height]);
48
- if (!barPath) {
49
- return null;
50
- }
46
+ const origin = useMemo(() => originProp !== null && originProp !== void 0 ? originProp : layout === 'horizontal' ? x : y + height, [originProp, layout, x, y, height]);
47
+ if (!barPath) return;
51
48
  return /*#__PURE__*/_jsx(BarComponent, {
52
49
  borderRadius: borderRadius,
53
50
  d: barPath,
@@ -56,6 +53,7 @@ export const Bar = /*#__PURE__*/memo(_ref => {
56
53
  fill: fill,
57
54
  fillOpacity: fillOpacity,
58
55
  height: height,
56
+ minSize: minSize,
59
57
  origin: origin,
60
58
  roundBottom: roundBottom,
61
59
  roundTop: roundTop,
@@ -49,9 +49,9 @@ export const BarChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
49
49
  });
50
50
  }, [seriesProp, stacked]);
51
51
  const seriesIds = useMemo(() => series === null || series === void 0 ? void 0 : series.map(s => s.id), [series]);
52
- const isHorizontal = chartProps.layout === 'horizontal';
53
- const defaultXScaleType = isHorizontal ? 'linear' : 'band';
54
- const defaultYScaleType = isHorizontal ? 'band' : 'linear';
52
+ const isHorizontalLayout = chartProps.layout === 'horizontal';
53
+ const defaultXScaleType = isHorizontalLayout ? 'linear' : 'band';
54
+ const defaultYScaleType = isHorizontalLayout ? 'band' : 'linear';
55
55
 
56
56
  // Split axis props into config props for Chart and visual props for axis components
57
57
  const _ref2 = xAxis || {},
@@ -87,24 +87,24 @@ export const BarChart = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) =>
87
87
  scaleType: xScaleType !== null && xScaleType !== void 0 ? xScaleType : defaultXScaleType,
88
88
  data: xData,
89
89
  categoryPadding: xCategoryPadding,
90
- domain: isHorizontal && !hasNegativeValues ? _objectSpread({
90
+ domain: isHorizontalLayout && !hasNegativeValues ? _objectSpread({
91
91
  min: 0
92
92
  }, xDomain) : xDomain,
93
93
  domainLimit: xDomainLimit,
94
94
  range: xRange
95
- }), [xScaleType, isHorizontal, xData, xCategoryPadding, hasNegativeValues, xDomain, xDomainLimit, xRange, defaultXScaleType]);
95
+ }), [xScaleType, isHorizontalLayout, xData, xCategoryPadding, hasNegativeValues, xDomain, xDomainLimit, xRange, defaultXScaleType]);
96
96
 
97
97
  // Set default min domain to 0 for bar chart, but only if there are no negative values
98
98
  const yAxisConfig = useMemo(() => ({
99
99
  scaleType: yScaleType !== null && yScaleType !== void 0 ? yScaleType : defaultYScaleType,
100
100
  data: yData,
101
101
  categoryPadding: yCategoryPadding,
102
- domain: !isHorizontal && !hasNegativeValues ? _objectSpread({
102
+ domain: !isHorizontalLayout && !hasNegativeValues ? _objectSpread({
103
103
  min: 0
104
104
  }, yDomain) : yDomain,
105
105
  domainLimit: yDomainLimit,
106
106
  range: yRange
107
- }), [yScaleType, isHorizontal, yData, yCategoryPadding, hasNegativeValues, yDomain, yDomainLimit, yRange, defaultYScaleType]);
107
+ }), [yScaleType, isHorizontalLayout, yData, yCategoryPadding, hasNegativeValues, yDomain, yDomainLimit, yRange, defaultYScaleType]);
108
108
  return /*#__PURE__*/_jsxs(CartesianChart, _objectSpread(_objectSpread({}, chartProps), {}, {
109
109
  ref: ref,
110
110
  inset: inset,
@@ -1,6 +1,7 @@
1
1
  import { memo, useId, useMemo } from 'react';
2
+ import { m as motion } from 'framer-motion';
2
3
  import { useCartesianChartContext } from '../ChartProvider';
3
- import { defaultAxisId } from '../utils';
4
+ import { getStackGroups, instantTransition } from '../utils';
4
5
  import { BarStackGroup } from './BarStackGroup';
5
6
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
6
7
  /**
@@ -10,6 +11,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
10
11
  * cross-axis stacking (e.g., comparing $1M vs $1B companies on different scales).
11
12
  */
12
13
  export const BarPlot = /*#__PURE__*/memo(_ref => {
14
+ var _transitions$update;
13
15
  let {
14
16
  seriesIds,
15
17
  barPadding = 0.1,
@@ -27,6 +29,7 @@ export const BarPlot = /*#__PURE__*/memo(_ref => {
27
29
  transition
28
30
  } = _ref;
29
31
  const {
32
+ animate,
30
33
  series: allSeries,
31
34
  drawingArea
32
35
  } = useCartesianChartContext();
@@ -38,37 +41,24 @@ export const BarPlot = /*#__PURE__*/memo(_ref => {
38
41
  }
39
42
  return allSeries;
40
43
  }, [allSeries, seriesIds]);
41
- const stackGroups = useMemo(() => {
42
- const groups = new Map();
44
+ const stackGroups = useMemo(() => getStackGroups(targetSeries), [targetSeries]);
45
+ if (!drawingArea) return;
46
+
47
+ // Clip path animation for bar is just for chart size changes, not for
48
+ // enter transition. One caveat, bar update transitions are staggered
49
+ // but clip path is not, so some bars could be clipped in rare cases
43
50
 
44
- // Group series into stacks based on stackId + axis ID combination
45
- targetSeries.forEach(series => {
46
- var _series$xAxisId, _series$yAxisId;
47
- const xAxisId = (_series$xAxisId = series.xAxisId) !== null && _series$xAxisId !== void 0 ? _series$xAxisId : defaultAxisId;
48
- const yAxisId = (_series$yAxisId = series.yAxisId) !== null && _series$yAxisId !== void 0 ? _series$yAxisId : defaultAxisId;
49
- const stackId = series.stackId || "individual-".concat(series.id);
50
- const stackKey = "".concat(stackId, ":").concat(xAxisId, ":").concat(yAxisId);
51
- if (!groups.has(stackKey)) {
52
- groups.set(stackKey, {
53
- stackId: stackKey,
54
- series: [],
55
- xAxisId: series.xAxisId,
56
- yAxisId: series.yAxisId
57
- });
58
- }
59
- const group = groups.get(stackKey);
60
- group.series.push(series);
61
- });
62
- return Array.from(groups.values());
63
- }, [targetSeries]);
64
- if (!drawingArea) {
65
- return null;
66
- }
67
51
  return /*#__PURE__*/_jsxs(_Fragment, {
68
52
  children: [/*#__PURE__*/_jsx("defs", {
69
53
  children: /*#__PURE__*/_jsx("clipPath", {
70
54
  id: clipPathId,
71
- children: /*#__PURE__*/_jsx("rect", {
55
+ children: animate ? /*#__PURE__*/_jsx(motion.rect, {
56
+ height: drawingArea.height,
57
+ transition: (_transitions$update = transitions === null || transitions === void 0 ? void 0 : transitions.update) !== null && _transitions$update !== void 0 ? _transitions$update : instantTransition,
58
+ width: drawingArea.width,
59
+ x: drawingArea.x,
60
+ y: drawingArea.y
61
+ }) : /*#__PURE__*/_jsx("rect", {
72
62
  height: drawingArea.height,
73
63
  width: drawingArea.width,
74
64
  x: drawingArea.x,
@@ -1,20 +1,14 @@
1
- function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
2
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
3
- function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
4
- function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
5
- function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
6
1
  import React, { memo, useMemo } from 'react';
7
2
  import { useCartesianChartContext } from '../ChartProvider';
8
- import { evaluateGradientAtValue, getGradientConfig } from '../utils/gradient';
3
+ import { EPSILON, getBars, getStackBaseline, getStackOrigin } from '../utils/bar';
4
+ import { getGradientConfig } from '../utils/gradient';
9
5
  import { Bar } from './Bar';
10
6
  import { DefaultBarStack } from './DefaultBarStack';
11
- import { jsx as _jsx } from "react/jsx-runtime";
12
- const EPSILON = 1e-4;
13
7
 
14
8
  /**
15
9
  * Extended series type that includes bar-specific properties.
16
10
  */
17
-
11
+ import { jsx as _jsx } from "react/jsx-runtime";
18
12
  /**
19
13
  * BarStack component that renders a single stack of bars at a specific category index.
20
14
  * Handles the stacking logic for bars within a single category.
@@ -29,6 +23,7 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
29
23
  valueScale,
30
24
  rect,
31
25
  xAxisId,
26
+ yAxisId,
32
27
  BarComponent: defaultBarComponent,
33
28
  fillOpacity: defaultFillOpacity,
34
29
  stroke: defaultStroke,
@@ -46,36 +41,21 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
46
41
  layout,
47
42
  getSeriesData,
48
43
  getXAxis,
49
- getSeries
44
+ getYAxis
50
45
  } = useCartesianChartContext();
51
- const barMinSizePx = barMinSize;
52
- const stackMinSizePx = stackMinSize;
53
46
  const xAxis = getXAxis(xAxisId);
54
- const barsGrowVertically = layout !== 'horizontal';
47
+ const yAxis = getYAxis(yAxisId);
55
48
  const baseline = useMemo(() => {
56
- var _valueScale, _valueScale2;
57
- const domain = valueScale.domain();
58
- const [domainMin, domainMax] = domain;
59
- const baselineValue = domainMin >= 0 ? domainMin : domainMax <= 0 ? domainMax : 0;
60
- const pos = (_valueScale = valueScale(baselineValue)) !== null && _valueScale !== void 0 ? _valueScale : 0;
61
-
62
- // In vertical layout (bars grow up), value scale is Y. In horizontal, it's X.
63
- const fallback = barsGrowVertically ? rect.y + rect.height : rect.x;
64
- const baselinePos = (_valueScale2 = valueScale(baselineValue)) !== null && _valueScale2 !== void 0 ? _valueScale2 : fallback;
65
- if (barsGrowVertically) {
66
- return Math.max(rect.y, Math.min(baselinePos, rect.y + rect.height));
67
- } else {
68
- return Math.max(rect.x, Math.min(baselinePos, rect.x + rect.width));
69
- }
70
- }, [rect, valueScale, barsGrowVertically]);
49
+ return getStackBaseline(valueScale, rect, layout);
50
+ }, [rect, valueScale, layout]);
71
51
  const seriesGradients = useMemo(() => {
72
52
  return series.map(s => {
73
53
  if (!s.gradient) return null;
74
- const evalScale = s.gradient.axis === 'x' ? barsGrowVertically ? indexScale : valueScale : barsGrowVertically ? valueScale : indexScale;
54
+ const evalScale = s.gradient.axis === 'x' ? layout === 'vertical' ? indexScale : valueScale : layout === 'vertical' ? valueScale : indexScale;
75
55
 
76
56
  // We need to pass original xScale/yScale to getGradientConfig for legacy reasons
77
57
  // For now let's assume getGradientConfig can handle these scales if we pass them correctly.
78
- const stops = getGradientConfig(s.gradient, barsGrowVertically ? indexScale : valueScale, barsGrowVertically ? valueScale : indexScale);
58
+ const stops = getGradientConfig(s.gradient, layout === 'vertical' ? indexScale : valueScale, layout === 'vertical' ? valueScale : indexScale);
79
59
  if (!stops) return null;
80
60
  return {
81
61
  seriesId: s.id,
@@ -84,279 +64,95 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
84
64
  stops
85
65
  };
86
66
  });
87
- }, [series, indexScale, valueScale, barsGrowVertically]);
88
-
89
- // Calculate bars for this specific category
90
- const {
91
- bars,
92
- stackRect
93
- } = useMemo(() => {
94
- let allBars = [];
95
-
96
- // Track how many bars we've stacked in each direction for gap calculation
97
- let positiveBarCount = 0;
98
- let negativeBarCount = 0;
99
-
100
- // Track stack bounds for clipping
101
- let minValuePos = Infinity;
102
- let maxValuePos = -Infinity;
103
-
104
- // Process each series in the stack
105
- series.forEach(s => {
106
- var _valueScale3, _valueScale4, _s$color;
107
- const data = getSeriesData(s.id);
108
- if (!data) return;
109
- const value = data[categoryIndex];
110
- if (value === null || value === undefined) return;
111
- const originalData = s.data;
112
- const originalValue = originalData === null || originalData === void 0 ? void 0 : originalData[categoryIndex];
113
- // Only apply gap logic if the original data wasn't tuple format
114
- const shouldApplyGap = !Array.isArray(originalValue);
115
-
116
- // Sort to be in ascending order
117
- const [bottom, top] = value.sort((a, b) => a - b);
118
- const isAboveBaseline = bottom >= 0 && top !== bottom;
119
- const isBelowBaseline = bottom <= 0 && bottom !== top;
120
- const edgeBottom = (_valueScale3 = valueScale(bottom)) !== null && _valueScale3 !== void 0 ? _valueScale3 : baseline;
121
- const edgeTop = (_valueScale4 = valueScale(top)) !== null && _valueScale4 !== void 0 ? _valueScale4 : baseline;
122
-
123
- // In vertical layout (bars grow up):
124
- // - edgeTop is min Y (top face)
125
- // - edgeBottom is max Y (bottom face)
126
- // In horizontal layout (bars grow sideways):
127
- // - edgeTop is max X (right face)
128
- // - edgeBottom is min X (left face)
129
- // However, edgeTop/edgeBottom here are values from the scale.
130
- // For positive bars: edgeTop = scale(value), edgeBottom = scale(0).
131
- // For horizontal: edgeTop > edgeBottom (X increases right).
132
- // For vertical: edgeTop < edgeBottom (Y increases down).
133
-
134
- const roundingEndA = roundBaseline || Math.abs(edgeTop - baseline) >= EPSILON;
135
- const roundingEndB = roundBaseline || Math.abs(edgeBottom - baseline) >= EPSILON;
136
-
137
- // In horizontal layout: roundTop is Right (edgeTop), roundBottom is Left (edgeBottom)
138
- // getBarPath already handles the mapping of roundTop/roundBottom to coordinates.
139
- const roundTop = roundingEndA;
140
- const roundBottom = roundingEndB;
141
-
142
- // Track bar counts for later gap calculations
143
- if (shouldApplyGap) {
144
- if (isAboveBaseline) {
145
- positiveBarCount++;
146
- } else if (isBelowBaseline) {
147
- negativeBarCount++;
148
- }
149
- }
150
-
151
- // Calculate length (measured along the value axis)
152
- const length = Math.abs(edgeBottom - edgeTop);
153
- const valuePos = Math.min(edgeBottom, edgeTop);
154
-
155
- // Skip bars that would have zero or negative height
156
- if (length <= 0) {
157
- return;
158
- }
159
-
160
- // Update stack bounds
161
- minValuePos = Math.min(minValuePos, valuePos);
162
- maxValuePos = Math.max(maxValuePos, valuePos + length);
163
- let barFill = (_s$color = s.color) !== null && _s$color !== void 0 ? _s$color : 'var(--color-fgPrimary)';
164
-
165
- // Evaluate gradient if provided (using precomputed stops)
166
- const seriesGradientConfig = seriesGradients.find(g => (g === null || g === void 0 ? void 0 : g.seriesId) === s.id);
167
- if (seriesGradientConfig && originalValue !== null && originalValue !== undefined) {
168
- var _seriesGradientConfig;
169
- const axis = (_seriesGradientConfig = seriesGradientConfig.gradient.axis) !== null && _seriesGradientConfig !== void 0 ? _seriesGradientConfig : 'y';
170
- let evalValue;
171
- if (axis === 'x') {
172
- // X-axis gradient: In vertical it's the index, in horizontal it's the value.
173
- evalValue = barsGrowVertically ? categoryIndex : Array.isArray(originalValue) ? originalValue[1] : originalValue;
174
- } else {
175
- // Y-axis gradient: In vertical it's the value, in horizontal it's the index.
176
- evalValue = barsGrowVertically ? Array.isArray(originalValue) ? originalValue[1] : originalValue : categoryIndex;
177
- }
178
- const evaluatedColor = evaluateGradientAtValue(seriesGradientConfig.stops, evalValue, seriesGradientConfig.scale);
179
- if (evaluatedColor) {
180
- barFill = evaluatedColor;
181
- }
182
- }
183
- allBars.push({
184
- seriesId: s.id,
185
- indexPos,
186
- valuePos,
187
- thickness,
188
- length,
189
- dataValue: value,
190
- fill: barFill,
191
- roundTop,
192
- roundBottom,
193
- shouldApplyGap,
194
- BarComponent: s.BarComponent
195
- });
196
- });
197
-
198
- // Apply proportional gap distribution to maintain total stack length
199
- if (stackGap && allBars.length > 1) {
200
- // Separate bars by baseline side
201
- const barsAboveBaseline = allBars.filter(bar => {
202
- const [bottom, top] = bar.dataValue.sort((a, b) => a - b);
203
- return bottom >= 0 && top !== bottom && bar.shouldApplyGap;
204
- });
205
- const barsBelowBaseline = allBars.filter(bar => {
206
- const [bottom, top] = bar.dataValue.sort((a, b) => a - b);
207
- return bottom <= 0 && bottom !== top && bar.shouldApplyGap;
208
- });
209
-
210
- // Apply proportional gaps to bars above baseline
211
- if (barsAboveBaseline.length > 1) {
212
- const totalGapSpace = stackGap * (barsAboveBaseline.length - 1);
213
- const totalDataLength = barsAboveBaseline.reduce((sum, bar) => sum + bar.length, 0);
214
- const lengthReduction = totalGapSpace / totalDataLength;
215
-
216
- // In SVG, for Y axis positive values go up (decreasing Y)
217
- // For X axis positive values go right (increasing X)
218
- const sortedBars = barsGrowVertically ? barsAboveBaseline.sort((a, b) => b.valuePos - a.valuePos) // Higher Y first
219
- : barsAboveBaseline.sort((a, b) => a.valuePos - b.valuePos); // Higher X last
220
-
221
- let currentEdge = baseline;
222
- sortedBars.forEach((bar, index) => {
223
- const newLength = bar.length * (1 - lengthReduction);
224
- let newValuePos;
225
- if (barsGrowVertically) {
226
- newValuePos = currentEdge - newLength;
227
- currentEdge = newValuePos - (index < sortedBars.length - 1 ? stackGap : 0);
228
- } else {
229
- newValuePos = currentEdge;
230
- currentEdge = newValuePos + newLength + (index < sortedBars.length - 1 ? stackGap : 0);
231
- }
232
- const barIndex = allBars.findIndex(b => b.seriesId === bar.seriesId);
233
- if (barIndex !== -1) {
234
- allBars[barIndex] = _objectSpread(_objectSpread({}, allBars[barIndex]), {}, {
235
- length: newLength,
236
- valuePos: newValuePos
237
- });
238
- }
239
- });
240
- }
241
-
242
- // Apply proportional gaps to bars below baseline
243
- if (barsBelowBaseline.length > 1) {
244
- const totalGapSpace = stackGap * (barsBelowBaseline.length - 1);
245
- const totalDataLength = barsBelowBaseline.reduce((sum, bar) => sum + bar.length, 0);
246
- const lengthReduction = totalGapSpace / totalDataLength;
247
- const sortedBars = barsGrowVertically ? barsBelowBaseline.sort((a, b) => a.valuePos - b.valuePos) : barsBelowBaseline.sort((a, b) => b.valuePos - a.valuePos);
248
- let currentEdge = baseline;
249
- sortedBars.forEach((bar, index) => {
250
- const newLength = bar.length * (1 - lengthReduction);
251
- let newValuePos;
252
- if (barsGrowVertically) {
253
- newValuePos = currentEdge;
254
- currentEdge = newValuePos + newLength + (index < sortedBars.length - 1 ? stackGap : 0);
255
- } else {
256
- newValuePos = currentEdge - newLength;
257
- currentEdge = newValuePos - (index < sortedBars.length - 1 ? stackGap : 0);
258
- }
259
- const barIndex = allBars.findIndex(b => b.seriesId === bar.seriesId);
260
- if (barIndex !== -1) {
261
- allBars[barIndex] = _objectSpread(_objectSpread({}, allBars[barIndex]), {}, {
262
- length: newLength,
263
- valuePos: newValuePos
264
- });
265
- }
266
- });
267
- }
268
-
269
- // Recalculate stack bounds after gap adjustments
270
- if (allBars.length > 0) {
271
- minValuePos = Math.min(...allBars.map(bar => bar.valuePos));
272
- maxValuePos = Math.max(...allBars.map(bar => bar.valuePos + bar.length));
273
- }
274
- }
275
-
276
- // Apply barMinSize constraints
277
- if (barMinSizePx) {
278
- // ... (Skipping full complex logic for brevity, but it should be layout-aware)
279
- // For now let's assume it's correctly handled similar to above by using length and valuePos
67
+ }, [series, indexScale, valueScale, layout]);
68
+ const categoryAxis = layout === 'vertical' ? xAxis : yAxis;
69
+ const categoryData = categoryAxis !== null && categoryAxis !== void 0 && categoryAxis.data && Array.isArray(categoryAxis.data) && typeof categoryAxis.data[0] === 'number' ? categoryAxis.data : undefined;
70
+ const categoryValue = categoryData ? categoryData[categoryIndex] : categoryIndex;
71
+ const seriesData = useMemo(() => Object.fromEntries(series.map(s => {
72
+ var _getSeriesData;
73
+ return [s.id, (_getSeriesData = getSeriesData(s.id)) !== null && _getSeriesData !== void 0 ? _getSeriesData : []];
74
+ })), [series, getSeriesData]);
75
+ const bars = useMemo(() => getBars({
76
+ series,
77
+ seriesData,
78
+ categoryIndex,
79
+ categoryValue,
80
+ indexPos,
81
+ thickness,
82
+ valueScale,
83
+ seriesGradients,
84
+ roundBaseline,
85
+ layout,
86
+ baseline,
87
+ stackGap,
88
+ barMinSize,
89
+ stackMinSize,
90
+ defaultFill: 'var(--color-fgPrimary)',
91
+ borderRadius,
92
+ defaultFillOpacity,
93
+ defaultStroke,
94
+ defaultStrokeWidth,
95
+ defaultBarComponent
96
+ }), [series, seriesData, stackGap, barMinSize, stackMinSize, indexPos, baseline, thickness, categoryIndex, categoryValue, valueScale, seriesGradients, roundBaseline, layout, borderRadius, defaultFillOpacity, defaultStroke, defaultStrokeWidth, defaultBarComponent]);
97
+ const stackRect = useMemo(() => {
98
+ if (bars.length === 0) {
99
+ return {
100
+ x: layout === 'vertical' ? indexPos : baseline,
101
+ y: layout === 'vertical' ? baseline : indexPos,
102
+ width: layout === 'vertical' ? thickness : 0,
103
+ height: layout === 'vertical' ? 0 : thickness
104
+ };
280
105
  }
281
-
282
- // Apply border radius logic
283
- const applyBorderRadiusLogic = bars => {
284
- // Sort bars from "bottom" to "top" of the stack relative to coordinate system
285
- // Vertical (Y axis): Max Y (bottom) to Min Y (top)
286
- // Horizontal (X axis): Min X (left) to Max X (right)
287
- const sortedBars = barsGrowVertically ? [...bars].sort((a, b) => b.valuePos - a.valuePos) : [...bars].sort((a, b) => a.valuePos - b.valuePos);
288
- return sortedBars.map((a, index) => {
289
- const barBefore = index > 0 ? sortedBars[index - 1] : null;
290
- const barAfter = index < sortedBars.length - 1 ? sortedBars[index + 1] : null;
291
-
292
- // shouldRoundLower: the face with the smaller coordinate (Top in vertical, Left in horizontal)
293
- const shouldRoundLower = (barsGrowVertically ? index === sortedBars.length - 1 : index === 0) || a.shouldApplyGap && stackGap || !a.shouldApplyGap && barAfter && barAfter.valuePos + barAfter.length !== a.valuePos;
294
-
295
- // shouldRoundHigher: the face with the larger coordinate (Bottom in vertical, Right in horizontal)
296
- const shouldRoundHigher = (barsGrowVertically ? index === 0 : index === sortedBars.length - 1) || a.shouldApplyGap && stackGap || !a.shouldApplyGap && barBefore && barBefore.valuePos !== a.valuePos + a.length;
297
- return _objectSpread(_objectSpread({}, a), {}, {
298
- roundTop: Boolean(a.roundTop && (barsGrowVertically ? shouldRoundLower : shouldRoundHigher)),
299
- roundBottom: Boolean(a.roundBottom && (barsGrowVertically ? shouldRoundHigher : shouldRoundLower))
300
- });
301
- });
302
- };
303
- allBars = applyBorderRadiusLogic(allBars);
304
-
305
- // Calculate the bounding rect for the entire stack
306
- const stackBounds = {
307
- x: barsGrowVertically ? indexPos : minValuePos === Infinity ? baseline : minValuePos,
308
- y: barsGrowVertically ? minValuePos === Infinity ? baseline : minValuePos : indexPos,
309
- width: barsGrowVertically ? thickness : maxValuePos === -Infinity ? 0 : maxValuePos - minValuePos,
310
- height: barsGrowVertically ? maxValuePos === -Infinity ? 0 : maxValuePos - minValuePos : thickness
311
- };
106
+ const minX = Math.min(...bars.map(b => b.x));
107
+ const minY = Math.min(...bars.map(b => b.y));
108
+ const maxX = Math.max(...bars.map(b => b.x + b.width));
109
+ const maxY = Math.max(...bars.map(b => b.y + b.height));
312
110
  return {
313
- bars: allBars,
314
- stackRect: stackBounds
111
+ x: minX,
112
+ y: minY,
113
+ width: maxX - minX,
114
+ height: maxY - minY
315
115
  };
316
- }, [series, stackGap, barMinSizePx, indexPos, baseline, thickness, getSeriesData, categoryIndex, valueScale, seriesGradients, roundBaseline, barsGrowVertically]);
317
- const xData = xAxis !== null && xAxis !== void 0 && xAxis.data && Array.isArray(xAxis.data) && typeof xAxis.data[0] === 'number' ? xAxis.data : undefined;
318
- const dataX = xData ? xData[categoryIndex] : categoryIndex;
319
- const barElements = bars.map((bar, index) => {
320
- var _bar$fillOpacity, _bar$stroke, _bar$strokeWidth;
321
- return /*#__PURE__*/_jsx(Bar, {
322
- BarComponent: bar.BarComponent || defaultBarComponent,
323
- borderRadius: borderRadius,
324
- dataX: barsGrowVertically ? dataX : bar.dataValue // This is a bit loose, depends on Bar implementation
325
- ,
326
- dataY: barsGrowVertically ? bar.dataValue : dataX,
327
- fill: bar.fill,
328
- fillOpacity: (_bar$fillOpacity = bar.fillOpacity) !== null && _bar$fillOpacity !== void 0 ? _bar$fillOpacity : defaultFillOpacity,
329
- height: barsGrowVertically ? bar.length : thickness,
330
- origin: baseline,
331
- roundBottom: bar.roundBottom,
332
- roundTop: bar.roundTop,
333
- seriesId: bar.seriesId,
334
- stroke: (_bar$stroke = bar.stroke) !== null && _bar$stroke !== void 0 ? _bar$stroke : defaultStroke,
335
- strokeWidth: (_bar$strokeWidth = bar.strokeWidth) !== null && _bar$strokeWidth !== void 0 ? _bar$strokeWidth : defaultStrokeWidth,
336
- transition: transition,
337
- transitions: transitions,
338
- width: barsGrowVertically ? thickness : bar.length,
339
- x: barsGrowVertically ? indexPos : bar.valuePos,
340
- y: barsGrowVertically ? bar.valuePos : indexPos
341
- }, "".concat(bar.seriesId, "-").concat(categoryIndex, "-").concat(index));
342
- });
343
-
344
- // Check if the stack as a whole should be rounded based on the baseline
345
- // edge: top in vertical, left in horizontal
346
- // size: height in vertical, width in horizontal
347
- const edge = barsGrowVertically ? stackRect.y : stackRect.x;
348
- const size = barsGrowVertically ? stackRect.height : stackRect.width;
349
-
350
- // stackRoundLower: face at smaller coordinate (Top in vertical, Left in horizontal)
351
- // stackRoundHigher: face at larger coordinate (Bottom in vertical, Right in horizontal)
116
+ }, [bars, baseline, indexPos, layout, thickness]);
117
+ const stackOrigin = useMemo(() => {
118
+ var _getStackOrigin;
119
+ return (_getStackOrigin = getStackOrigin(bars.map(b => b.origin), bars.map(b => {
120
+ var _b$minSize;
121
+ return (_b$minSize = b.minSize) !== null && _b$minSize !== void 0 ? _b$minSize : 0;
122
+ }))) !== null && _getStackOrigin !== void 0 ? _getStackOrigin : baseline;
123
+ }, [bars, baseline]);
124
+ const barElements = bars.map((bar, index) => /*#__PURE__*/_jsx(Bar, {
125
+ BarComponent: bar.BarComponent,
126
+ borderRadius: bar.borderRadius,
127
+ dataX: bar.dataX,
128
+ dataY: bar.dataY,
129
+ fill: bar.fill,
130
+ fillOpacity: bar.fillOpacity,
131
+ height: bar.height,
132
+ minSize: bar.minSize,
133
+ origin: bar.origin,
134
+ roundBottom: bar.roundBottom,
135
+ roundTop: bar.roundTop,
136
+ seriesId: bar.seriesId,
137
+ stroke: bar.stroke,
138
+ strokeWidth: bar.strokeWidth,
139
+ transition: transition,
140
+ transitions: transitions,
141
+ width: bar.width,
142
+ x: bar.x,
143
+ y: bar.y
144
+ }, "".concat(bar.seriesId, "-").concat(categoryIndex, "-").concat(index)));
145
+ const edge = layout === 'vertical' ? stackRect.y : stackRect.x;
146
+ const size = layout === 'vertical' ? stackRect.height : stackRect.width;
352
147
  const stackRoundLower = roundBaseline || Math.abs(edge - baseline) >= EPSILON;
353
148
  const stackRoundHigher = roundBaseline || Math.abs(edge + size - baseline) >= EPSILON;
354
- const stackRoundTop = barsGrowVertically ? stackRoundLower : stackRoundHigher;
355
- const stackRoundBottom = barsGrowVertically ? stackRoundHigher : stackRoundLower;
149
+ const stackRoundTop = layout === 'vertical' ? stackRoundLower : stackRoundHigher;
150
+ const stackRoundBottom = layout === 'vertical' ? stackRoundHigher : stackRoundLower;
356
151
  return /*#__PURE__*/_jsx(BarStackComponent, {
357
152
  borderRadius: borderRadius,
358
153
  categoryIndex: categoryIndex,
359
154
  height: stackRect.height,
155
+ origin: stackOrigin,
360
156
  roundBottom: stackRoundBottom,
361
157
  roundTop: stackRoundTop,
362
158
  transition: transition,
@@ -364,7 +160,6 @@ export const BarStack = /*#__PURE__*/memo(_ref => {
364
160
  width: stackRect.width,
365
161
  x: stackRect.x,
366
162
  y: stackRect.y,
367
- yOrigin: baseline,
368
163
  children: barElements
369
164
  });
370
165
  });