@coinbase/cds-web-visualization 3.4.0-beta.5 → 3.4.0-beta.6

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 (167) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dts/chart/CartesianChart.d.ts +38 -2
  3. package/dts/chart/CartesianChart.d.ts.map +1 -1
  4. package/dts/chart/Path.d.ts +27 -7
  5. package/dts/chart/Path.d.ts.map +1 -1
  6. package/dts/chart/PeriodSelector.d.ts +0 -4
  7. package/dts/chart/PeriodSelector.d.ts.map +1 -1
  8. package/dts/chart/area/Area.d.ts +54 -24
  9. package/dts/chart/area/Area.d.ts.map +1 -1
  10. package/dts/chart/area/AreaChart.d.ts +33 -6
  11. package/dts/chart/area/AreaChart.d.ts.map +1 -1
  12. package/dts/chart/area/DottedArea.d.ts +21 -44
  13. package/dts/chart/area/DottedArea.d.ts.map +1 -1
  14. package/dts/chart/area/GradientArea.d.ts +21 -12
  15. package/dts/chart/area/GradientArea.d.ts.map +1 -1
  16. package/dts/chart/area/SolidArea.d.ts +16 -1
  17. package/dts/chart/area/SolidArea.d.ts.map +1 -1
  18. package/dts/chart/axis/Axis.d.ts +89 -43
  19. package/dts/chart/axis/Axis.d.ts.map +1 -1
  20. package/dts/chart/axis/DefaultAxisTickLabel.d.ts +8 -0
  21. package/dts/chart/axis/DefaultAxisTickLabel.d.ts.map +1 -0
  22. package/dts/chart/axis/XAxis.d.ts +1 -1
  23. package/dts/chart/axis/XAxis.d.ts.map +1 -1
  24. package/dts/chart/axis/YAxis.d.ts +1 -1
  25. package/dts/chart/axis/YAxis.d.ts.map +1 -1
  26. package/dts/chart/axis/index.d.ts +1 -0
  27. package/dts/chart/axis/index.d.ts.map +1 -1
  28. package/dts/chart/bar/Bar.d.ts +16 -13
  29. package/dts/chart/bar/Bar.d.ts.map +1 -1
  30. package/dts/chart/bar/BarChart.d.ts +17 -8
  31. package/dts/chart/bar/BarChart.d.ts.map +1 -1
  32. package/dts/chart/bar/BarPlot.d.ts +2 -1
  33. package/dts/chart/bar/BarPlot.d.ts.map +1 -1
  34. package/dts/chart/bar/BarStack.d.ts +40 -48
  35. package/dts/chart/bar/BarStack.d.ts.map +1 -1
  36. package/dts/chart/bar/BarStackGroup.d.ts +1 -0
  37. package/dts/chart/bar/BarStackGroup.d.ts.map +1 -1
  38. package/dts/chart/bar/DefaultBar.d.ts.map +1 -1
  39. package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
  40. package/dts/chart/gradient/Gradient.d.ts +35 -0
  41. package/dts/chart/gradient/Gradient.d.ts.map +1 -0
  42. package/dts/chart/gradient/index.d.ts +2 -0
  43. package/dts/chart/gradient/index.d.ts.map +1 -0
  44. package/dts/chart/index.d.ts +2 -1
  45. package/dts/chart/index.d.ts.map +1 -1
  46. package/dts/chart/line/DefaultReferenceLineLabel.d.ts +9 -0
  47. package/dts/chart/line/DefaultReferenceLineLabel.d.ts.map +1 -0
  48. package/dts/chart/line/DottedLine.d.ts +15 -3
  49. package/dts/chart/line/DottedLine.d.ts.map +1 -1
  50. package/dts/chart/line/Line.d.ts +70 -28
  51. package/dts/chart/line/Line.d.ts.map +1 -1
  52. package/dts/chart/line/LineChart.d.ts +26 -8
  53. package/dts/chart/line/LineChart.d.ts.map +1 -1
  54. package/dts/chart/line/ReferenceLine.d.ts +85 -44
  55. package/dts/chart/line/ReferenceLine.d.ts.map +1 -1
  56. package/dts/chart/line/SolidLine.d.ts +14 -3
  57. package/dts/chart/line/SolidLine.d.ts.map +1 -1
  58. package/dts/chart/line/index.d.ts +1 -1
  59. package/dts/chart/line/index.d.ts.map +1 -1
  60. package/dts/chart/point/DefaultPointLabel.d.ts +10 -0
  61. package/dts/chart/point/DefaultPointLabel.d.ts.map +1 -0
  62. package/dts/chart/point/Point.d.ts +201 -0
  63. package/dts/chart/point/Point.d.ts.map +1 -0
  64. package/dts/chart/point/index.d.ts +3 -0
  65. package/dts/chart/point/index.d.ts.map +1 -0
  66. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts +24 -0
  67. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -0
  68. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts +12 -0
  69. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts.map +1 -0
  70. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +10 -0
  71. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -0
  72. package/dts/chart/scrubber/Scrubber.d.ts +203 -64
  73. package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
  74. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts +70 -0
  75. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -0
  76. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts +32 -0
  77. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts.map +1 -0
  78. package/dts/chart/scrubber/index.d.ts +3 -0
  79. package/dts/chart/scrubber/index.d.ts.map +1 -1
  80. package/dts/chart/text/ChartText.d.ts +46 -43
  81. package/dts/chart/text/ChartText.d.ts.map +1 -1
  82. package/dts/chart/text/{SmartChartTextGroup.d.ts → ChartTextGroup.d.ts} +9 -3
  83. package/dts/chart/text/ChartTextGroup.d.ts.map +1 -0
  84. package/dts/chart/text/index.d.ts +1 -1
  85. package/dts/chart/text/index.d.ts.map +1 -1
  86. package/dts/chart/utils/chart.d.ts +27 -7
  87. package/dts/chart/utils/chart.d.ts.map +1 -1
  88. package/dts/chart/utils/context.d.ts +6 -0
  89. package/dts/chart/utils/context.d.ts.map +1 -1
  90. package/dts/chart/utils/gradient.d.ts +104 -0
  91. package/dts/chart/utils/gradient.d.ts.map +1 -0
  92. package/dts/chart/utils/index.d.ts +4 -0
  93. package/dts/chart/utils/index.d.ts.map +1 -1
  94. package/dts/chart/utils/interpolate.d.ts +112 -0
  95. package/dts/chart/utils/interpolate.d.ts.map +1 -0
  96. package/dts/chart/utils/path.d.ts +24 -1
  97. package/dts/chart/utils/path.d.ts.map +1 -1
  98. package/dts/chart/utils/point.d.ts +29 -0
  99. package/dts/chart/utils/point.d.ts.map +1 -1
  100. package/dts/chart/utils/scrubber.d.ts +39 -0
  101. package/dts/chart/utils/scrubber.d.ts.map +1 -0
  102. package/dts/chart/utils/transition.d.ts +65 -0
  103. package/dts/chart/utils/transition.d.ts.map +1 -0
  104. package/esm/chart/CartesianChart.js +140 -85
  105. package/esm/chart/Path.js +51 -46
  106. package/esm/chart/PeriodSelector.js +4 -18
  107. package/esm/chart/area/Area.js +24 -34
  108. package/esm/chart/area/AreaChart.js +24 -15
  109. package/esm/chart/area/DottedArea.js +35 -89
  110. package/esm/chart/area/GradientArea.js +34 -80
  111. package/esm/chart/area/SolidArea.js +29 -11
  112. package/esm/chart/axis/Axis.js +4 -25
  113. package/esm/chart/axis/DefaultAxisTickLabel.js +15 -0
  114. package/esm/chart/axis/XAxis.js +53 -36
  115. package/esm/chart/axis/YAxis.js +55 -32
  116. package/esm/chart/axis/index.js +1 -0
  117. package/esm/chart/bar/Bar.js +3 -1
  118. package/esm/chart/bar/BarChart.js +15 -32
  119. package/esm/chart/bar/BarPlot.js +3 -2
  120. package/esm/chart/bar/BarStack.js +65 -23
  121. package/esm/chart/bar/BarStackGroup.js +7 -17
  122. package/esm/chart/bar/DefaultBar.js +4 -7
  123. package/esm/chart/bar/DefaultBarStack.js +5 -7
  124. package/esm/chart/gradient/Gradient.js +104 -0
  125. package/esm/chart/gradient/index.js +1 -0
  126. package/esm/chart/index.js +2 -1
  127. package/esm/chart/line/DefaultReferenceLineLabel.js +81 -0
  128. package/esm/chart/line/DottedLine.js +38 -17
  129. package/esm/chart/line/Line.js +96 -70
  130. package/esm/chart/line/LineChart.js +18 -6
  131. package/esm/chart/line/ReferenceLine.js +34 -41
  132. package/esm/chart/line/SolidLine.js +36 -15
  133. package/esm/chart/line/index.js +1 -1
  134. package/esm/chart/{line/GradientLine.js → point/DefaultPointLabel.js} +31 -45
  135. package/esm/chart/point/Point.css +2 -0
  136. package/esm/chart/{Point.js → point/Point.js} +66 -57
  137. package/esm/chart/point/index.js +2 -0
  138. package/esm/chart/scrubber/DefaultScrubberBeacon.js +155 -0
  139. package/esm/chart/scrubber/{ScrubberBeaconLabel.js → DefaultScrubberBeaconLabel.js} +23 -10
  140. package/esm/chart/scrubber/DefaultScrubberLabel.js +30 -0
  141. package/esm/chart/scrubber/Scrubber.js +98 -392
  142. package/esm/chart/scrubber/ScrubberBeaconGroup.js +166 -0
  143. package/esm/chart/scrubber/ScrubberBeaconLabelGroup.js +186 -0
  144. package/esm/chart/scrubber/index.js +3 -1
  145. package/esm/chart/text/ChartText.js +13 -19
  146. package/esm/chart/text/{SmartChartTextGroup.js → ChartTextGroup.js} +4 -3
  147. package/esm/chart/text/index.js +1 -1
  148. package/esm/chart/utils/chart.js +29 -3
  149. package/esm/chart/utils/gradient.js +257 -0
  150. package/esm/chart/utils/index.js +4 -0
  151. package/esm/chart/utils/interpolate.js +644 -0
  152. package/esm/chart/utils/path.js +32 -9
  153. package/esm/chart/utils/point.js +69 -0
  154. package/esm/chart/utils/scrubber.js +132 -0
  155. package/esm/chart/utils/transition.js +111 -0
  156. package/package.json +5 -5
  157. package/dts/chart/Point.d.ts +0 -153
  158. package/dts/chart/Point.d.ts.map +0 -1
  159. package/dts/chart/line/GradientLine.d.ts +0 -42
  160. package/dts/chart/line/GradientLine.d.ts.map +0 -1
  161. package/dts/chart/scrubber/ScrubberBeacon.d.ts +0 -93
  162. package/dts/chart/scrubber/ScrubberBeacon.d.ts.map +0 -1
  163. package/dts/chart/scrubber/ScrubberBeaconLabel.d.ts +0 -7
  164. package/dts/chart/scrubber/ScrubberBeaconLabel.d.ts.map +0 -1
  165. package/dts/chart/text/SmartChartTextGroup.d.ts.map +0 -1
  166. package/esm/chart/Point.css +0 -2
  167. package/esm/chart/scrubber/ScrubberBeacon.js +0 -195
