@coinbase/cds-web-visualization 3.3.2 → 3.4.0-beta.10

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 (212) hide show
  1. package/CHANGELOG.md +50 -2
  2. package/dts/chart/CartesianChart.d.ts +72 -0
  3. package/dts/chart/CartesianChart.d.ts.map +1 -0
  4. package/dts/chart/ChartProvider.d.ts +6 -0
  5. package/dts/chart/ChartProvider.d.ts.map +1 -0
  6. package/dts/chart/Path.d.ts +54 -0
  7. package/dts/chart/Path.d.ts.map +1 -0
  8. package/dts/chart/PeriodSelector.d.ts +57 -0
  9. package/dts/chart/PeriodSelector.d.ts.map +1 -0
  10. package/dts/chart/area/Area.d.ts +78 -0
  11. package/dts/chart/area/Area.d.ts.map +1 -0
  12. package/dts/chart/area/AreaChart.d.ts +79 -0
  13. package/dts/chart/area/AreaChart.d.ts.map +1 -0
  14. package/dts/chart/area/DottedArea.d.ts +45 -0
  15. package/dts/chart/area/DottedArea.d.ts.map +1 -0
  16. package/dts/chart/area/GradientArea.d.ts +39 -0
  17. package/dts/chart/area/GradientArea.d.ts.map +1 -0
  18. package/dts/chart/area/SolidArea.d.ts +23 -0
  19. package/dts/chart/area/SolidArea.d.ts.map +1 -0
  20. package/dts/chart/area/index.d.ts +6 -0
  21. package/dts/chart/area/index.d.ts.map +1 -0
  22. package/dts/chart/axis/Axis.d.ts +255 -0
  23. package/dts/chart/axis/Axis.d.ts.map +1 -0
  24. package/dts/chart/axis/DefaultAxisTickLabel.d.ts +8 -0
  25. package/dts/chart/axis/DefaultAxisTickLabel.d.ts.map +1 -0
  26. package/dts/chart/axis/XAxis.d.ts +16 -0
  27. package/dts/chart/axis/XAxis.d.ts.map +1 -0
  28. package/dts/chart/axis/YAxis.d.ts +21 -0
  29. package/dts/chart/axis/YAxis.d.ts.map +1 -0
  30. package/dts/chart/axis/index.d.ts +5 -0
  31. package/dts/chart/axis/index.d.ts.map +1 -0
  32. package/dts/chart/bar/Bar.d.ts +94 -0
  33. package/dts/chart/bar/Bar.d.ts.map +1 -0
  34. package/dts/chart/bar/BarChart.d.ts +62 -0
  35. package/dts/chart/bar/BarChart.d.ts.map +1 -0
  36. package/dts/chart/bar/BarPlot.d.ts +30 -0
  37. package/dts/chart/bar/BarPlot.d.ts.map +1 -0
  38. package/dts/chart/bar/BarStack.d.ts +103 -0
  39. package/dts/chart/bar/BarStack.d.ts.map +1 -0
  40. package/dts/chart/bar/BarStackGroup.d.ts +36 -0
  41. package/dts/chart/bar/BarStackGroup.d.ts.map +1 -0
  42. package/dts/chart/bar/DefaultBar.d.ts +17 -0
  43. package/dts/chart/bar/DefaultBar.d.ts.map +1 -0
  44. package/dts/chart/bar/DefaultBarStack.d.ts +16 -0
  45. package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -0
  46. package/dts/chart/bar/index.d.ts +8 -0
  47. package/dts/chart/bar/index.d.ts.map +1 -0
  48. package/dts/chart/gradient/Gradient.d.ts +35 -0
  49. package/dts/chart/gradient/Gradient.d.ts.map +1 -0
  50. package/dts/chart/gradient/index.d.ts +2 -0
  51. package/dts/chart/gradient/index.d.ts.map +1 -0
  52. package/dts/chart/index.d.ts +14 -0
  53. package/dts/chart/index.d.ts.map +1 -0
  54. package/dts/chart/line/DefaultReferenceLineLabel.d.ts +9 -0
  55. package/dts/chart/line/DefaultReferenceLineLabel.d.ts.map +1 -0
  56. package/dts/chart/line/DottedLine.d.ts +26 -0
  57. package/dts/chart/line/DottedLine.d.ts.map +1 -0
  58. package/dts/chart/line/Line.d.ts +122 -0
  59. package/dts/chart/line/Line.d.ts.map +1 -0
  60. package/dts/chart/line/LineChart.d.ts +77 -0
  61. package/dts/chart/line/LineChart.d.ts.map +1 -0
  62. package/dts/chart/line/ReferenceLine.d.ts +178 -0
  63. package/dts/chart/line/ReferenceLine.d.ts.map +1 -0
  64. package/dts/chart/line/SolidLine.d.ts +25 -0
  65. package/dts/chart/line/SolidLine.d.ts.map +1 -0
  66. package/dts/chart/line/index.d.ts +7 -0
  67. package/dts/chart/line/index.d.ts.map +1 -0
  68. package/dts/chart/point/DefaultPointLabel.d.ts +10 -0
  69. package/dts/chart/point/DefaultPointLabel.d.ts.map +1 -0
  70. package/dts/chart/point/Point.d.ts +201 -0
  71. package/dts/chart/point/Point.d.ts.map +1 -0
  72. package/dts/chart/point/index.d.ts +3 -0
  73. package/dts/chart/point/index.d.ts.map +1 -0
  74. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts +24 -0
  75. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -0
  76. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts +12 -0
  77. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts.map +1 -0
  78. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +10 -0
  79. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -0
  80. package/dts/chart/scrubber/Scrubber.d.ts +290 -0
  81. package/dts/chart/scrubber/Scrubber.d.ts.map +1 -0
  82. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts +70 -0
  83. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -0
  84. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts +32 -0
  85. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts.map +1 -0
  86. package/dts/chart/scrubber/ScrubberProvider.d.ts +17 -0
  87. package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -0
  88. package/dts/chart/scrubber/index.d.ts +5 -0
  89. package/dts/chart/scrubber/index.d.ts.map +1 -0
  90. package/dts/chart/text/ChartText.d.ts +117 -0
  91. package/dts/chart/text/ChartText.d.ts.map +1 -0
  92. package/dts/chart/text/ChartTextGroup.d.ts +61 -0
  93. package/dts/chart/text/ChartTextGroup.d.ts.map +1 -0
  94. package/dts/chart/text/index.d.ts +3 -0
  95. package/dts/chart/text/index.d.ts.map +1 -0
  96. package/dts/chart/utils/axis.d.ts +342 -0
  97. package/dts/chart/utils/axis.d.ts.map +1 -0
  98. package/dts/chart/utils/bar.d.ts +20 -0
  99. package/dts/chart/utils/bar.d.ts.map +1 -0
  100. package/dts/chart/utils/chart.d.ts +117 -0
  101. package/dts/chart/utils/chart.d.ts.map +1 -0
  102. package/dts/chart/utils/context.d.ts +101 -0
  103. package/dts/chart/utils/context.d.ts.map +1 -0
  104. package/dts/chart/utils/gradient.d.ts +104 -0
  105. package/dts/chart/utils/gradient.d.ts.map +1 -0
  106. package/dts/chart/utils/index.d.ts +12 -0
  107. package/dts/chart/utils/index.d.ts.map +1 -0
  108. package/dts/chart/utils/interpolate.d.ts +112 -0
  109. package/dts/chart/utils/interpolate.d.ts.map +1 -0
  110. package/dts/chart/utils/path.d.ts +130 -0
  111. package/dts/chart/utils/path.d.ts.map +1 -0
  112. package/dts/chart/utils/point.d.ts +104 -0
  113. package/dts/chart/utils/point.d.ts.map +1 -0
  114. package/dts/chart/utils/scale.d.ts +43 -0
  115. package/dts/chart/utils/scale.d.ts.map +1 -0
  116. package/dts/chart/utils/scrubber.d.ts +39 -0
  117. package/dts/chart/utils/scrubber.d.ts.map +1 -0
  118. package/dts/chart/utils/transition.d.ts +65 -0
  119. package/dts/chart/utils/transition.d.ts.map +1 -0
  120. package/dts/index.d.ts +1 -0
  121. package/dts/index.d.ts.map +1 -1
  122. package/dts/sparkline/Sparkline.d.ts +44 -9
  123. package/dts/sparkline/Sparkline.d.ts.map +1 -1
  124. package/dts/sparkline/SparklineArea.d.ts +4 -0
  125. package/dts/sparkline/SparklineArea.d.ts.map +1 -1
  126. package/dts/sparkline/SparklineAreaPattern.d.ts +5 -0
  127. package/dts/sparkline/SparklineAreaPattern.d.ts.map +1 -1
  128. package/dts/sparkline/SparklineGradient.d.ts +5 -0
  129. package/dts/sparkline/SparklineGradient.d.ts.map +1 -1
  130. package/dts/sparkline/generateSparklineWithId.d.ts +1 -0
  131. package/dts/sparkline/generateSparklineWithId.d.ts.map +1 -1
  132. package/dts/sparkline/sparkline-interactive/SparklineInteractive.d.ts +9 -0
  133. package/dts/sparkline/sparkline-interactive/SparklineInteractive.d.ts.map +1 -1
  134. package/dts/sparkline/sparkline-interactive/SparklineInteractiveAnimatedPath.d.ts +3 -0
  135. package/dts/sparkline/sparkline-interactive/SparklineInteractiveAnimatedPath.d.ts.map +1 -1
  136. package/dts/sparkline/sparkline-interactive/SparklineInteractivePaths.d.ts +2 -1
  137. package/dts/sparkline/sparkline-interactive/SparklineInteractivePaths.d.ts.map +1 -1
  138. package/esm/chart/CartesianChart.css +1 -0
  139. package/esm/chart/CartesianChart.js +313 -0
  140. package/esm/chart/ChartProvider.js +10 -0
  141. package/esm/chart/Path.js +95 -0
  142. package/esm/chart/PeriodSelector.css +1 -0
  143. package/esm/chart/PeriodSelector.js +112 -0
  144. package/esm/chart/area/Area.js +75 -0
  145. package/esm/chart/area/AreaChart.js +173 -0
  146. package/esm/chart/area/DottedArea.js +87 -0
  147. package/esm/chart/area/GradientArea.js +65 -0
  148. package/esm/chart/area/SolidArea.js +47 -0
  149. package/esm/chart/area/index.js +7 -0
  150. package/esm/chart/axis/Axis.js +25 -0
  151. package/esm/chart/axis/DefaultAxisTickLabel.js +15 -0
  152. package/esm/chart/axis/XAxis.css +2 -0
  153. package/esm/chart/axis/XAxis.js +219 -0
  154. package/esm/chart/axis/YAxis.css +2 -0
  155. package/esm/chart/axis/YAxis.js +214 -0
  156. package/esm/chart/axis/index.js +6 -0
  157. package/esm/chart/bar/Bar.js +61 -0
  158. package/esm/chart/bar/BarChart.js +130 -0
  159. package/esm/chart/bar/BarPlot.js +97 -0
  160. package/esm/chart/bar/BarStack.js +561 -0
  161. package/esm/chart/bar/BarStackGroup.js +86 -0
  162. package/esm/chart/bar/DefaultBar.js +61 -0
  163. package/esm/chart/bar/DefaultBarStack.js +58 -0
  164. package/esm/chart/bar/index.js +9 -0
  165. package/esm/chart/gradient/Gradient.js +104 -0
  166. package/esm/chart/gradient/index.js +1 -0
  167. package/esm/chart/index.js +15 -0
  168. package/esm/chart/line/DefaultReferenceLineLabel.js +81 -0
  169. package/esm/chart/line/DottedLine.js +59 -0
  170. package/esm/chart/line/Line.js +185 -0
  171. package/esm/chart/line/LineChart.js +132 -0
  172. package/esm/chart/line/ReferenceLine.js +140 -0
  173. package/esm/chart/line/SolidLine.js +55 -0
  174. package/esm/chart/line/index.js +8 -0
  175. package/esm/chart/point/DefaultPointLabel.js +44 -0
  176. package/esm/chart/point/Point.css +2 -0
  177. package/esm/chart/point/Point.js +180 -0
  178. package/esm/chart/point/index.js +2 -0
  179. package/esm/chart/scrubber/DefaultScrubberBeacon.js +155 -0
  180. package/esm/chart/scrubber/DefaultScrubberBeaconLabel.js +46 -0
  181. package/esm/chart/scrubber/DefaultScrubberLabel.js +30 -0
  182. package/esm/chart/scrubber/Scrubber.js +189 -0
  183. package/esm/chart/scrubber/ScrubberBeaconGroup.js +166 -0
  184. package/esm/chart/scrubber/ScrubberBeaconLabelGroup.js +186 -0
  185. package/esm/chart/scrubber/ScrubberProvider.js +228 -0
  186. package/esm/chart/scrubber/index.js +4 -0
  187. package/esm/chart/text/ChartText.js +230 -0
  188. package/esm/chart/text/ChartTextGroup.js +227 -0
  189. package/esm/chart/text/index.js +4 -0
  190. package/esm/chart/utils/axis.js +593 -0
  191. package/esm/chart/utils/bar.js +24 -0
  192. package/esm/chart/utils/chart.js +255 -0
  193. package/esm/chart/utils/context.js +15 -0
  194. package/esm/chart/utils/gradient.js +257 -0
  195. package/esm/chart/utils/index.js +13 -0
  196. package/esm/chart/utils/interpolate.js +644 -0
  197. package/esm/chart/utils/path.js +227 -0
  198. package/esm/chart/utils/point.js +187 -0
  199. package/esm/chart/utils/scale.js +48 -0
  200. package/esm/chart/utils/scrubber.js +132 -0
  201. package/esm/chart/utils/transition.js +111 -0
  202. package/esm/index.js +4 -1
  203. package/esm/sparkline/Sparkline.js +129 -15
  204. package/esm/sparkline/SparklineArea.js +7 -2
  205. package/esm/sparkline/SparklineAreaPattern.js +4 -2
  206. package/esm/sparkline/SparklineGradient.js +16 -58
  207. package/esm/sparkline/generateSparklineWithId.js +3 -2
  208. package/esm/sparkline/sparkline-interactive/SparklineInteractive.js +5 -1
  209. package/esm/sparkline/sparkline-interactive/SparklineInteractiveAnimatedPath.js +5 -2
  210. package/esm/sparkline/sparkline-interactive/SparklineInteractiveMarkerDates.js +1 -1
  211. package/esm/sparkline/sparkline-interactive/SparklineInteractivePaths.js +4 -0
  212. package/package.json +13 -9
