@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,593 @@
1
+ const _excluded = ["id"];
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 { useCallback, useMemo, useState } from 'react';
10
+ import { getChartDomain, getChartRange, isValidBounds } from './chart';
11
+ import { getCategoricalScale, getNumericScale, isCategoricalScale, isNumericScale } from './scale';
12
+ export const defaultAxisId = 'DEFAULT_AXIS_ID';
13
+ export const defaultAxisScaleType = 'linear';
14
+
15
+ /**
16
+ * Axis configuration with computed bounds
17
+ */
18
+
19
+ /**
20
+ * Axis configuration without computed bounds (used for input)
21
+ */
22
+
23
+ /**
24
+ * Gets a D3 scale based on the axis configuration.
25
+ * Handles both numeric (linear/log) and categorical (band) scales.
26
+ *
27
+ * For numeric scales, the domain limit controls whether bounds are "nice" (human-friendly)
28
+ * or "strict" (exact min/max). Range can be customized using function-based configuration.
29
+ *
30
+ * @param params - Scale parameters
31
+ * @returns The D3 scale function
32
+ * @throws An Error if bounds are invalid
33
+ */
34
+ export const getAxisScale = _ref => {
35
+ var _config$scaleType;
36
+ let {
37
+ config,
38
+ type,
39
+ range,
40
+ dataDomain
41
+ } = _ref;
42
+ const scaleType = (_config$scaleType = config === null || config === void 0 ? void 0 : config.scaleType) !== null && _config$scaleType !== void 0 ? _config$scaleType : 'linear';
43
+ let adjustedRange = range;
44
+
45
+ // Invert range for Y axis for SVG coordinate system
46
+ if (type === 'y') {
47
+ adjustedRange = {
48
+ min: adjustedRange.max,
49
+ max: adjustedRange.min
50
+ };
51
+ }
52
+ let adjustedDomain = dataDomain;
53
+ if (config !== null && config !== void 0 && config.domain) {
54
+ var _config$domain$min, _config$domain$max;
55
+ adjustedDomain = {
56
+ min: (_config$domain$min = config.domain.min) !== null && _config$domain$min !== void 0 ? _config$domain$min : dataDomain.min,
57
+ max: (_config$domain$max = config.domain.max) !== null && _config$domain$max !== void 0 ? _config$domain$max : dataDomain.max
58
+ };
59
+ }
60
+ if (!isValidBounds(adjustedDomain)) throw new Error('Invalid domain bounds. See https://cds.coinbase.com/http://localhost:3000/components/graphs/XAxis/#domain');
61
+ if (scaleType === 'band') {
62
+ var _config$categoryPaddi;
63
+ return getCategoricalScale({
64
+ domain: adjustedDomain,
65
+ range: adjustedRange,
66
+ padding: (_config$categoryPaddi = config === null || config === void 0 ? void 0 : config.categoryPadding) !== null && _config$categoryPaddi !== void 0 ? _config$categoryPaddi : 0.3
67
+ });
68
+ } else {
69
+ const scale = getNumericScale({
70
+ domain: adjustedDomain,
71
+ range: adjustedRange,
72
+ scaleType: scaleType
73
+ });
74
+ if ((config === null || config === void 0 ? void 0 : config.domainLimit) === 'nice') scale.nice();
75
+ return scale;
76
+ }
77
+ };
78
+
79
+ /**
80
+ * Formats the array of user-provided axis configs with default values and validates axis ids.
81
+ * Ensures at least one axis config exists if no input is provided.
82
+ * Requires specific axis ids when there are more than 1 axes.
83
+ * Defaults the axis id for a single axis config if there is no id.
84
+ * @param type - the type of axis, 'x' or 'y'
85
+ * @param axes - array of axis configs or single axis config
86
+ * @param defaultId - the default id to use for the axis
87
+ * @param defaultScaleType - the default scale type to use for the axis
88
+ * @returns array of axis configs with IDs
89
+ */
90
+ export const getAxisConfig = function (type, axes) {
91
+ let defaultId = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : defaultAxisId;
92
+ let defaultScaleType = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : defaultAxisScaleType;
93
+ const defaultDomainLimit = type === 'x' ? 'strict' : 'nice';
94
+ if (!axes) {
95
+ return [{
96
+ id: defaultId,
97
+ scaleType: defaultScaleType,
98
+ domainLimit: defaultDomainLimit
99
+ }];
100
+ }
101
+ if (Array.isArray(axes)) {
102
+ const axesLength = axes.length;
103
+ // forces id to be defined on every input config when there are multiple axes
104
+ if (axesLength > 1 && axes.some(_ref2 => {
105
+ let {
106
+ id
107
+ } = _ref2;
108
+ return id === undefined;
109
+ })) {
110
+ throw new Error('When defining multiple axes, each must have a unique id. See https://cds.coinbase.com/components/graphs/YAxis/#multiple-y-axes.');
111
+ }
112
+ return axes.map(_ref3 => {
113
+ let {
114
+ id
115
+ } = _ref3,
116
+ axis = _objectWithoutProperties(_ref3, _excluded);
117
+ return _objectSpread({
118
+ // defaults the axis id if only a single axis is provided
119
+ id: axesLength > 1 ? id !== null && id !== void 0 ? id : defaultAxisId : id,
120
+ scaleType: defaultScaleType,
121
+ domainLimit: defaultDomainLimit
122
+ }, axis);
123
+ });
124
+ }
125
+
126
+ // Single axis config
127
+ return [_objectSpread({
128
+ id: defaultId,
129
+ scaleType: defaultScaleType,
130
+ domainLimit: defaultDomainLimit
131
+ }, axes)];
132
+ };
133
+
134
+ /**
135
+ * Calculates the data domain for an axis based on its configuration and series data.
136
+ * Handles both x and y axes, categorical data, custom domain configurations, and stacking.
137
+ *
138
+ * @param axisParam - The axis configuration
139
+ * @param series - Array of series objects (for stacking support)
140
+ * @param axisType - Whether this is an 'x' or 'y' axis
141
+ * @returns The calculated axis bounds
142
+ */
143
+ export const getAxisDomain = (axisParam, series, axisType) => {
144
+ var _finalDomain$min, _finalDomain$max;
145
+ let dataDomain = null;
146
+ if (axisParam.data && Array.isArray(axisParam.data) && axisParam.data.length > 0) {
147
+ const firstItem = axisParam.data[0];
148
+ if (typeof firstItem === 'number') {
149
+ // Numeric data - use actual min/max values
150
+ const numericData = axisParam.data;
151
+ dataDomain = {
152
+ min: Math.min(...numericData),
153
+ max: Math.max(...numericData)
154
+ };
155
+ } else if (typeof firstItem === 'string') {
156
+ // String labels - use indices as domain (0 to length-1)
157
+ // This allows using string labels with linear scales
158
+ dataDomain = {
159
+ min: 0,
160
+ max: axisParam.data.length - 1
161
+ };
162
+ }
163
+ }
164
+
165
+ // Calculate domain from series data
166
+ const seriesDomain = axisType === 'x' ? getChartDomain(series) : getChartRange(series);
167
+
168
+ // If data sets the domain, use that instead of the series domain
169
+ const preferredDataDomain = dataDomain !== null && dataDomain !== void 0 ? dataDomain : seriesDomain;
170
+ const bounds = axisParam.domain;
171
+ let finalDomain;
172
+ if (typeof bounds === 'function') {
173
+ var _preferredDataDomain$, _preferredDataDomain$2;
174
+ // Apply the transform function to the base domain
175
+ // No need to default to 0 here since we'll do it once at the end
176
+ finalDomain = bounds({
177
+ min: (_preferredDataDomain$ = preferredDataDomain.min) !== null && _preferredDataDomain$ !== void 0 ? _preferredDataDomain$ : 0,
178
+ max: (_preferredDataDomain$2 = preferredDataDomain.max) !== null && _preferredDataDomain$2 !== void 0 ? _preferredDataDomain$2 : 0
179
+ });
180
+ } else if (bounds && typeof bounds === 'object') {
181
+ var _bounds$min, _bounds$max;
182
+ // Merge explicit bounds with calculated domain
183
+ finalDomain = {
184
+ min: (_bounds$min = bounds.min) !== null && _bounds$min !== void 0 ? _bounds$min : preferredDataDomain.min,
185
+ max: (_bounds$max = bounds.max) !== null && _bounds$max !== void 0 ? _bounds$max : preferredDataDomain.max
186
+ };
187
+ } else {
188
+ // Use the base domain as-is
189
+ finalDomain = preferredDataDomain;
190
+ }
191
+
192
+ // Ensure we always return valid bounds with no undefined values
193
+ return {
194
+ min: (_finalDomain$min = finalDomain.min) !== null && _finalDomain$min !== void 0 ? _finalDomain$min : 0,
195
+ max: (_finalDomain$max = finalDomain.max) !== null && _finalDomain$max !== void 0 ? _finalDomain$max : 0
196
+ };
197
+ };
198
+
199
+ /**
200
+ * Calculates the visual range for an axis based on the chart rectangle and configuration.
201
+ * Handles custom range configurations including functions and partial bounds.
202
+ *
203
+ * @param axisParam - The axis configuration
204
+ * @param chartRect - The chart drawing area rectangle
205
+ * @param axisType - Whether this is an 'x' or 'y' axis
206
+ * @returns The calculated axis range bounds
207
+ */
208
+ export const getAxisRange = (axisParam, chartRect, axisType) => {
209
+ // Calculate base range based on axis type
210
+ let baseRange;
211
+ if (axisType === 'x') {
212
+ baseRange = {
213
+ min: chartRect.x,
214
+ max: chartRect.x + chartRect.width
215
+ };
216
+ } else {
217
+ baseRange = {
218
+ min: chartRect.y,
219
+ max: chartRect.y + chartRect.height
220
+ };
221
+ }
222
+
223
+ // Apply any custom range configuration
224
+ const rangeConfig = axisParam.range;
225
+ if (!rangeConfig) {
226
+ return baseRange;
227
+ }
228
+ if (typeof rangeConfig === 'function') {
229
+ // Apply the transform function to the base range
230
+ return rangeConfig(baseRange);
231
+ } else {
232
+ var _rangeConfig$min, _rangeConfig$max;
233
+ // Merge explicit range values with calculated range
234
+ return {
235
+ min: (_rangeConfig$min = rangeConfig.min) !== null && _rangeConfig$min !== void 0 ? _rangeConfig$min : baseRange.min,
236
+ max: (_rangeConfig$max = rangeConfig.max) !== null && _rangeConfig$max !== void 0 ? _rangeConfig$max : baseRange.max
237
+ };
238
+ }
239
+ };
240
+
241
+ /**
242
+ * Options for tick generation behavior
243
+ */
244
+
245
+ /**
246
+ * Formats a tick value for display on an axis.
247
+ * Consolidates the identical formatting logic shared between XAxis and YAxis.
248
+ *
249
+ * @param value - The raw tick value to format
250
+ * @param tickFormatter - Optional custom formatter function
251
+ * @returns The formatted tick value as a React node
252
+ */
253
+ export const formatAxisTick = (value, tickFormatter) => {
254
+ if (tickFormatter) {
255
+ return tickFormatter(value);
256
+ }
257
+ return value;
258
+ };
259
+
260
+ /**
261
+ * Calculates a rounded step size for tick generation.
262
+ * Chooses from multiples of 1, 2, or 5 (scaled by powers of 10).
263
+ *
264
+ * @param roughStep - The approximate step size needed
265
+ * @param minStep - Optional minimum step size constraint
266
+ * @param maxStep - Optional maximum step size constraint
267
+ * @returns rounded step size within the specified constraints
268
+ */
269
+ const calculateNiceStep = (roughStep, minStep, maxStep) => {
270
+ if (roughStep <= 0) return minStep !== null && minStep !== void 0 ? minStep : 1;
271
+ const magnitude = Math.pow(10, Math.floor(Math.log10(roughStep)));
272
+ const residual = roughStep / magnitude;
273
+ let roundResidual;
274
+ if (residual <= 1) {
275
+ roundResidual = 1;
276
+ } else if (residual <= 2) {
277
+ roundResidual = 2;
278
+ } else if (residual <= 5) {
279
+ roundResidual = 5;
280
+ } else {
281
+ roundResidual = 10;
282
+ }
283
+ let niceStep = roundResidual * magnitude;
284
+ if (minStep !== undefined && niceStep < minStep) {
285
+ niceStep = minStep;
286
+ }
287
+ if (maxStep !== undefined && niceStep > maxStep) {
288
+ niceStep = maxStep;
289
+ }
290
+ return niceStep;
291
+ };
292
+
293
+ /**
294
+ * Generates evenly distributed tick values.
295
+ * Always includes first and last domain values, with intermediate ticks evenly distributed using nice step sizes.
296
+ * Selects from actual data points (possibleTickValues) or generates nice round numbers.
297
+ *
298
+ * @param scale - The numeric scale function
299
+ * @param tickInterval - Space between ticks (in pixels)
300
+ * @param possibleTickValues - Optional array of possible tick values to select from (e.g., data indices). If not provided, generates nice round numbers with guaranteed first/last inclusion.
301
+ * @param options - Options for tick generation behavior
302
+ * @returns Array of tick values, always including first and last domain values
303
+ */
304
+ const generateEvenlyDistributedTicks = (scale, tickInterval, possibleTickValues, options) => {
305
+ var _options$minTickCount;
306
+ const minTickCount = (_options$minTickCount = options === null || options === void 0 ? void 0 : options.minTickCount) !== null && _options$minTickCount !== void 0 ? _options$minTickCount : 4;
307
+ const [rangeMin, rangeMax] = scale.range();
308
+ const range = Math.abs(rangeMax - rangeMin);
309
+ const tickCountFromSpace = Math.floor(range / tickInterval);
310
+ const tickCount = Math.max(tickCountFromSpace, minTickCount);
311
+ if (tickCount < 1) {
312
+ return [];
313
+ }
314
+
315
+ // If we have possibleTickValues, select evenly from them
316
+ if (possibleTickValues && possibleTickValues.length > 0) {
317
+ // Limit tick count to available values
318
+ const finalTickCount = Math.min(tickCount, possibleTickValues.length);
319
+ const tickValues = [];
320
+ const step = (possibleTickValues.length - 1) / (finalTickCount - 1);
321
+ for (let i = 0; i < finalTickCount; i++) {
322
+ const index = i === finalTickCount - 1 ? possibleTickValues.length - 1 : Math.round(step * i);
323
+ tickValues.push(possibleTickValues[index]);
324
+ }
325
+ return tickValues;
326
+ }
327
+
328
+ // Generate nice round numbers that always include first and last domain values
329
+ const [domainMin, domainMax] = scale.domain();
330
+ if (tickCount === 1) {
331
+ return [domainMin];
332
+ }
333
+ if (tickCount === 2) {
334
+ return [domainMin, domainMax];
335
+ }
336
+
337
+ // Calculate a nice step size
338
+ const domainRange = domainMax - domainMin;
339
+ const roughStep = domainRange / (tickCount - 1);
340
+ const niceStep = calculateNiceStep(roughStep, options === null || options === void 0 ? void 0 : options.minStep, options === null || options === void 0 ? void 0 : options.maxStep);
341
+
342
+ // Generate ticks starting from domainMin and stepping by niceStep
343
+ const tickValues = [domainMin];
344
+
345
+ // Generate intermediate ticks using the nice step, starting from domainMin
346
+ let currentTick = domainMin + niceStep;
347
+ while (currentTick < domainMax) {
348
+ // Avoid floating point precision issues
349
+ const roundedTick = Number(currentTick.toFixed(10));
350
+ tickValues.push(roundedTick);
351
+ currentTick += niceStep;
352
+ }
353
+
354
+ // Only include domainMax if it naturally falls on a step (or very close due to floating point)
355
+ // or if the last tick is far enough away that including max provides useful context
356
+ const lastTick = tickValues[tickValues.length - 1];
357
+ const distanceToMax = domainMax - lastTick;
358
+
359
+ // Include max if:
360
+ // 1. It naturally falls on a step (within floating point tolerance)
361
+ // 2. Or the last tick is more than half a step away (provides meaningful context)
362
+ const tolerance = niceStep * 0.0001; // Floating point tolerance
363
+ const shouldIncludeMax = Math.abs(distanceToMax - niceStep) < tolerance ||
364
+ // Natural step
365
+ distanceToMax > niceStep * 0.5; // Far enough to provide context
366
+
367
+ if (shouldIncludeMax && domainMax !== lastTick) {
368
+ tickValues.push(domainMax);
369
+ }
370
+ return tickValues;
371
+ };
372
+
373
+ /**
374
+ * Processes tick configuration and returns tick data with positions.
375
+ *
376
+ * **Parameter Precedence by Scale Type:**
377
+ *
378
+ * **For Numeric Scales (linear/log):**
379
+ * 1. `ticks` (array) - Explicit tick values override all other options
380
+ * 2. `ticks` (function) - Filter function for tick selection
381
+ * 3. `ticks` (boolean) - Show/hide all possible ticks
382
+ * 4. `requestedTickCount` - D3 automatic tick generation (overrides tickInterval)
383
+ * 5. `tickInterval` - Pixel-based spacing (fallback)
384
+ *
385
+ * **For Categorical Scales (band):**
386
+ * 1. `ticks` (array) - Explicit category indices to display
387
+ * 2. `ticks` (function) - Filter function for category selection
388
+ * 3. `ticks` (boolean) - Show/hide all categories
389
+ * 4. Default - Show all categories (requestedTickCount and tickInterval are ignored)
390
+ *
391
+ * @param params - Tick processing parameters
392
+ * @param params.ticks - Custom tick configuration with multiple formats:
393
+ * - **Array**: For numeric scales: exact tick values; For band scales: category indices
394
+ * - **Function**: Predicate to filter tick values or category indices
395
+ * - **Boolean**: Show all (true) or no ticks (false) for both scale types
396
+ * @param params.scaleFunction - D3 scale function (numeric or band scale)
397
+ * @param params.requestedTickCount - Number of ticks for D3 generation (**numeric scales only**, overrides tickInterval)
398
+ * @param params.categories - Category labels (**band scales only**)
399
+ * @param params.possibleTickValues - Available tick values for filtering/selection (**numeric scales only**)
400
+ * @param params.tickInterval - Pixel spacing between ticks (**numeric scales only**, fallback option)
401
+ * @returns Array of tick data with values and positions
402
+ *
403
+ * @example
404
+ * // Basic usage with tickInterval for pixel-based spacing
405
+ * import { scaleLinear } from 'd3-scale';
406
+ *
407
+ * const numericScale = scaleLinear().domain([0, 10]).range([0, 400]);
408
+ * const result = getAxisTicksData({
409
+ * scaleFunction: numericScale,
410
+ * tickInterval: 80, // 80 pixels between ticks
411
+ * possibleTickValues: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
412
+ * });
413
+ * // Returns: [
414
+ * // { tick: 0, position: 0 }, // Always includes first
415
+ * // { tick: 2, position: 80 },
416
+ * // { tick: 5, position: 200 },
417
+ * // { tick: 7, position: 280 },
418
+ * // { tick: 10, position: 400 } // Always includes last
419
+ * // ]
420
+ *
421
+ * @example
422
+ * // Using requestedTickCount for D3-generated ticks
423
+ * const result = getAxisTicksData({
424
+ * scaleFunction: numericScale,
425
+ * requestedTickCount: 5
426
+ * });
427
+ * // Uses D3's tick generation algorithm
428
+ *
429
+ * @example
430
+ * // Using explicit tick values
431
+ * const result = getAxisTicksData({
432
+ * scaleFunction: numericScale,
433
+ * ticks: [0, 2.5, 5, 7.5, 10]
434
+ * });
435
+ * // Returns exact positions for specified values
436
+ *
437
+ * @example
438
+ * // Using tick filter function
439
+ * const result = getAxisTicksData({
440
+ * scaleFunction: numericScale,
441
+ * ticks: (value) => value % 2 === 0, // Only even numbers
442
+ * possibleTickValues: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
443
+ * });
444
+ * // Returns: [0, 2, 4, 6, 8, 10] with their positions
445
+ *
446
+ * @example
447
+ * // Band scale with categories (requestedTickCount and tickInterval are ignored)
448
+ * import { scaleBand } from 'd3-scale';
449
+ *
450
+ * const bandScale = scaleBand().domain([0, 1, 2, 3, 4]).range([0, 400]).padding(0.1);
451
+ * const result = getAxisTicksData({
452
+ * scaleFunction: bandScale,
453
+ * categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May'],
454
+ * ticks: [0, 2, 4], // Show only Jan (index 0), Mar (index 2), May (index 4)
455
+ * requestedTickCount: 10, // IGNORED for band scales
456
+ * tickInterval: 50 // IGNORED for band scales
457
+ * });
458
+ * // Returns tick positions centered in each selected band
459
+ */
460
+ export const getAxisTicksData = _ref4 => {
461
+ let {
462
+ ticks,
463
+ scaleFunction,
464
+ requestedTickCount,
465
+ categories = [],
466
+ possibleTickValues,
467
+ tickInterval,
468
+ options
469
+ } = _ref4;
470
+ // Handle band scales
471
+ if (isCategoricalScale(scaleFunction)) {
472
+ // If explicit ticks are provided as array, use them
473
+ if (Array.isArray(ticks)) {
474
+ return ticks.filter(index => index >= 0 && index < categories.length).map(index => {
475
+ var _bandwidth, _bandwidth2, _ref5;
476
+ // Band scales expect numeric indices, not category strings
477
+ const position = scaleFunction(index);
478
+ if (position === undefined) return null;
479
+ return {
480
+ tick: index,
481
+ position: position + ((_bandwidth = (_bandwidth2 = (_ref5 = scaleFunction).bandwidth) === null || _bandwidth2 === void 0 ? void 0 : _bandwidth2.call(_ref5)) !== null && _bandwidth !== void 0 ? _bandwidth : 0) / 2
482
+ };
483
+ }).filter(Boolean);
484
+ }
485
+
486
+ // If a tick function is provided, use it to filter
487
+ if (typeof ticks === 'function') {
488
+ return categories.map((category, index) => {
489
+ var _bandwidth3, _bandwidth4, _ref6;
490
+ if (!ticks(index)) return null;
491
+
492
+ // Band scales expect numeric indices, not category strings
493
+ const position = scaleFunction(index);
494
+ if (position === undefined) return null;
495
+ return {
496
+ tick: index,
497
+ position: position + ((_bandwidth3 = (_bandwidth4 = (_ref6 = scaleFunction).bandwidth) === null || _bandwidth4 === void 0 ? void 0 : _bandwidth4.call(_ref6)) !== null && _bandwidth3 !== void 0 ? _bandwidth3 : 0) / 2
498
+ };
499
+ }).filter(Boolean);
500
+ }
501
+ if (typeof ticks === 'boolean' && !ticks) {
502
+ return [];
503
+ }
504
+
505
+ // For band scales without explicit ticks, show all categories
506
+ // requestedTickCount is ignored for categorical scales - use ticks parameter to control visibility
507
+ return categories.map((category, index) => {
508
+ var _bandwidth5, _bandwidth6, _ref7;
509
+ // Band scales expect numeric indices, not category strings
510
+ const position = scaleFunction(index);
511
+ if (position === undefined) return null;
512
+ return {
513
+ tick: index,
514
+ position: position + ((_bandwidth5 = (_bandwidth6 = (_ref7 = scaleFunction).bandwidth) === null || _bandwidth6 === void 0 ? void 0 : _bandwidth6.call(_ref7)) !== null && _bandwidth5 !== void 0 ? _bandwidth5 : 0) / 2
515
+ };
516
+ }).filter(Boolean);
517
+ }
518
+
519
+ // Handle numeric scales
520
+ if (!isNumericScale(scaleFunction)) {
521
+ console.warn('Scale does not support automatic tick generation');
522
+ return [];
523
+ }
524
+ const numericScale = scaleFunction;
525
+ let tickValues = [];
526
+ if (Array.isArray(ticks)) {
527
+ // Use exact tick values provided
528
+ tickValues = ticks;
529
+ } else if (typeof ticks === 'function') {
530
+ // Filter the possible tick values using the predicate function
531
+ if (possibleTickValues) {
532
+ tickValues = possibleTickValues.filter(ticks);
533
+ } else {
534
+ // Fallback to scale-generated ticks if no possible tick values provided
535
+ const generatedTicks = numericScale.ticks(requestedTickCount);
536
+ tickValues = generatedTicks.filter(ticks);
537
+ }
538
+ } else if (requestedTickCount !== undefined) {
539
+ // Use scale-generated ticks
540
+ tickValues = numericScale.ticks(requestedTickCount);
541
+ } else if (tickInterval !== undefined) {
542
+ tickValues = generateEvenlyDistributedTicks(numericScale, tickInterval, possibleTickValues, options);
543
+ }
544
+
545
+ // Map values to positions using the scale function
546
+ return tickValues.map(tick => ({
547
+ tick,
548
+ position: numericScale(tick)
549
+ }));
550
+ };
551
+ /**
552
+ * Calculates the total amount of padding needed to render a set of axes on the main drawing area of the chart.
553
+ * Returns the registed axes, an API for adding/removing axes as well as the total calculated padding that must be reserved in the drawing area.
554
+ */
555
+ export const useTotalAxisPadding = () => {
556
+ const [renderedAxes, setRenderedAxes] = useState(new Map());
557
+ const registerAxis = useCallback((id, position, size) => {
558
+ setRenderedAxes(prev => {
559
+ const newMap = new Map(prev);
560
+ newMap.set(id, {
561
+ id,
562
+ position,
563
+ size
564
+ });
565
+ return newMap;
566
+ });
567
+ }, []);
568
+ const unregisterAxis = useCallback(id => {
569
+ setRenderedAxes(prev => {
570
+ const newMap = new Map(prev);
571
+ newMap.delete(id);
572
+ return newMap;
573
+ });
574
+ }, []);
575
+ const axisPadding = useMemo(() => {
576
+ const padding = {
577
+ top: 0,
578
+ right: 0,
579
+ bottom: 0,
580
+ left: 0
581
+ };
582
+ renderedAxes.forEach(axis => {
583
+ padding[axis.position] += axis.size;
584
+ });
585
+ return padding;
586
+ }, [renderedAxes]);
587
+ return {
588
+ renderedAxes,
589
+ axisPadding,
590
+ registerAxis,
591
+ unregisterAxis
592
+ };
593
+ };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Calculates the size adjustment needed for bars when accounting for gaps between them.
3
+ * This function helps determine how much to reduce each bar's width to accommodate
4
+ * the specified gap size between multiple bars in a group.
5
+ *
6
+ * @param barCount - The number of bars in the group
7
+ * @param gapSize - The desired gap size between bars
8
+ * @returns The amount to reduce each bar's size by, or 0 if there's only one bar
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * // For 3 bars with 12px gaps, each bar should be reduced by 8px
13
+ * const adjustment = getBarSizeAdjustment(3, 12);
14
+ *
15
+ * // Single bar needs no adjustment
16
+ * const singleBarAdjustment = getBarSizeAdjustment(1, 10);
17
+ * ```
18
+ */
19
+ export function getBarSizeAdjustment(barCount, gapSize) {
20
+ if (barCount <= 1) {
21
+ return 0;
22
+ }
23
+ return gapSize * (barCount - 1) / barCount;
24
+ }