@@ -40,17 +40,18 @@ export const getPathCurveFunction = function () {
40
40
  * @example
41
41
  * ```typescript
42
42
  * const chartScale = getChartScale({ chartRect, domain, range, xScale, yScale });
43
- * const path = getLinePath({ data: [1, 2, 3], chartScale, curve: 'linear' });
43
+ * const path = getLinePath({ data: [1, 2, 3], chartScale, curve: 'bump' });
44
44
  * ```
45
45
  */
46
46
  export const getLinePath = _ref => {
47
47
  var _pathGenerator;
48
48
  let {
49
49
  data,
50
- curve = 'linear',
50
+ curve = 'bump',
51
51
  xScale,
52
52
  yScale,
53
- xData
53
+ xData,
54
+ connectNulls
54
55
  } = _ref;
55
56
  if (data.length === 0) {
56
57
  return '';
@@ -62,9 +63,12 @@ export const getLinePath = _ref => {
62
63
  yScale,
63
64
  xData
64
65
  });
65
- const pathGenerator = d3Line().x(d => d.x).y(d => d.y).curve(curveFunction).defined(d => d !== null); // Only draw lines where point is not null
66
66
 
67
- return (_pathGenerator = pathGenerator(dataPoints)) !== null && _pathGenerator !== void 0 ? _pathGenerator : '';
67
+ // When connectNulls is true, filter out null values before rendering
68
+ // When false, use defined() to create gaps in the line
69
+ const filteredPoints = connectNulls ? dataPoints.filter(d => d !== null) : dataPoints;
70
+ const pathGenerator = d3Line().x(d => d.x).y(d => d.y).curve(curveFunction).defined(d => connectNulls || d !== null);
71
+ return (_pathGenerator = pathGenerator(filteredPoints)) !== null && _pathGenerator !== void 0 ? _pathGenerator : '';
68
72
  };
69
73
 
70
74
  /**
@@ -92,10 +96,11 @@ export const getLinePath = _ref => {
92
96
  export const getAreaPath = _ref2 => {
93
97
  let {
94
98
  data,
95
- curve = 'linear',
99
+ curve = 'bump',
96
100
  xScale,
97
101
  yScale,
98
- xData
102
+ xData,
103
+ connectNulls
99
104
  } = _ref2;
100
105
  if (data.length === 0) {
101
106
  return '';
@@ -156,6 +161,10 @@ export const getAreaPath = _ref2 => {
156
161
  isValid: true
157
162
  };
158
163
  });
164
+
165
+ // When connectNulls is true, filter out invalid points before rendering
166
+ // When false, use defined() to create gaps in the area
167
+ const filteredPoints = connectNulls ? dataPoints.filter(d => d.isValid) : dataPoints;
159
168
  const areaGenerator = d3Area().x(d => d.x).y0(d => {
160
169
  var _d$low;
161
170
  return (_d$low = d.low) !== null && _d$low !== void 0 ? _d$low : 0;
@@ -164,12 +173,26 @@ export const getAreaPath = _ref2 => {
164
173
  var _d$high;
165
174
  return (_d$high = d.high) !== null && _d$high !== void 0 ? _d$high : 0;
166
175
  }) // Top boundary (high values), fallback to 0
167
- .curve(curveFunction).defined(d => d.isValid && d.low != null && d.high != null); // Only draw where both values exist
176
+ .curve(curveFunction).defined(d => connectNulls || d.isValid && d.low != null && d.high != null); // Only draw where both values exist
168
177
 
169
- const result = areaGenerator(dataPoints);
178
+ const result = areaGenerator(filteredPoints);
170
179
  return result !== null && result !== void 0 ? result : '';
171
180
  };
172
181
 
182
+ /**
183
+ * Converts line coordinates to an SVG path string.
184
+ * Useful for rendering axis lines and tick marks.
185
+ *
186
+ * @example
187
+ * ```typescript
188
+ * const path = lineToPath(0, 0, 100, 100);
189
+ * // Returns: "M 0 0 L 100 100"
190
+ * ```
191
+ */
192
+ export const lineToPath = (x1, y1, x2, y2) => {
193
+ return "M".concat(x1, ",").concat(y1, " L").concat(x2, ",").concat(y2);
194
+ };
195
+
173
196
  /**
174
197
  * Creates an SVG path string for a rectangle with selective corner rounding.
175
198
  * Useful for creating bars in charts with optional rounded corners.
@@ -1,5 +1,13 @@
1
1
  import { isCategoricalScale, isLogScale, isNumericScale } from './scale';
2
2
 
3
+ /**
4
+ * Position a label should be placed relative to the point
5
+ *
6
+ * @example
7
+ * 'top' would have the label be located above the point itself,
8
+ * and thus the vertical alignment of that text would be bottom.
9
+ */
10
+
3
11
  /**
4
12
  * Get a point from a data value and a scale.
5
13
  * @note for categorical scales, the point will be centered within the band.
@@ -115,4 +123,65 @@ export const projectPoints = _ref2 => {
115
123
  yScale
116
124
  });
117
125
  });
126
+ };
127
+
128
+ /**
129
+ * Determines text alignment based on label position.
130
+ * For example, a 'top' position needs the text aligned to the 'bottom' so it appears above the point.
131
+ */
132
+ export const getAlignmentFromPosition = position => {
133
+ let horizontalAlignment = 'center';
134
+ let verticalAlignment = 'middle';
135
+ switch (position) {
136
+ case 'top':
137
+ verticalAlignment = 'bottom';
138
+ break;
139
+ case 'bottom':
140
+ verticalAlignment = 'top';
141
+ break;
142
+ case 'left':
143
+ horizontalAlignment = 'right';
144
+ break;
145
+ case 'right':
146
+ horizontalAlignment = 'left';
147
+ break;
148
+ case 'center':
149
+ default:
150
+ horizontalAlignment = 'center';
151
+ verticalAlignment = 'middle';
152
+ break;
153
+ }
154
+ return {
155
+ horizontalAlignment,
156
+ verticalAlignment
157
+ };
158
+ };
159
+
160
+ /**
161
+ * Calculates the final label coordinates by applying offset based on position.
162
+ */
163
+ export const getLabelCoordinates = (x, y, position, offset) => {
164
+ let dx = 0;
165
+ let dy = 0;
166
+ switch (position) {
167
+ case 'top':
168
+ dy = -offset;
169
+ break;
170
+ case 'bottom':
171
+ dy = offset;
172
+ break;
173
+ case 'left':
174
+ dx = -offset;
175
+ break;
176
+ case 'right':
177
+ dx = offset;
178
+ break;
179
+ case 'center':
180
+ default:
181
+ break;
182
+ }
183
+ return {
184
+ x: x + dx,
185
+ y: y + dy
186
+ };
118
187
  };
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Determines which side (left/right) to place scrubber labels based on available space.
3
+ * Prefers right side, switches to left when labels would overflow.
4
+ */
5
+ export const getLabelPosition = function (beaconX, maxLabelWidth, drawingArea) {
6
+ let xOffset = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 16;
7
+ if (drawingArea.width <= 0 || drawingArea.height <= 0) {
8
+ return 'right';
9
+ }
10
+ const availableRightSpace = drawingArea.x + drawingArea.width - beaconX;
11
+ const requiredSpace = maxLabelWidth + xOffset;
12
+ return requiredSpace <= availableRightSpace ? 'right' : 'left';
13
+ };
14
+ /**
15
+ * Calculates Y positions for all labels avoiding overlaps while maintaining order.
16
+ */
17
+ export const calculateLabelYPositions = (dimensions, drawingArea, labelHeight, minGap) => {
18
+ if (dimensions.length === 0) {
19
+ return new Map();
20
+ }
21
+
22
+ // Sort by preferred Y values and create working labels
23
+ const sortedLabels = [...dimensions].sort((a, b) => a.preferredY - b.preferredY).map(dim => ({
24
+ seriesId: dim.seriesId,
25
+ preferredY: dim.preferredY,
26
+ finalY: dim.preferredY
27
+ }));
28
+
29
+ // Initial bounds fitting
30
+ const minY = drawingArea.y + labelHeight / 2;
31
+ const maxY = drawingArea.y + drawingArea.height - labelHeight / 2;
32
+ const requiredDistance = labelHeight + minGap;
33
+ for (const label of sortedLabels) {
34
+ // Clamp each label to the drawing area
35
+ label.finalY = Math.max(minY, Math.min(maxY, label.preferredY));
36
+ }
37
+
38
+ // First pass: push down any overlapping labels
39
+ for (let i = 1; i < sortedLabels.length; i++) {
40
+ const prev = sortedLabels[i - 1];
41
+ const current = sortedLabels[i];
42
+ const minAllowedY = prev.finalY + requiredDistance;
43
+ if (current.finalY < minAllowedY) {
44
+ current.finalY = minAllowedY;
45
+ }
46
+ }
47
+
48
+ // Find collision groups - groups of labels that are tightly packed (gap < minGap between them)
49
+ const collisionGroups = [];
50
+ let currentGroup = [sortedLabels[0]];
51
+ for (let i = 1; i < sortedLabels.length; i++) {
52
+ const prev = sortedLabels[i - 1];
53
+ const current = sortedLabels[i];
54
+ const gap = current.finalY - prev.finalY - labelHeight;
55
+ if (gap < minGap + 0.01) {
56
+ // Labels are touching or very close - part of same collision group
57
+ currentGroup.push(current);
58
+ } else {
59
+ // Gap is large enough - start new group
60
+ collisionGroups.push(currentGroup);
61
+ currentGroup = [current];
62
+ }
63
+ }
64
+ collisionGroups.push(currentGroup);
65
+
66
+ // Process each collision group - optimize positioning to minimize displacement
67
+ for (const group of collisionGroups) {
68
+ if (group.length === 1) {
69
+ // Single label, already at best position
70
+ continue;
71
+ }
72
+ const groupLastLabel = group[group.length - 1];
73
+ const groupFirstLabel = group[0];
74
+ const groupOverflow = groupLastLabel.finalY + labelHeight / 2 - (drawingArea.y + drawingArea.height);
75
+
76
+ // Calculate the ideal center point for this group
77
+ const groupPreferredCenter = group.reduce((sum, label) => sum + label.preferredY, 0) / group.length;
78
+ const groupTotalNeeded = group.length * labelHeight + (group.length - 1) * minGap;
79
+ if (groupOverflow <= 0) {
80
+ // Group fits, but let's center it better if possible
81
+ // Calculate how much we can shift up/down to center around preferred positions
82
+ const currentCenter = (groupFirstLabel.finalY + groupLastLabel.finalY) / 2;
83
+ const desiredShift = groupPreferredCenter - currentCenter;
84
+
85
+ // Calculate max shift in each direction
86
+ const maxShiftUp = groupFirstLabel.finalY - minY;
87
+ const maxShiftDown = maxY - groupLastLabel.finalY;
88
+
89
+ // Apply the shift, constrained by boundaries
90
+ const actualShift = Math.max(-maxShiftUp, Math.min(maxShiftDown, desiredShift));
91
+ if (Math.abs(actualShift) > 0.01) {
92
+ for (const label of group) {
93
+ label.finalY += actualShift;
94
+ }
95
+ }
96
+ } else {
97
+ // Group overflows - need to adjust
98
+ const groupStartY = groupFirstLabel.finalY - labelHeight / 2;
99
+ const availableSpace = drawingArea.y + drawingArea.height - groupStartY;
100
+ const maxShiftUp = groupFirstLabel.finalY - minY;
101
+ if (maxShiftUp >= groupOverflow) {
102
+ // Can shift entire group up to fit
103
+ for (const label of group) {
104
+ label.finalY -= groupOverflow;
105
+ }
106
+ } else if (groupTotalNeeded <= availableSpace) {
107
+ // Can't shift enough, but there's room - redistribute with proper spacing
108
+ let currentY = Math.max(minY, groupFirstLabel.finalY - maxShiftUp);
109
+ const gap = (availableSpace - group.length * labelHeight) / Math.max(1, group.length - 1);
110
+ for (const label of group) {
111
+ label.finalY = currentY;
112
+ currentY += labelHeight + gap;
113
+ }
114
+ } else {
115
+ // Not enough space even with compression - compress gaps and fit to bottom
116
+ const compressedGap = Math.max(1, (availableSpace - group.length * labelHeight) / Math.max(1, group.length - 1));
117
+ // Position so last label is at maxY
118
+ let currentY = maxY - (group.length - 1) * (labelHeight + compressedGap);
119
+ currentY = Math.max(minY, currentY);
120
+ for (const label of group) {
121
+ label.finalY = currentY;
122
+ currentY += labelHeight + compressedGap;
123
+ }
124
+ }
125
+ }
126
+ }
127
+ const result = new Map();
128
+ for (const label of sortedLabels) {
129
+ result.set(label.seriesId, label.finalY);
130
+ }
131
+ return result;
132
+ };
@@ -0,0 +1,111 @@
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 { useEffect, useRef } from 'react';
7
+ import { interpolatePath } from 'd3-interpolate-path';
8
+ import { animate, useMotionValue, useTransform } from 'framer-motion';
9
+
10
+ /**
11
+ * Default transition configuration used across all chart components.
12
+ */
13
+ export const defaultTransition = {
14
+ type: 'spring',
15
+ stiffness: 900,
16
+ damping: 120,
17
+ mass: 4
18
+ };
19
+
20
+ /**
21
+ * Duration in seconds for accessory elements to fade in.
22
+ */
23
+ export const accessoryFadeTransitionDuration = 0.15;
24
+
25
+ /**
26
+ * Delay in seconds before accessory elements fade in.
27
+ */
28
+ export const accessoryFadeTransitionDelay = 0.35;
29
+
30
+ /**
31
+ * Hook for path animation state and transitions.
32
+ *
33
+ * @param currentPath - Current target path to animate to
34
+ * @param initialPath - Initial path for enter animation. When provided, the first animation will go from initialPath to currentPath.
35
+ * @param transition - Transition configuration
36
+ * @returns MotionValue containing the current interpolated path string
37
+ *
38
+ * @example
39
+ * // Simple path transition
40
+ * const animatedPath = usePathTransition({
41
+ * currentPath: d ?? '',
42
+ * transition: {
43
+ * type: 'spring',
44
+ * stiffness: 300,
45
+ * damping: 20
46
+ * }
47
+ * });
48
+ *
49
+ * @example
50
+ * // Time based animation
51
+ * const animatedPath = usePathTransition({
52
+ * currentPath: targetPath,
53
+ * initialPath: baselinePath,
54
+ * transition: {
55
+ * type: 'tween',
56
+ * duration: 0.3,
57
+ * ease: 'easeInOut'
58
+ * }
59
+ * });
60
+ */
61
+ export const usePathTransition = _ref => {
62
+ let {
63
+ currentPath,
64
+ initialPath,
65
+ transition = defaultTransition
66
+ } = _ref;
67
+ const isInitialRender = useRef(true);
68
+ const previousPathRef = useRef(initialPath !== null && initialPath !== void 0 ? initialPath : currentPath);
69
+ const targetPathRef = useRef(currentPath);
70
+ const animationRef = useRef(null);
71
+ const progress = useMotionValue(0);
72
+
73
+ // Derive the interpolated path from progress using useTransform
74
+ const interpolatedPath = useTransform(progress, latest => {
75
+ const pathInterpolator = interpolatePath(previousPathRef.current, targetPathRef.current);
76
+ return pathInterpolator(latest);
77
+ });
78
+ useEffect(() => {
79
+ // Only proceed if the target path has actually changed
80
+ if (targetPathRef.current !== currentPath) {
81
+ // Cancel any ongoing animation before starting a new one
82
+ const wasAnimating = !!animationRef.current;
83
+ if (animationRef.current) {
84
+ animationRef.current.cancel();
85
+ animationRef.current = null;
86
+ }
87
+ const currentInterpolatedPath = interpolatedPath.get();
88
+
89
+ // If we were animating and the interpolated path is different from both start and end,
90
+ // use it as the starting point for the next animation (smooth interruption)
91
+ const isInterpolatedPosition = currentInterpolatedPath !== previousPathRef.current && currentInterpolatedPath !== currentPath;
92
+ if (wasAnimating && isInterpolatedPosition) {
93
+ previousPathRef.current = currentInterpolatedPath;
94
+ }
95
+ targetPathRef.current = currentPath;
96
+ progress.set(0);
97
+ animationRef.current = animate(progress, 1, _objectSpread(_objectSpread({}, transition), {}, {
98
+ onComplete: () => {
99
+ previousPathRef.current = currentPath;
100
+ }
101
+ }));
102
+ isInitialRender.current = false;
103
+ }
104
+ return () => {
105
+ if (animationRef.current) {
106
+ animationRef.current.cancel();
107
+ }
108
+ };
109
+ }, [currentPath, transition, progress, interpolatedPath]);
110
+ return interpolatedPath;
111
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coinbase/cds-web-visualization",
3
- "version": "3.4.0-beta.5",
3
+ "version": "3.4.0-beta.6",
4
4
  "description": "Coinbase Design System - Web Sparkline",
5
5
  "repository": {
6
6
  "type": "git",
@@ -38,10 +38,10 @@
38
38
  "CHANGELOG"
39
39
  ],
40
40
  "peerDependencies": {
41
- "@coinbase/cds-common": "^8.19.1",
41
+ "@coinbase/cds-common": "^8.21.8",
42
42
  "@coinbase/cds-lottie-files": "^3.3.3",
43
43
  "@coinbase/cds-utils": "^2.3.4",
44
- "@coinbase/cds-web": "^8.19.1",
44
+ "@coinbase/cds-web": "^8.21.8",
45
45
  "react": "^18.3.1",
46
46
  "react-dom": "^18.3.1"
47
47
  },
@@ -58,10 +58,10 @@
58
58
  "@babel/preset-env": "^7.28.0",
59
59
  "@babel/preset-react": "^7.27.1",
60
60
  "@babel/preset-typescript": "^7.27.1",
61
- "@coinbase/cds-common": "^8.19.1",
61
+ "@coinbase/cds-common": "^8.21.8",
62
62
  "@coinbase/cds-lottie-files": "^3.3.3",
63
63
  "@coinbase/cds-utils": "^2.3.4",
64
- "@coinbase/cds-web": "^8.19.1",
64
+ "@coinbase/cds-web": "^8.21.8",
65
65
  "@figma/code-connect": "^1.3.4",
66
66
  "@linaria/core": "^3.0.0-beta.22",
67
67
  "@types/react": "^18.3.12",
@@ -1,153 +0,0 @@
1
- import React from 'react';
2
- import type { SVGProps } from 'react';
3
- import type { SharedProps } from '@coinbase/cds-common/types';
4
- import type { ChartTextChildren } from './text/ChartText';
5
- import { type ChartTextProps } from './text';
6
- /**
7
- * Parameters passed to renderPoints callback function.
8
- */
9
- export type RenderPointsParams = {
10
- /**
11
- * X coordinate in SVG pixel space.
12
- */
13
- x: number;
14
- /**
15
- * Y coordinate in SVG pixel space.
16
- */
17
- y: number;
18
- /**
19
- * X coordinate in data space (usually same as index).
20
- */
21
- dataX: number;
22
- /**
23
- * Y coordinate in data space (same as value).
24
- */
25
- dataY: number;
26
- };
27
- /**
28
- * Shared configuration for point appearance and behavior.
29
- * Used by line-associated points rendered via Line/LineChart components.
30
- */
31
- export type PointConfig = {
32
- /**
33
- * The fill color of the point.
34
- */
35
- fill?: string;
36
- /**
37
- * Optional Y-axis id to specify which axis to plot along.
38
- * Defaults to the first y-axis
39
- */
40
- yAxisId?: string;
41
- /**
42
- * Radius of the point.
43
- * @default 4
44
- */
45
- radius?: number;
46
- /**
47
- * Opacity of the point.
48
- */
49
- opacity?: number;
50
- /**
51
- * Handler for when the point is clicked.
52
- */
53
- onClick?: (
54
- event: React.MouseEvent,
55
- point: {
56
- x: number;
57
- y: number;
58
- dataX: number;
59
- dataY: number;
60
- },
61
- ) => void;
62
- /**
63
- * Handler for when the scrubber enters this point.
64
- */
65
- onScrubberEnter?: (point: { x: number; y: number }) => void;
66
- /**
67
- * Color of the outer stroke around the point.
68
- * @default 'var(--color-bg)'
69
- */
70
- stroke?: string;
71
- /**
72
- * Outer stroke width of the point.
73
- * Set to 0 to remove the stroke.
74
- * @default 2
75
- */
76
- strokeWidth?: number;
77
- /**
78
- * Custom class name for the point.
79
- */
80
- className?: string;
81
- /**
82
- * Custom styles for the point.
83
- */
84
- style?: React.CSSProperties;
85
- /**
86
- * Accessibility label for screen readers to describe the point.
87
- * If not provided, a default label will be generated using the data coordinates.
88
- */
89
- accessibilityLabel?: string;
90
- /**
91
- * Simple text label to display at the point position.
92
- * If provided, a ChartText will be automatically rendered.
93
- */
94
- label?: ChartTextChildren;
95
- /**
96
- * Configuration for the automatically rendered label.
97
- * Only used when `label` prop is provided.
98
- */
99
- labelProps?: Omit<ChartTextProps, 'x' | 'y' | 'children'>;
100
- };
101
- export type PointProps = SharedProps &
102
- PointConfig &
103
- Omit<SVGProps<SVGCircleElement>, 'onClick'> & {
104
- /**
105
- * X coordinate in data space (not pixel coordinates).
106
- */
107
- dataX: number;
108
- /**
109
- * Y coordinate in data space (not pixel coordinates).
110
- */
111
- dataY: number;
112
- /**
113
- * Coordinates in SVG pixel space.
114
- * Overrides dataX and dataY for pixel coordinate calculation.
115
- */
116
- pixelCoordinates?: {
117
- x: number;
118
- y: number;
119
- };
120
- /**
121
- * Override the chart's animation setting for this specific point.
122
- * When undefined, uses the chart context's animation setting.
123
- */
124
- animate?: boolean;
125
- /**
126
- * Custom class names for the component.
127
- */
128
- classNames?: {
129
- /**
130
- * Custom class name for the point container element.
131
- */
132
- container?: string;
133
- /**
134
- * Custom class name for the inner circle element.
135
- */
136
- point?: string;
137
- };
138
- /**
139
- * Custom styles for the component.
140
- */
141
- styles?: {
142
- /**
143
- * Custom styles for the point container element.
144
- */
145
- container?: React.CSSProperties;
146
- /**
147
- * Custom styles for the inner circle element.
148
- */
149
- point?: React.CSSProperties;
150
- };
151
- };
152
- export declare const Point: React.NamedExoticComponent<PointProps>;
153
- //# sourceMappingURL=Point.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Point.d.ts","sourceRoot":"","sources":["../../src/chart/Point.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmC,MAAM,OAAO,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAK9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,QAAQ,CAAC;AAqBxD;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;OAEG;IACH,CAAC,EAAE,MAAM,CAAC;IACV;;OAEG;IACH,CAAC,EAAE,MAAM,CAAC;IACV;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,OAAO,CAAC,EAAE,CACR,KAAK,EAAE,KAAK,CAAC,UAAU,EACvB,KAAK,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAC1D,IAAI,CAAC;IACV;;OAEG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC5D;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;OAGG;IACH,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B;;;OAGG;IACH,UAAU,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,GAAG,GAAG,GAAG,GAAG,UAAU,CAAC,CAAC;CAC3D,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,WAAW,GAClC,WAAW,GACX,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,CAAC,GAAG;IAC5C;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,gBAAgB,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,UAAU,CAAC,EAAE;QACX;;WAEG;QACH,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB;;WAEG;QACH,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF;;OAEG;IACH,MAAM,CAAC,EAAE;QACP;;WAEG;QACH,SAAS,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;QAChC;;WAEG;QACH,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;KAC7B,CAAC;CACH,CAAC;AAEJ,eAAO,MAAM,KAAK,wCAsKjB,CAAC"}
@@ -1,42 +0,0 @@
1
- import type { SharedProps } from '@coinbase/cds-common/types';
2
- import { type PathProps } from '../Path';
3
- import type { LineComponentProps } from './Line';
4
- export type GradientLineProps = SharedProps &
5
- Omit<PathProps, 'stroke' | 'strokeOpacity' | 'strokeWidth'> &
6
- Pick<LineComponentProps, 'strokeWidth'> & {
7
- /**
8
- * The color of the line.
9
- * @default 'var(--color-bgLine)'
10
- */
11
- stroke?: string;
12
- /**
13
- * Opacity of the line.
14
- * @default 1
15
- */
16
- strokeOpacity?: number;
17
- /**
18
- * The color of the start of the gradient.
19
- * @default stroke or 'var(--color-bgLine)'
20
- */
21
- startColor?: string;
22
- /**
23
- * The color of the end of the gradient.
24
- * @default stroke or 'var(--color-bgLine)'
25
- */
26
- endColor?: string;
27
- /**
28
- * Opacity of the start color.
29
- * @default strokeOpacity
30
- */
31
- startOpacity?: number;
32
- /**
33
- * Opacity of the end color.
34
- * @default strokeOpacity
35
- */
36
- endOpacity?: number;
37
- };
38
- /**
39
- * A gradient line component which uses path element.
40
- */
41
- export declare const GradientLine: import('react').NamedExoticComponent<GradientLineProps>;
42
- //# sourceMappingURL=GradientLine.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"GradientLine.d.ts","sourceRoot":"","sources":["../../../src/chart/line/GradientLine.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE9D,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAEjD,MAAM,MAAM,iBAAiB,GAAG,WAAW,GACzC,IAAI,CAAC,SAAS,EAAE,QAAQ,GAAG,eAAe,GAAG,aAAa,CAAC,GAC3D,IAAI,CAAC,kBAAkB,EAAE,aAAa,CAAC,GAAG;IACxC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,YAAY,yDAqCxB,CAAC"}