@@ -0,0 +1,561 @@
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
+ import React, { memo, useMemo } from 'react';
7
+ import { useCartesianChartContext } from '../ChartProvider';
8
+ import { evaluateGradientAtValue, getGradientConfig } from '../utils/gradient';
9
+ import { Bar } from './Bar';
10
+ import { DefaultBarStack } from './DefaultBarStack';
11
+ import { jsx as _jsx } from "react/jsx-runtime";
12
+ const EPSILON = 1e-4;
13
+ /**
14
+ * BarStack component that renders a single stack of bars at a specific category index.
15
+ * Handles the stacking logic for bars within a single category.
16
+ */
17
+ export const BarStack = /*#__PURE__*/memo(_ref => {
18
+ let {
19
+ series,
20
+ categoryIndex,
21
+ x,
22
+ width,
23
+ yScale,
24
+ rect,
25
+ BarComponent: defaultBarComponent,
26
+ fillOpacity: defaultFillOpacity,
27
+ stroke: defaultStroke,
28
+ strokeWidth: defaultStrokeWidth,
29
+ borderRadius = 4,
30
+ BarStackComponent = DefaultBarStack,
31
+ stackGap,
32
+ barMinSize,
33
+ stackMinSize,
34
+ roundBaseline,
35
+ transition
36
+ } = _ref;
37
+ const {
38
+ getSeriesData,
39
+ getXAxis,
40
+ getXScale,
41
+ getSeries
42
+ } = useCartesianChartContext();
43
+ const xScale = getXScale();
44
+ const barMinSizePx = barMinSize;
45
+ const stackMinSizePx = stackMinSize;
46
+ const xAxis = getXAxis();
47
+ const baseline = useMemo(() => {
48
+ var _yScale;
49
+ const domain = yScale.domain();
50
+ const [domainMin, domainMax] = domain;
51
+ const baselineValue = domainMin >= 0 ? domainMin : domainMax <= 0 ? domainMax : 0;
52
+ const baseline = (_yScale = yScale(baselineValue)) !== null && _yScale !== void 0 ? _yScale : rect.y + rect.height;
53
+ return Math.max(rect.y, Math.min(baseline, rect.y + rect.height));
54
+ }, [rect.height, rect.y, yScale]);
55
+ const seriesGradients = useMemo(() => {
56
+ return series.map(s => {
57
+ if (!s.gradient || !xScale || !yScale) return null;
58
+ const gradientScale = s.gradient.axis === 'x' ? xScale : yScale;
59
+ const stops = getGradientConfig(s.gradient, xScale, yScale);
60
+ if (!stops) return null;
61
+ return {
62
+ seriesId: s.id,
63
+ gradient: s.gradient,
64
+ scale: gradientScale,
65
+ stops
66
+ };
67
+ });
68
+ }, [series, xScale, yScale]);
69
+
70
+ // Calculate bars for this specific category
71
+ const {
72
+ bars,
73
+ stackRect
74
+ } = useMemo(() => {
75
+ let allBars = [];
76
+
77
+ // Track how many bars we've stacked in each direction for gap calculation
78
+ let positiveBarCount = 0;
79
+ let negativeBarCount = 0;
80
+
81
+ // Track stack bounds for clipping
82
+ let minY = Infinity;
83
+ let maxY = -Infinity;
84
+
85
+ // Process each series in the stack
86
+ series.forEach(s => {
87
+ var _yScale2, _yScale3, _s$color;
88
+ const data = getSeriesData(s.id);
89
+ if (!data) return;
90
+ const value = data[categoryIndex];
91
+ if (value === null || value === undefined) return;
92
+ const originalData = s.data;
93
+ const originalValue = originalData === null || originalData === void 0 ? void 0 : originalData[categoryIndex];
94
+ // Only apply gap logic if the original data wasn't tuple format
95
+ const shouldApplyGap = !Array.isArray(originalValue);
96
+
97
+ // Sort to be in ascending order
98
+ const [bottom, top] = value.sort((a, b) => a - b);
99
+ const isAboveBaseline = bottom >= 0 && top !== bottom;
100
+ const isBelowBaseline = bottom <= 0 && bottom !== top;
101
+ const barBottom = (_yScale2 = yScale(bottom)) !== null && _yScale2 !== void 0 ? _yScale2 : baseline;
102
+ const barTop = (_yScale3 = yScale(top)) !== null && _yScale3 !== void 0 ? _yScale3 : baseline;
103
+
104
+ // Track bar counts for later gap calculations
105
+ if (shouldApplyGap) {
106
+ if (isAboveBaseline) {
107
+ positiveBarCount++;
108
+ } else if (isBelowBaseline) {
109
+ negativeBarCount++;
110
+ }
111
+ }
112
+
113
+ // Calculate height (remember SVG y coordinates are inverted)
114
+ const height = Math.abs(barBottom - barTop);
115
+ const y = Math.min(barBottom, barTop);
116
+
117
+ // Skip bars that would have zero or negative height
118
+ if (height <= 0) {
119
+ return;
120
+ }
121
+
122
+ // Update stack bounds
123
+ minY = Math.min(minY, y);
124
+ maxY = Math.max(maxY, y + height);
125
+ let barFill = (_s$color = s.color) !== null && _s$color !== void 0 ? _s$color : 'var(--color-fgPrimary)';
126
+
127
+ // Evaluate gradient if provided (using precomputed stops)
128
+ const seriesGradientConfig = seriesGradients.find(g => (g === null || g === void 0 ? void 0 : g.seriesId) === s.id);
129
+ if (seriesGradientConfig && originalValue !== null && originalValue !== undefined) {
130
+ var _seriesGradientConfig;
131
+ const axis = (_seriesGradientConfig = seriesGradientConfig.gradient.axis) !== null && _seriesGradientConfig !== void 0 ? _seriesGradientConfig : 'y';
132
+ // For x-axis gradient, use the categoryIndex
133
+ // For y-axis gradient, use the ORIGINAL data value (not the processed top value)
134
+ // This is important for bar charts where originalValue might be a single number (e.g., -40, 15)
135
+ // or a tuple (e.g., [0, 10] for range bars)
136
+ let evalValue;
137
+ if (axis === 'x') {
138
+ evalValue = categoryIndex;
139
+ } else {
140
+ // Use original value for evaluation - handles both single numbers and tuples
141
+ evalValue = Array.isArray(originalValue) ? originalValue[1] : originalValue;
142
+ }
143
+ const evaluatedColor = evaluateGradientAtValue(seriesGradientConfig.stops, evalValue, seriesGradientConfig.scale);
144
+ if (evaluatedColor) {
145
+ // Only apply gradient color if fill is not explicitly set
146
+ barFill = evaluatedColor;
147
+ }
148
+ }
149
+ allBars.push({
150
+ seriesId: s.id,
151
+ x,
152
+ y,
153
+ width,
154
+ height,
155
+ dataY: value,
156
+ // Store the actual data value
157
+ fill: barFill,
158
+ // Check if the bar should be rounded based on the baseline, with an epsilon to handle floating-point rounding
159
+ roundTop: roundBaseline || Math.abs(barTop - baseline) >= EPSILON,
160
+ roundBottom: roundBaseline || Math.abs(barBottom - baseline) >= EPSILON,
161
+ shouldApplyGap
162
+ });
163
+ });
164
+
165
+ // Apply proportional gap distribution to maintain total stack height
166
+ if (stackGap && allBars.length > 1) {
167
+ // Separate bars by baseline side
168
+ const barsAboveBaseline = allBars.filter(bar => {
169
+ const [bottom, top] = bar.dataY.sort((a, b) => a - b);
170
+ return bottom >= 0 && top !== bottom && bar.shouldApplyGap;
171
+ });
172
+ const barsBelowBaseline = allBars.filter(bar => {
173
+ const [bottom, top] = bar.dataY.sort((a, b) => a - b);
174
+ return bottom <= 0 && bottom !== top && bar.shouldApplyGap;
175
+ });
176
+
177
+ // Apply proportional gaps to bars above baseline
178
+ if (barsAboveBaseline.length > 1) {
179
+ const totalGapSpace = stackGap * (barsAboveBaseline.length - 1);
180
+ const totalDataHeight = barsAboveBaseline.reduce((sum, bar) => sum + bar.height, 0);
181
+ const heightReduction = totalGapSpace / totalDataHeight;
182
+
183
+ // Sort bars by position (from baseline upward)
184
+ const sortedBars = barsAboveBaseline.sort((a, b) => b.y - a.y);
185
+ let currentY = baseline;
186
+ sortedBars.forEach((bar, index) => {
187
+ // Reduce bar height proportionally
188
+ const newHeight = bar.height * (1 - heightReduction);
189
+ const newY = currentY - newHeight;
190
+
191
+ // Update the bar in allBars array
192
+ const barIndex = allBars.findIndex(b => b.seriesId === bar.seriesId);
193
+ if (barIndex !== -1) {
194
+ allBars[barIndex] = _objectSpread(_objectSpread({}, allBars[barIndex]), {}, {
195
+ height: newHeight,
196
+ y: newY
197
+ });
198
+ }
199
+
200
+ // Move to next position (include gap for next bar)
201
+ currentY = newY - (index < sortedBars.length - 1 ? stackGap : 0);
202
+ });
203
+ }
204
+
205
+ // Apply proportional gaps to bars below baseline
206
+ if (barsBelowBaseline.length > 1) {
207
+ const totalGapSpace = stackGap * (barsBelowBaseline.length - 1);
208
+ const totalDataHeight = barsBelowBaseline.reduce((sum, bar) => sum + bar.height, 0);
209
+ const heightReduction = totalGapSpace / totalDataHeight;
210
+
211
+ // Sort bars by position (from baseline downward)
212
+ const sortedBars = barsBelowBaseline.sort((a, b) => a.y - b.y);
213
+ let currentY = baseline;
214
+ sortedBars.forEach((bar, index) => {
215
+ // Reduce bar height proportionally
216
+ const newHeight = bar.height * (1 - heightReduction);
217
+
218
+ // Update the bar in allBars array
219
+ const barIndex = allBars.findIndex(b => b.seriesId === bar.seriesId);
220
+ if (barIndex !== -1) {
221
+ allBars[barIndex] = _objectSpread(_objectSpread({}, allBars[barIndex]), {}, {
222
+ height: newHeight,
223
+ y: currentY
224
+ });
225
+ }
226
+
227
+ // Move to next position (include gap for next bar)
228
+ currentY = currentY + newHeight + (index < sortedBars.length - 1 ? stackGap : 0);
229
+ });
230
+ }
231
+
232
+ // Recalculate stack bounds after gap adjustments
233
+ if (allBars.length > 0) {
234
+ minY = Math.min(...allBars.map(bar => bar.y));
235
+ maxY = Math.max(...allBars.map(bar => bar.y + bar.height));
236
+ }
237
+ }
238
+
239
+ // Apply barMinSize constraints
240
+ if (barMinSizePx) {
241
+ // First, expand bars that need it and track the expansion
242
+ const expandedBars = allBars.map((bar, index) => {
243
+ if (bar.height < barMinSizePx) {
244
+ var _yScale4, _yScale5, _yScale6, _yScale7;
245
+ const heightIncrease = barMinSizePx - bar.height;
246
+ const bottom = 0;
247
+ const top = 0;
248
+
249
+ // Determine how to expand the bar
250
+ let newBottom = bottom;
251
+ let newTop = top;
252
+ const scaleUnit = Math.abs(((_yScale4 = yScale(1)) !== null && _yScale4 !== void 0 ? _yScale4 : 0) - ((_yScale5 = yScale(0)) !== null && _yScale5 !== void 0 ? _yScale5 : 0));
253
+ if (bottom === 0) {
254
+ // Expand away from baseline (upward for positive)
255
+ newTop = top + heightIncrease / scaleUnit;
256
+ } else if (top === 0) {
257
+ // Expand away from baseline (downward for negative)
258
+ newBottom = bottom - heightIncrease / scaleUnit;
259
+ } else {
260
+ // Expand in both directions
261
+ const halfIncrease = heightIncrease / scaleUnit / 2;
262
+ newBottom = bottom - halfIncrease;
263
+ newTop = top + halfIncrease;
264
+ }
265
+
266
+ // Recalculate bar position with new data values
267
+ const newBarBottom = (_yScale6 = yScale(newBottom)) !== null && _yScale6 !== void 0 ? _yScale6 : baseline;
268
+ const newBarTop = (_yScale7 = yScale(newTop)) !== null && _yScale7 !== void 0 ? _yScale7 : baseline;
269
+ const newHeight = Math.abs(newBarBottom - newBarTop);
270
+ const newY = Math.min(newBarBottom, newBarTop);
271
+ return _objectSpread(_objectSpread({}, bar), {}, {
272
+ height: newHeight,
273
+ y: newY,
274
+ wasExpanded: true
275
+ });
276
+ }
277
+ return _objectSpread(_objectSpread({}, bar), {}, {
278
+ wasExpanded: false
279
+ });
280
+ });
281
+
282
+ // Now reposition all bars to avoid overlaps, similar to stackMinSize logic
283
+
284
+ // Sort bars by position to maintain order
285
+ const sortedExpandedBars = [...expandedBars].sort((a, b) => a.y - b.y);
286
+
287
+ // Determine if we have bars above and below baseline
288
+ const barsAboveBaseline = sortedExpandedBars.filter(bar => bar.y + bar.height <= baseline);
289
+ const barsBelowBaseline = sortedExpandedBars.filter(bar => bar.y >= baseline);
290
+
291
+ // Create a map of new positions
292
+ const newPositions = new Map();
293
+
294
+ // Start positioning from the baseline and work outward
295
+ let currentYAbove = baseline; // Start at baseline, work upward (decreasing Y)
296
+ let currentYBelow = baseline; // Start at baseline, work downward (increasing Y)
297
+
298
+ // Position bars above baseline (positive values, decreasing Y)
299
+ for (let i = barsAboveBaseline.length - 1; i >= 0; i--) {
300
+ const bar = barsAboveBaseline[i];
301
+ const newY = currentYAbove - bar.height;
302
+ newPositions.set(bar.seriesId, {
303
+ y: newY,
304
+ height: bar.height
305
+ });
306
+
307
+ // Update currentYAbove for next bar (preserve gaps)
308
+ if (i > 0) {
309
+ const currentBar = barsAboveBaseline[i];
310
+ const nextBar = barsAboveBaseline[i - 1];
311
+ // Find original bars to get original gap
312
+ const originalCurrent = allBars.find(b => b.seriesId === currentBar.seriesId);
313
+ const originalNext = allBars.find(b => b.seriesId === nextBar.seriesId);
314
+ const originalGap = originalCurrent.y - (originalNext.y + originalNext.height);
315
+ currentYAbove = newY - originalGap;
316
+ }
317
+ }
318
+
319
+ // Position bars below baseline (negative values, increasing Y)
320
+ for (let i = 0; i < barsBelowBaseline.length; i++) {
321
+ const bar = barsBelowBaseline[i];
322
+ const newY = currentYBelow;
323
+ newPositions.set(bar.seriesId, {
324
+ y: newY,
325
+ height: bar.height
326
+ });
327
+
328
+ // Update currentYBelow for next bar (preserve gaps)
329
+ if (i < barsBelowBaseline.length - 1) {
330
+ const currentBar = barsBelowBaseline[i];
331
+ const nextBar = barsBelowBaseline[i + 1];
332
+ // Find original bars to get original gap
333
+ const originalCurrent = allBars.find(b => b.seriesId === currentBar.seriesId);
334
+ const originalNext = allBars.find(b => b.seriesId === nextBar.seriesId);
335
+ const originalGap = originalNext.y - (originalCurrent.y + originalCurrent.height);
336
+ currentYBelow = newY + bar.height + originalGap;
337
+ }
338
+ }
339
+
340
+ // Apply new positions to all bars
341
+ allBars = expandedBars.map(bar => {
342
+ const newPos = newPositions.get(bar.seriesId);
343
+ if (newPos) {
344
+ return _objectSpread(_objectSpread({}, bar), {}, {
345
+ y: newPos.y,
346
+ height: newPos.height
347
+ });
348
+ }
349
+ return bar;
350
+ });
351
+
352
+ // Recalculate stack bounds after barMinSize expansion and repositioning
353
+ if (allBars.length > 0) {
354
+ minY = Math.min(...allBars.map(bar => bar.y));
355
+ maxY = Math.max(...allBars.map(bar => bar.y + bar.height));
356
+ }
357
+ }
358
+
359
+ // Apply border radius logic (will be reapplied after stackMinSize if needed)
360
+ const applyBorderRadiusLogic = bars => {
361
+ return bars.sort((a, b) => b.y - a.y).map((a, index) => {
362
+ const barBefore = index > 0 ? bars[index - 1] : null;
363
+ const barAfter = index < bars.length - 1 ? bars[index + 1] : null;
364
+ const shouldRoundTop = index === bars.length - 1 || a.shouldApplyGap && stackGap || !a.shouldApplyGap && barAfter && barAfter.y + barAfter.height !== a.y;
365
+ const shouldRoundBottom = index === 0 || a.shouldApplyGap && stackGap || !a.shouldApplyGap && barBefore && barBefore.y !== a.y + a.height;
366
+ return _objectSpread(_objectSpread({}, a), {}, {
367
+ roundTop: Boolean(a.roundTop && shouldRoundTop),
368
+ roundBottom: Boolean(a.roundBottom && shouldRoundBottom)
369
+ });
370
+ });
371
+ };
372
+ allBars = applyBorderRadiusLogic(allBars);
373
+
374
+ // Calculate the bounding rect for the entire stack
375
+ let stackBounds = {
376
+ x,
377
+ y: minY === Infinity ? baseline : minY,
378
+ width,
379
+ height: maxY === -Infinity ? 0 : maxY - minY
380
+ };
381
+
382
+ // Apply stackMinSize constraints
383
+ if (stackMinSizePx) {
384
+ if (allBars.length === 1 && stackBounds.height < stackMinSizePx) {
385
+ var _yScale8, _yScale9, _yScale0, _yScale1;
386
+ // For single bars (non-stacked), treat stackMinSize like barMinSize
387
+
388
+ const bar = allBars[0];
389
+ const heightIncrease = stackMinSizePx - bar.height;
390
+ const bottom = 0;
391
+ const top = 0;
392
+
393
+ // Determine how to expand the bar (same logic as barMinSize)
394
+ let newBottom = bottom;
395
+ let newTop = top;
396
+ const scaleUnit = Math.abs(((_yScale8 = yScale(1)) !== null && _yScale8 !== void 0 ? _yScale8 : 0) - ((_yScale9 = yScale(0)) !== null && _yScale9 !== void 0 ? _yScale9 : 0));
397
+ if (bottom === 0) {
398
+ // Expand away from baseline (upward for positive)
399
+ newTop = top + heightIncrease / scaleUnit;
400
+ } else if (top === 0) {
401
+ // Expand away from baseline (downward for negative)
402
+ newBottom = bottom - heightIncrease / scaleUnit;
403
+ } else {
404
+ // Expand in both directions
405
+ const halfIncrease = heightIncrease / scaleUnit / 2;
406
+ newBottom = bottom - halfIncrease;
407
+ newTop = top + halfIncrease;
408
+ }
409
+
410
+ // Recalculate bar position with new data values
411
+ const newBarBottom = (_yScale0 = yScale(newBottom)) !== null && _yScale0 !== void 0 ? _yScale0 : baseline;
412
+ const newBarTop = (_yScale1 = yScale(newTop)) !== null && _yScale1 !== void 0 ? _yScale1 : baseline;
413
+ const newHeight = Math.abs(newBarBottom - newBarTop);
414
+ const newY = Math.min(newBarBottom, newBarTop);
415
+ allBars[0] = _objectSpread(_objectSpread({}, bar), {}, {
416
+ height: newHeight,
417
+ y: newY
418
+ });
419
+
420
+ // Recalculate stack bounds
421
+ stackBounds = {
422
+ x,
423
+ y: newY,
424
+ width,
425
+ height: newHeight
426
+ };
427
+ } else if (allBars.length > 1 && stackBounds.height < stackMinSizePx) {
428
+ // For multiple bars (stacked), scale heights while preserving gaps
429
+
430
+ // Calculate total bar height (excluding gaps)
431
+ const totalBarHeight = allBars.reduce((sum, bar) => sum + bar.height, 0);
432
+ const totalGapHeight = stackBounds.height - totalBarHeight;
433
+
434
+ // Calculate how much we need to increase bar heights
435
+ const requiredBarHeight = stackMinSizePx - totalGapHeight;
436
+ const barScaleFactor = requiredBarHeight / totalBarHeight;
437
+
438
+ // Sort bars by position to maintain order
439
+ const sortedBars = [...allBars].sort((a, b) => a.y - b.y);
440
+
441
+ // Determine if we have bars above and below baseline
442
+ const barsAboveBaseline = sortedBars.filter(bar => bar.y + bar.height <= baseline);
443
+ const barsBelowBaseline = sortedBars.filter(bar => bar.y >= baseline);
444
+
445
+ // Create a map of new positions
446
+ const newPositions = new Map();
447
+
448
+ // Start positioning from the baseline and work outward
449
+ let currentYAbove = baseline; // Start at baseline, work upward (decreasing Y)
450
+ let currentYBelow = baseline; // Start at baseline, work downward (increasing Y)
451
+
452
+ // Position bars above baseline (positive values, decreasing Y)
453
+ for (let i = barsAboveBaseline.length - 1; i >= 0; i--) {
454
+ const bar = barsAboveBaseline[i];
455
+ const newHeight = bar.height * barScaleFactor;
456
+ const newY = currentYAbove - newHeight;
457
+ newPositions.set(bar.seriesId, {
458
+ y: newY,
459
+ height: newHeight
460
+ });
461
+
462
+ // Update currentYAbove for next bar (preserve gaps)
463
+ if (i > 0) {
464
+ const currentBar = barsAboveBaseline[i];
465
+ const nextBar = barsAboveBaseline[i - 1];
466
+ const originalGap = currentBar.y - (nextBar.y + nextBar.height);
467
+ currentYAbove = newY - originalGap;
468
+ }
469
+ }
470
+
471
+ // Position bars below baseline (negative values, increasing Y)
472
+ for (let i = 0; i < barsBelowBaseline.length; i++) {
473
+ const bar = barsBelowBaseline[i];
474
+ const newHeight = bar.height * barScaleFactor;
475
+ const newY = currentYBelow;
476
+ newPositions.set(bar.seriesId, {
477
+ y: newY,
478
+ height: newHeight
479
+ });
480
+
481
+ // Update currentYBelow for next bar (preserve gaps)
482
+ if (i < barsBelowBaseline.length - 1) {
483
+ const currentBar = barsBelowBaseline[i];
484
+ const nextBar = barsBelowBaseline[i + 1];
485
+ const originalGap = nextBar.y - (currentBar.y + currentBar.height);
486
+ currentYBelow = newY + newHeight + originalGap;
487
+ }
488
+ }
489
+
490
+ // Apply new positions to all bars
491
+ allBars = allBars.map(bar => {
492
+ const newPos = newPositions.get(bar.seriesId);
493
+ if (!newPos) return bar;
494
+ return _objectSpread(_objectSpread({}, bar), {}, {
495
+ height: newPos.height,
496
+ y: newPos.y
497
+ });
498
+ });
499
+
500
+ // Recalculate stack bounds
501
+ const newMinY = Math.min(...allBars.map(bar => bar.y));
502
+ const newMaxY = Math.max(...allBars.map(bar => bar.y + bar.height));
503
+ stackBounds = {
504
+ x,
505
+ y: newMinY,
506
+ width,
507
+ height: newMaxY - newMinY
508
+ };
509
+ }
510
+
511
+ // Reapply border radius logic only if we actually scaled
512
+ if (stackBounds.height < stackMinSizePx) {
513
+ allBars = applyBorderRadiusLogic(allBars);
514
+ }
515
+ }
516
+ return {
517
+ bars: allBars,
518
+ stackRect: stackBounds
519
+ };
520
+ }, [series, stackGap, barMinSizePx, x, baseline, width, stackMinSizePx, getSeriesData, categoryIndex, yScale, seriesGradients, roundBaseline]);
521
+ const xData = xAxis !== null && xAxis !== void 0 && xAxis.data && Array.isArray(xAxis.data) && typeof xAxis.data[0] === 'number' ? xAxis.data : undefined;
522
+ const dataX = xData ? xData[categoryIndex] : categoryIndex;
523
+ const barElements = bars.map((bar, index) => {
524
+ var _bar$fillOpacity, _bar$stroke, _bar$strokeWidth;
525
+ return /*#__PURE__*/_jsx(Bar, {
526
+ BarComponent: bar.BarComponent || defaultBarComponent,
527
+ borderRadius: borderRadius,
528
+ dataX: dataX,
529
+ dataY: bar.dataY,
530
+ fill: bar.fill,
531
+ fillOpacity: (_bar$fillOpacity = bar.fillOpacity) !== null && _bar$fillOpacity !== void 0 ? _bar$fillOpacity : defaultFillOpacity,
532
+ height: bar.height,
533
+ originY: baseline,
534
+ roundBottom: bar.roundBottom,
535
+ roundTop: bar.roundTop,
536
+ stroke: (_bar$stroke = bar.stroke) !== null && _bar$stroke !== void 0 ? _bar$stroke : defaultStroke,
537
+ strokeWidth: (_bar$strokeWidth = bar.strokeWidth) !== null && _bar$strokeWidth !== void 0 ? _bar$strokeWidth : defaultStrokeWidth,
538
+ transition: transition,
539
+ width: bar.width,
540
+ x: bar.x,
541
+ y: bar.y
542
+ }, "".concat(bar.seriesId, "-").concat(categoryIndex, "-").concat(index));
543
+ });
544
+
545
+ // Check if the bar should be rounded based on the baseline, with an epsilon to handle floating-point rounding
546
+ const stackRoundBottom = roundBaseline || Math.abs(stackRect.y + stackRect.height - baseline) >= EPSILON;
547
+ const stackRoundTop = roundBaseline || Math.abs(stackRect.y - baseline) >= EPSILON;
548
+ return /*#__PURE__*/_jsx(BarStackComponent, {
549
+ borderRadius: borderRadius,
550
+ categoryIndex: categoryIndex,
551
+ height: stackRect.height,
552
+ roundBottom: stackRoundBottom,
553
+ roundTop: stackRoundTop,
554
+ transition: transition,
555
+ width: stackRect.width,
556
+ x: stackRect.x,
557
+ y: stackRect.y,
558
+ yOrigin: baseline,
559
+ children: barElements
560
+ });
561
+ });
@@ -0,0 +1,86 @@
1
+ const _excluded = ["series", "yAxisId", "stackIndex", "totalStacks", "barPadding"];
2
+ 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; }
3
+ 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; }
4
+ 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; }
5
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
6
+ 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); }
7
+ function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
8
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
9
+ import { memo, useMemo, createElement as _createElement } from 'react';
10
+ import { useCartesianChartContext } from '../ChartProvider';
11
+ import { getBarSizeAdjustment } from '../utils/bar';
12
+ import { isCategoricalScale } from '../utils/scale';
13
+ import { BarStack } from './BarStack';
14
+ /**
15
+ * BarStackGroup component that renders a group of stacks across all categories.
16
+ * Delegates the actual stacking logic to BarStack for each category.
17
+ */
18
+ export const BarStackGroup = /*#__PURE__*/memo(_ref => {
19
+ let {
20
+ series,
21
+ yAxisId,
22
+ stackIndex,
23
+ totalStacks,
24
+ barPadding = 0.1
25
+ } = _ref,
26
+ props = _objectWithoutProperties(_ref, _excluded);
27
+ const {
28
+ getXScale,
29
+ getYScale,
30
+ drawingArea,
31
+ dataLength
32
+ } = useCartesianChartContext();
33
+ const xScale = getXScale();
34
+ const yScale = getYScale(yAxisId);
35
+ const stackConfigs = useMemo(() => {
36
+ if (!xScale || !yScale || !drawingArea || dataLength === 0) return [];
37
+ if (!isCategoricalScale(xScale)) {
38
+ return [];
39
+ }
40
+ const categoryWidth = xScale.bandwidth();
41
+
42
+ // Calculate width for each stack within a category
43
+ // Only apply barPadding when there are multiple stacks
44
+ const gapWidth = totalStacks > 1 ? categoryWidth * barPadding / (totalStacks - 1) : 0;
45
+ const barWidth = categoryWidth / totalStacks - getBarSizeAdjustment(totalStacks, gapWidth);
46
+ const configs = [];
47
+
48
+ // Calculate position for each category
49
+ // todo: look at using xDomain for this instead of dataLength
50
+ for (let categoryIndex = 0; categoryIndex < dataLength; categoryIndex++) {
51
+ // Get x position for this category
52
+ const categoryX = xScale(categoryIndex);
53
+ if (categoryX !== undefined) {
54
+ // Calculate x position for this specific stack within the category
55
+ const stackX = categoryX + stackIndex * (barWidth + gapWidth);
56
+ configs.push({
57
+ categoryIndex,
58
+ x: stackX,
59
+ width: barWidth
60
+ });
61
+ }
62
+ }
63
+ return configs;
64
+ }, [xScale, yScale, drawingArea, dataLength, stackIndex, totalStacks, barPadding]);
65
+ if (xScale && !isCategoricalScale(xScale)) {
66
+ throw new Error('BarStackGroup requires a band scale for x-axis. See https://cds.coinbase.com/components/graphs/XAxis/#scale-type');
67
+ }
68
+ if (!yScale || !drawingArea || stackConfigs.length === 0) return null;
69
+ return stackConfigs.map(_ref2 => {
70
+ let {
71
+ categoryIndex,
72
+ x,
73
+ width
74
+ } = _ref2;
75
+ return /*#__PURE__*/_createElement(BarStack, _objectSpread(_objectSpread({}, props), {}, {
76
+ key: "stack-".concat(stackIndex, "-category-").concat(categoryIndex),
77
+ categoryIndex: categoryIndex,
78
+ rect: drawingArea,
79
+ series: series,
80
+ width: width,
81
+ x: x,
82
+ yAxisId: yAxisId,
83
+ yScale: yScale
84
+ }));
85
+ });
86
+ });