@coinbase/cds-mobile-visualization 3.4.0-beta.1 → 3.4.0-beta.11

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 (187) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/dts/chart/CartesianChart.d.ts +57 -33
  3. package/dts/chart/CartesianChart.d.ts.map +1 -1
  4. package/dts/chart/ChartContextBridge.d.ts +28 -0
  5. package/dts/chart/ChartContextBridge.d.ts.map +1 -0
  6. package/dts/chart/Path.d.ts +77 -34
  7. package/dts/chart/Path.d.ts.map +1 -1
  8. package/dts/chart/PeriodSelector.d.ts +2 -2
  9. package/dts/chart/PeriodSelector.d.ts.map +1 -1
  10. package/dts/chart/area/Area.d.ts +42 -27
  11. package/dts/chart/area/Area.d.ts.map +1 -1
  12. package/dts/chart/area/AreaChart.d.ts +51 -10
  13. package/dts/chart/area/AreaChart.d.ts.map +1 -1
  14. package/dts/chart/area/DottedArea.d.ts +21 -2
  15. package/dts/chart/area/DottedArea.d.ts.map +1 -1
  16. package/dts/chart/area/GradientArea.d.ts +19 -13
  17. package/dts/chart/area/GradientArea.d.ts.map +1 -1
  18. package/dts/chart/area/SolidArea.d.ts +17 -2
  19. package/dts/chart/area/SolidArea.d.ts.map +1 -1
  20. package/dts/chart/axis/Axis.d.ts +86 -118
  21. package/dts/chart/axis/Axis.d.ts.map +1 -1
  22. package/dts/chart/axis/DefaultAxisTickLabel.d.ts +8 -0
  23. package/dts/chart/axis/DefaultAxisTickLabel.d.ts.map +1 -0
  24. package/dts/chart/axis/XAxis.d.ts +1 -1
  25. package/dts/chart/axis/XAxis.d.ts.map +1 -1
  26. package/dts/chart/axis/YAxis.d.ts +2 -2
  27. package/dts/chart/axis/YAxis.d.ts.map +1 -1
  28. package/dts/chart/axis/index.d.ts +1 -0
  29. package/dts/chart/axis/index.d.ts.map +1 -1
  30. package/dts/chart/bar/Bar.d.ts +16 -13
  31. package/dts/chart/bar/Bar.d.ts.map +1 -1
  32. package/dts/chart/bar/BarChart.d.ts +36 -20
  33. package/dts/chart/bar/BarChart.d.ts.map +1 -1
  34. package/dts/chart/bar/BarPlot.d.ts +2 -1
  35. package/dts/chart/bar/BarPlot.d.ts.map +1 -1
  36. package/dts/chart/bar/BarStack.d.ts +39 -48
  37. package/dts/chart/bar/BarStack.d.ts.map +1 -1
  38. package/dts/chart/bar/BarStackGroup.d.ts +1 -0
  39. package/dts/chart/bar/BarStackGroup.d.ts.map +1 -1
  40. package/dts/chart/bar/DefaultBar.d.ts +1 -1
  41. package/dts/chart/bar/DefaultBar.d.ts.map +1 -1
  42. package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
  43. package/dts/chart/gradient/Gradient.d.ts +25 -0
  44. package/dts/chart/gradient/Gradient.d.ts.map +1 -0
  45. package/dts/chart/gradient/index.d.ts +2 -0
  46. package/dts/chart/gradient/index.d.ts.map +1 -0
  47. package/dts/chart/index.d.ts +3 -1
  48. package/dts/chart/index.d.ts.map +1 -1
  49. package/dts/chart/line/DefaultReferenceLineLabel.d.ts +9 -0
  50. package/dts/chart/line/DefaultReferenceLineLabel.d.ts.map +1 -0
  51. package/dts/chart/line/DottedLine.d.ts +13 -5
  52. package/dts/chart/line/DottedLine.d.ts.map +1 -1
  53. package/dts/chart/line/Line.d.ts +64 -25
  54. package/dts/chart/line/Line.d.ts.map +1 -1
  55. package/dts/chart/line/LineChart.d.ts +43 -9
  56. package/dts/chart/line/LineChart.d.ts.map +1 -1
  57. package/dts/chart/line/ReferenceLine.d.ts +68 -20
  58. package/dts/chart/line/ReferenceLine.d.ts.map +1 -1
  59. package/dts/chart/line/SolidLine.d.ts +8 -5
  60. package/dts/chart/line/SolidLine.d.ts.map +1 -1
  61. package/dts/chart/line/index.d.ts +1 -1
  62. package/dts/chart/line/index.d.ts.map +1 -1
  63. package/dts/chart/point/DefaultPointLabel.d.ts +10 -0
  64. package/dts/chart/point/DefaultPointLabel.d.ts.map +1 -0
  65. package/dts/chart/point/Point.d.ts +120 -0
  66. package/dts/chart/point/Point.d.ts.map +1 -0
  67. package/dts/chart/point/index.d.ts +3 -0
  68. package/dts/chart/point/index.d.ts.map +1 -0
  69. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts +8 -0
  70. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -0
  71. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts +12 -0
  72. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts.map +1 -0
  73. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +11 -0
  74. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -0
  75. package/dts/chart/scrubber/Scrubber.d.ts +172 -43
  76. package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
  77. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts +44 -0
  78. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -0
  79. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts +31 -0
  80. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts.map +1 -0
  81. package/dts/chart/scrubber/ScrubberProvider.d.ts +6 -3
  82. package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -1
  83. package/dts/chart/scrubber/index.d.ts +3 -0
  84. package/dts/chart/scrubber/index.d.ts.map +1 -1
  85. package/dts/chart/text/ChartText.d.ts +151 -77
  86. package/dts/chart/text/ChartText.d.ts.map +1 -1
  87. package/dts/chart/text/{SmartChartTextGroup.d.ts → ChartTextGroup.d.ts} +9 -3
  88. package/dts/chart/text/ChartTextGroup.d.ts.map +1 -0
  89. package/dts/chart/text/index.d.ts +1 -1
  90. package/dts/chart/text/index.d.ts.map +1 -1
  91. package/dts/chart/utils/axis.d.ts +25 -1
  92. package/dts/chart/utils/axis.d.ts.map +1 -1
  93. package/dts/chart/utils/chart.d.ts +34 -7
  94. package/dts/chart/utils/chart.d.ts.map +1 -1
  95. package/dts/chart/utils/context.d.ts +28 -7
  96. package/dts/chart/utils/context.d.ts.map +1 -1
  97. package/dts/chart/utils/gradient.d.ts +117 -0
  98. package/dts/chart/utils/gradient.d.ts.map +1 -0
  99. package/dts/chart/utils/index.d.ts +3 -0
  100. package/dts/chart/utils/index.d.ts.map +1 -1
  101. package/dts/chart/utils/path.d.ts +53 -0
  102. package/dts/chart/utils/path.d.ts.map +1 -1
  103. package/dts/chart/utils/point.d.ts +71 -7
  104. package/dts/chart/utils/point.d.ts.map +1 -1
  105. package/dts/chart/utils/scale.d.ts +102 -0
  106. package/dts/chart/utils/scale.d.ts.map +1 -1
  107. package/dts/chart/utils/scrubber.d.ts +39 -0
  108. package/dts/chart/utils/scrubber.d.ts.map +1 -0
  109. package/dts/chart/utils/transition.d.ts +140 -0
  110. package/dts/chart/utils/transition.d.ts.map +1 -0
  111. package/esm/chart/CartesianChart.js +164 -70
  112. package/esm/chart/ChartContextBridge.js +148 -0
  113. package/esm/chart/Path.js +198 -113
  114. package/esm/chart/PeriodSelector.js +2 -2
  115. package/esm/chart/__stories__/CartesianChart.stories.js +378 -131
  116. package/esm/chart/__stories__/Chart.stories.js +2 -4
  117. package/esm/chart/__stories__/PeriodSelector.stories.js +103 -75
  118. package/esm/chart/area/Area.js +25 -35
  119. package/esm/chart/area/AreaChart.js +17 -12
  120. package/esm/chart/area/DottedArea.js +61 -109
  121. package/esm/chart/area/GradientArea.js +35 -91
  122. package/esm/chart/area/SolidArea.js +22 -8
  123. package/esm/chart/area/__stories__/AreaChart.stories.js +1 -1
  124. package/esm/chart/axis/Axis.js +5 -39
  125. package/esm/chart/axis/DefaultAxisTickLabel.js +11 -0
  126. package/esm/chart/axis/XAxis.js +148 -66
  127. package/esm/chart/axis/YAxis.js +149 -65
  128. package/esm/chart/axis/__stories__/Axis.stories.js +259 -1
  129. package/esm/chart/axis/index.js +1 -0
  130. package/esm/chart/bar/Bar.js +3 -1
  131. package/esm/chart/bar/BarChart.js +15 -37
  132. package/esm/chart/bar/BarPlot.js +41 -35
  133. package/esm/chart/bar/BarStack.js +75 -38
  134. package/esm/chart/bar/BarStackGroup.js +6 -16
  135. package/esm/chart/bar/DefaultBar.js +26 -48
  136. package/esm/chart/bar/DefaultBarStack.js +23 -58
  137. package/esm/chart/bar/__stories__/BarChart.stories.js +502 -77
  138. package/esm/chart/gradient/Gradient.js +53 -0
  139. package/esm/chart/gradient/index.js +1 -0
  140. package/esm/chart/index.js +3 -1
  141. package/esm/chart/line/DefaultReferenceLineLabel.js +66 -0
  142. package/esm/chart/line/DottedLine.js +29 -14
  143. package/esm/chart/line/Line.js +106 -67
  144. package/esm/chart/line/LineChart.js +20 -14
  145. package/esm/chart/line/ReferenceLine.js +80 -63
  146. package/esm/chart/line/SolidLine.js +25 -10
  147. package/esm/chart/line/__stories__/LineChart.stories.js +2101 -1977
  148. package/esm/chart/line/__stories__/ReferenceLine.stories.js +83 -28
  149. package/esm/chart/line/index.js +1 -1
  150. package/esm/chart/point/DefaultPointLabel.js +39 -0
  151. package/esm/chart/point/Point.js +188 -0
  152. package/esm/chart/point/index.js +2 -0
  153. package/esm/chart/scrubber/DefaultScrubberBeacon.js +179 -0
  154. package/esm/chart/scrubber/DefaultScrubberBeaconLabel.js +43 -0
  155. package/esm/chart/scrubber/DefaultScrubberLabel.js +28 -0
  156. package/esm/chart/scrubber/Scrubber.js +126 -146
  157. package/esm/chart/scrubber/ScrubberBeaconGroup.js +161 -0
  158. package/esm/chart/scrubber/ScrubberBeaconLabelGroup.js +185 -0
  159. package/esm/chart/scrubber/ScrubberProvider.js +46 -54
  160. package/esm/chart/scrubber/index.js +3 -1
  161. package/esm/chart/text/ChartText.js +242 -174
  162. package/esm/chart/text/{SmartChartTextGroup.js → ChartTextGroup.js} +6 -5
  163. package/esm/chart/text/index.js +1 -1
  164. package/esm/chart/utils/axis.js +45 -29
  165. package/esm/chart/utils/chart.js +44 -3
  166. package/esm/chart/utils/gradient.js +305 -0
  167. package/esm/chart/utils/index.js +3 -0
  168. package/esm/chart/utils/path.js +76 -8
  169. package/esm/chart/utils/point.js +171 -17
  170. package/esm/chart/utils/scale.js +242 -2
  171. package/esm/chart/utils/scrubber.js +139 -0
  172. package/esm/chart/utils/transition.js +185 -0
  173. package/esm/sparkline/__stories__/Sparkline.stories.js +11 -7
  174. package/esm/sparkline/__stories__/SparklineGradient.stories.js +7 -4
  175. package/esm/sparkline/sparkline-interactive/__stories__/SparklineInteractive.stories.js +51 -26
  176. package/esm/sparkline/sparkline-interactive-header/__stories__/SparklineInteractiveHeader.stories.js +17 -9
  177. package/package.json +15 -9
  178. package/dts/chart/Point.d.ts +0 -103
  179. package/dts/chart/Point.d.ts.map +0 -1
  180. package/dts/chart/line/GradientLine.d.ts +0 -45
  181. package/dts/chart/line/GradientLine.d.ts.map +0 -1
  182. package/dts/chart/scrubber/ScrubberBeacon.d.ts +0 -75
  183. package/dts/chart/scrubber/ScrubberBeacon.d.ts.map +0 -1
  184. package/dts/chart/text/SmartChartTextGroup.d.ts.map +0 -1
  185. package/esm/chart/Point.js +0 -111
  186. package/esm/chart/line/GradientLine.js +0 -62
  187. package/esm/chart/scrubber/ScrubberBeacon.js +0 -199
@@ -1,20 +1,47 @@
1
- import { isCategoricalScale, isLogScale, isNumericScale } from './scale';
1
+ import { applyBandScale, applySerializableScale, isCategoricalScale, isLogScale, isNumericScale } from './scale';
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
+ */
2
10
 
3
11
  /**
4
12
  * Get a point from a data value and a scale.
5
- * @note for categorical scales, the point will be centered within the band.
6
- * @note for log scales, zero and negative values are clamped to a small positive value.
7
- * @param data - the data value.
8
- * @param scale - the scale function.
9
- * @returns the pixel value (defaulting to 0 if data value is not defined in scale).
13
+ *
14
+ * @param dataValue - The data value to convert to a pixel position.
15
+ * @param scale - The scale function.
16
+ * @param anchor (@default 'middle') - For band scales, where to anchor the point within the band.
17
+ * @returns The pixel value (@default 0 if data value is not defined in scale).
10
18
  */
11
- export const getPointOnScale = (dataValue, scale) => {
12
- var _scale2;
19
+ export const getPointOnScale = function (dataValue, scale, anchor) {
20
+ var _scale;
21
+ if (anchor === void 0) {
22
+ anchor = 'middle';
23
+ }
13
24
  if (isCategoricalScale(scale)) {
14
- var _scale, _scale$bandwidth;
15
- const bandStart = (_scale = scale(dataValue)) != null ? _scale : 0;
16
- const bandwidth = (_scale$bandwidth = scale.bandwidth()) != null ? _scale$bandwidth : 0;
17
- return bandStart + bandwidth / 2;
25
+ var _bandScale$bandwidth, _bandScale$step;
26
+ const bandScale = scale;
27
+ const bandStart = bandScale(dataValue);
28
+ if (bandStart === undefined) return 0;
29
+ const bandwidth = (_bandScale$bandwidth = bandScale.bandwidth == null ? void 0 : bandScale.bandwidth()) != null ? _bandScale$bandwidth : 0;
30
+ const step = (_bandScale$step = bandScale.step == null ? void 0 : bandScale.step()) != null ? _bandScale$step : bandwidth;
31
+ const paddingOffset = (step - bandwidth) / 2;
32
+ const stepStart = bandStart - paddingOffset;
33
+ switch (anchor) {
34
+ case 'stepStart':
35
+ return stepStart;
36
+ case 'bandStart':
37
+ return bandStart;
38
+ case 'middle':
39
+ return bandStart + bandwidth / 2;
40
+ case 'bandEnd':
41
+ return bandStart + bandwidth;
42
+ case 'stepEnd':
43
+ return stepStart + step;
44
+ }
18
45
  }
19
46
 
20
47
  // For log scales, ensure the value is positive
@@ -22,9 +49,75 @@ export const getPointOnScale = (dataValue, scale) => {
22
49
  if (isLogScale(scale) && dataValue <= 0) {
23
50
  adjustedValue = 0.001; // Use a small positive value for log scales
24
51
  }
25
- return (_scale2 = scale(adjustedValue)) != null ? _scale2 : 0;
52
+ return (_scale = scale(adjustedValue)) != null ? _scale : 0;
26
53
  };
27
54
 
55
+ /**
56
+ * Get a point from a data value and a serializable scale (worklet-compatible).
57
+ *
58
+ * @param dataValue - The data value to convert to a pixel position.
59
+ * @param scale - The serializable scale function.
60
+ * @param anchor (@default 'middle') - For band scales, where to anchor the point within the band.
61
+ * @returns The pixel value (@default 0 if data value is not defined in scale).
62
+ */
63
+ export function getPointOnSerializableScale(dataValue, scale, anchor) {
64
+ 'worklet';
65
+
66
+ // Handle band scales with the specified position
67
+ if (anchor === void 0) {
68
+ anchor = 'middle';
69
+ }
70
+ if (scale.type === 'band') {
71
+ const bandScale = scale;
72
+ const [domainMin, domainMax] = bandScale.domain;
73
+ const index = dataValue - domainMin;
74
+ const n = domainMax - domainMin + 1;
75
+ if (index < 0 || index >= n) {
76
+ return 0;
77
+ }
78
+ const bandStart = applyBandScale(dataValue, bandScale);
79
+ const paddingOffset = (bandScale.step - bandScale.bandwidth) / 2;
80
+ const stepStart = bandStart - paddingOffset;
81
+ switch (anchor) {
82
+ case 'stepStart':
83
+ return stepStart;
84
+ case 'bandStart':
85
+ return bandStart;
86
+ case 'middle':
87
+ return bandStart + bandScale.bandwidth / 2;
88
+ case 'bandEnd':
89
+ return bandStart + bandScale.bandwidth;
90
+ case 'stepEnd':
91
+ return stepStart + bandScale.step;
92
+ }
93
+ }
94
+
95
+ // For log scales, ensure the value is positive
96
+ if (scale.type === 'log' && dataValue <= 0) {
97
+ dataValue = 0.001; // Use a small positive value for log scales
98
+ }
99
+ return applySerializableScale(dataValue, scale);
100
+ }
101
+
102
+ /**
103
+ * Projects a single data point to pixel coordinates using serializable scales.
104
+ * This is the worklet-compatible version for use in react-native-reanimated.
105
+ */
106
+ export function projectPointWithSerializableScale(_ref) {
107
+ 'worklet';
108
+
109
+ let {
110
+ x,
111
+ y,
112
+ xScale,
113
+ yScale
114
+ } = _ref;
115
+ return {
116
+ x: getPointOnSerializableScale(x, xScale),
117
+ y: getPointOnSerializableScale(y, yScale)
118
+ };
119
+ }
120
+
28
121
  /**
29
122
  * Projects a data point to pixel coordinates using the chart scale.
30
123
  * Automatically handles log scale transformations for zero/negative values.
@@ -40,13 +133,13 @@ export const getPointOnScale = (dataValue, scale) => {
40
133
  * const pixelCoord = projectPoint({ x: 2, y: 10, chartScale, xData: ['Jan', 'Feb', 'Mar'] });
41
134
  * ```
42
135
  */
43
- export const projectPoint = _ref => {
136
+ export const projectPoint = _ref2 => {
44
137
  let {
45
138
  x,
46
139
  y,
47
140
  xScale,
48
141
  yScale
49
- } = _ref;
142
+ } = _ref2;
50
143
  return {
51
144
  x: getPointOnScale(x, xScale),
52
145
  y: getPointOnScale(y, yScale)
@@ -65,14 +158,14 @@ export const projectPoint = _ref => {
65
158
  * const pixelPoints = projectPoints({ data, chartScale, xData: ['Jan', 'Feb', 'Mar'] });
66
159
  * ```
67
160
  */
68
- export const projectPoints = _ref2 => {
161
+ export const projectPoints = _ref3 => {
69
162
  let {
70
163
  data,
71
164
  xScale,
72
165
  yScale,
73
166
  xData,
74
167
  yData
75
- } = _ref2;
168
+ } = _ref3;
76
169
  if (data.length === 0) {
77
170
  return [];
78
171
  }
@@ -115,4 +208,65 @@ export const projectPoints = _ref2 => {
115
208
  yScale
116
209
  });
117
210
  });
211
+ };
212
+
213
+ /**
214
+ * Determines text alignment based on label position.
215
+ * For example, a 'top' position needs the text aligned to the 'bottom' so it appears above the point.
216
+ */
217
+ export const getAlignmentFromPosition = position => {
218
+ let horizontalAlignment = 'center';
219
+ let verticalAlignment = 'middle';
220
+ switch (position) {
221
+ case 'top':
222
+ verticalAlignment = 'bottom';
223
+ break;
224
+ case 'bottom':
225
+ verticalAlignment = 'top';
226
+ break;
227
+ case 'left':
228
+ horizontalAlignment = 'right';
229
+ break;
230
+ case 'right':
231
+ horizontalAlignment = 'left';
232
+ break;
233
+ case 'center':
234
+ default:
235
+ horizontalAlignment = 'center';
236
+ verticalAlignment = 'middle';
237
+ break;
238
+ }
239
+ return {
240
+ horizontalAlignment,
241
+ verticalAlignment
242
+ };
243
+ };
244
+
245
+ /**
246
+ * Calculates the final label coordinates by applying offset based on position.
247
+ */
248
+ export const getLabelCoordinates = (x, y, position, offset) => {
249
+ let dx = 0;
250
+ let dy = 0;
251
+ switch (position) {
252
+ case 'top':
253
+ dy = -offset;
254
+ break;
255
+ case 'bottom':
256
+ dy = offset;
257
+ break;
258
+ case 'left':
259
+ dx = -offset;
260
+ break;
261
+ case 'right':
262
+ dx = offset;
263
+ break;
264
+ case 'center':
265
+ default:
266
+ break;
267
+ }
268
+ return {
269
+ x: x + dx,
270
+ y: y + dy
271
+ };
118
272
  };
@@ -16,6 +16,16 @@ export const isLogScale = scale => {
16
16
  return scale !== undefined && 'base' in scale && typeof scale.base === 'function';
17
17
  };
18
18
 
19
+ /**
20
+ * Type guard to check if a scale is a SerializableScale.
21
+ * This can be used in worklets to differentiate between scale objects and scale functions.
22
+ */
23
+ export const isSerializableScale = scale => {
24
+ 'worklet';
25
+
26
+ return typeof scale === 'object' && scale !== null && 'type' in scale && 'domain' in scale && 'range' in scale;
27
+ };
28
+
19
29
  /**
20
30
  * Create a numeric scale (linear or logarithmic)
21
31
  * @returns A numeric scale function
@@ -43,6 +53,236 @@ export const getCategoricalScale = _ref2 => {
43
53
  const domainArray = Array.from({
44
54
  length: domain.max - domain.min + 1
45
55
  }, (_, i) => i);
46
- const scale = scaleBand().domain(domainArray).range([range.min, range.max]).padding(padding);
56
+ const scale = scaleBand().domain(domainArray).range([range.min, range.max]).paddingInner(padding).paddingOuter(padding / 2);
47
57
  return scale;
48
- };
58
+ };
59
+
60
+ /**
61
+ * Anchor position for points on a scale. Currently used only for band scales.
62
+ *
63
+ * For band scales, this determines where within the band to position a point:
64
+ * - `'stepStart'` - At the start of the step
65
+ * - `'bandStart'` - At the start of the band
66
+ * - `'middle'` - At the center of the band
67
+ * - `'bandEnd'` - At the end of the band
68
+ * - `'stepEnd'` - At the end of the step
69
+ */
70
+
71
+ /**
72
+ * Convert a D3 scale to a serializable scale configuration that can be used in worklets
73
+ */
74
+ export function convertToSerializableScale(d3Scale) {
75
+ if (!d3Scale) return undefined;
76
+ const domain = d3Scale.domain();
77
+ const range = d3Scale.range();
78
+
79
+ // Handle band/categorical scales
80
+ if (isCategoricalScale(d3Scale)) {
81
+ var _step;
82
+ const bandScale = d3Scale;
83
+ const bandwidth = bandScale.bandwidth();
84
+ const step = (_step = bandScale.step == null ? void 0 : bandScale.step()) != null ? _step : (range[1] - range[0]) / domain.length;
85
+ return {
86
+ type: 'band',
87
+ domain: [domain[0], domain[domain.length - 1]],
88
+ range: [range[0], range[range.length - 1]],
89
+ bandwidth,
90
+ step
91
+ };
92
+ }
93
+
94
+ // Handle log scales
95
+ if (isLogScale(d3Scale)) {
96
+ var _base;
97
+ const logScale = d3Scale;
98
+ // D3 log scales default to base 10
99
+ const base = (_base = logScale.base == null ? void 0 : logScale.base()) != null ? _base : 10;
100
+ return {
101
+ type: 'log',
102
+ domain: [domain[0], domain[domain.length - 1]],
103
+ range: [range[0], range[range.length - 1]],
104
+ base
105
+ };
106
+ }
107
+
108
+ // Handle linear scales (default)
109
+ if (isNumericScale(d3Scale)) {
110
+ return {
111
+ type: 'linear',
112
+ domain: [domain[0], domain[domain.length - 1]],
113
+ range: [range[0], range[range.length - 1]]
114
+ };
115
+ }
116
+ return undefined;
117
+ }
118
+
119
+ /**
120
+ * Convert multiple D3 scales to serializable scales
121
+ */
122
+ export function convertScalesToSerializableScales(xScale, yScales) {
123
+ const result = {
124
+ yScales: {}
125
+ };
126
+
127
+ // Convert X scale
128
+ if (xScale) {
129
+ result.xScale = convertToSerializableScale(xScale);
130
+ }
131
+
132
+ // Convert Y scales
133
+ if (yScales) {
134
+ yScales.forEach((scale, id) => {
135
+ const serializableScale = convertToSerializableScale(scale);
136
+ if (serializableScale) {
137
+ result.yScales[id] = serializableScale;
138
+ }
139
+ });
140
+ }
141
+ return result;
142
+ }
143
+
144
+ /**
145
+ * Serializable scale implementations based on D3 scale concepts.
146
+ * These scales can be used directly on the UI thread in Reanimated worklets.
147
+ */
148
+
149
+ /**
150
+ * Serializable linear scale function
151
+ */
152
+ export function applyLinearScale(value, scale) {
153
+ 'worklet';
154
+
155
+ const [d0, d1] = scale.domain;
156
+ const [r0, r1] = scale.range;
157
+ const t = (value - d0) / (d1 - d0); // normalize to [0, 1]
158
+ return r0 + t * (r1 - r0); // interpolate in range
159
+ }
160
+
161
+ /**
162
+ * Serializable log scale function
163
+ */
164
+ export function applyLogScale(value, scale) {
165
+ 'worklet';
166
+
167
+ var _scale$base;
168
+ const [d0, d1] = scale.domain;
169
+ const [r0, r1] = scale.range;
170
+ const base = (_scale$base = scale.base) != null ? _scale$base : 10;
171
+ const logBase = base === 10 ? Math.log10 : base === Math.E ? Math.log : x => Math.log(x) / Math.log(base);
172
+ const t = (logBase(value) - logBase(d0)) / (logBase(d1) - logBase(d0));
173
+ return r0 + t * (r1 - r0);
174
+ }
175
+
176
+ /**
177
+ * Serializable band scale function
178
+ */
179
+ export function applyBandScale(value, scale) {
180
+ 'worklet';
181
+
182
+ const [r0, r1] = scale.range;
183
+ const [domainMin, domainMax] = scale.domain;
184
+ const n = domainMax - domainMin + 1;
185
+ const step = scale.step;
186
+ const index = value - domainMin;
187
+ if (index < 0 || index >= n) {
188
+ return r0;
189
+ }
190
+ const paddingOffset = (step - scale.bandwidth) / 2;
191
+ const bandStart = r0 + step * index + paddingOffset;
192
+ return bandStart;
193
+ }
194
+
195
+ /**
196
+ * Universal serializable scale function that handles any scale type
197
+ */
198
+ export function applySerializableScale(value, scale) {
199
+ 'worklet';
200
+
201
+ switch (scale.type) {
202
+ case 'linear':
203
+ return applyLinearScale(value, scale);
204
+ case 'log':
205
+ return applyLogScale(value, scale);
206
+ case 'band':
207
+ return applyBandScale(value, scale);
208
+ default:
209
+ return 0;
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Get bandwidth for band scales (returns 0 for other scale types)
215
+ */
216
+ export function getScaleBandwidth(scale) {
217
+ 'worklet';
218
+
219
+ if (scale.type === 'band') {
220
+ return scale.bandwidth;
221
+ }
222
+ return 0;
223
+ }
224
+
225
+ /**
226
+ * Invert a linear scale - convert from range value back to domain value
227
+ */
228
+ export function invertLinearScale(rangeValue, scale) {
229
+ 'worklet';
230
+
231
+ const [d0, d1] = scale.domain;
232
+ const [r0, r1] = scale.range;
233
+ const t = (rangeValue - r0) / (r1 - r0); // normalize to [0, 1]
234
+ return d0 + t * (d1 - d0); // interpolate in domain
235
+ }
236
+
237
+ /**
238
+ * Invert a log scale - convert from range value back to domain value
239
+ */
240
+ export function invertLogScale(rangeValue, scale) {
241
+ 'worklet';
242
+
243
+ var _scale$base2;
244
+ const [d0, d1] = scale.domain;
245
+ const [r0, r1] = scale.range;
246
+ const base = (_scale$base2 = scale.base) != null ? _scale$base2 : 10;
247
+ const logBase = base === 10 ? Math.log10 : base === Math.E ? Math.log : x => Math.log(x) / Math.log(base);
248
+ const t = (rangeValue - r0) / (r1 - r0); // normalize to [0, 1]
249
+ const logValue = logBase(d0) + t * (logBase(d1) - logBase(d0));
250
+
251
+ // Convert back from log space
252
+ return base === 10 ? Math.pow(10, logValue) : base === Math.E ? Math.exp(logValue) : Math.pow(base, logValue);
253
+ }
254
+
255
+ /**
256
+ * Invert a band scale - convert from range value back to domain index
257
+ */
258
+ export function invertBandScale(rangeValue, scale) {
259
+ 'worklet';
260
+
261
+ const [r0, r1] = scale.range;
262
+ const n = scale.domain.length;
263
+ const step = (r1 - r0) / n;
264
+
265
+ // Find which band this range value falls into
266
+ const index = Math.floor((rangeValue - r0) / step);
267
+
268
+ // Clamp to valid range
269
+ return Math.max(0, Math.min(index, n - 1));
270
+ }
271
+
272
+ /**
273
+ * Universal serializable scale invert function that handles any scale type
274
+ */
275
+ export function invertSerializableScale(rangeValue, scale) {
276
+ 'worklet';
277
+
278
+ switch (scale.type) {
279
+ case 'linear':
280
+ return invertLinearScale(rangeValue, scale);
281
+ case 'log':
282
+ return invertLogScale(rangeValue, scale);
283
+ case 'band':
284
+ return invertBandScale(rangeValue, scale);
285
+ default:
286
+ return 0;
287
+ }
288
+ }
@@ -0,0 +1,139 @@
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, xOffset) {
6
+ 'worklet';
7
+
8
+ // any regular functions in ui thread must be marked with 'worklet'
9
+ if (xOffset === void 0) {
10
+ xOffset = 16;
11
+ }
12
+ if (drawingArea.width <= 0 || drawingArea.height <= 0) {
13
+ return 'right';
14
+ }
15
+ const availableRightSpace = drawingArea.x + drawingArea.width - beaconX;
16
+ const requiredSpace = maxLabelWidth + xOffset;
17
+ return requiredSpace <= availableRightSpace ? 'right' : 'left';
18
+ };
19
+ /**
20
+ * Calculates Y positions for all labels avoiding overlaps while maintaining order.
21
+ */
22
+ export const calculateLabelYPositions = (dimensions, drawingArea, labelHeight, minGap) => {
23
+ 'worklet';
24
+
25
+ if (dimensions.length === 0) {
26
+ return new Map();
27
+ }
28
+
29
+ // Sort by preferred Y values and create working labels
30
+ const sortedLabels = [...dimensions].sort((a, b) => a.preferredY - b.preferredY).map(dim => ({
31
+ seriesId: dim.seriesId,
32
+ preferredY: dim.preferredY,
33
+ finalY: dim.preferredY
34
+ }));
35
+
36
+ // Initial bounds fitting
37
+ const minY = drawingArea.y + labelHeight / 2;
38
+ const maxY = drawingArea.y + drawingArea.height - labelHeight / 2;
39
+ const requiredDistance = labelHeight + minGap;
40
+ for (const label of sortedLabels) {
41
+ // Clamp each label to the drawing area
42
+ label.finalY = Math.max(minY, Math.min(maxY, label.preferredY));
43
+ }
44
+
45
+ // First pass: push down any overlapping labels
46
+ for (let i = 1; i < sortedLabels.length; i++) {
47
+ const prev = sortedLabels[i - 1];
48
+ const current = sortedLabels[i];
49
+ const minAllowedY = prev.finalY + requiredDistance;
50
+ if (current.finalY < minAllowedY) {
51
+ current.finalY = minAllowedY;
52
+ }
53
+ }
54
+
55
+ // Find collision groups - groups of labels that are tightly packed (gap < minGap between them)
56
+ const collisionGroups = [];
57
+ let currentGroup = [sortedLabels[0]];
58
+ for (let i = 1; i < sortedLabels.length; i++) {
59
+ const prev = sortedLabels[i - 1];
60
+ const current = sortedLabels[i];
61
+ const gap = current.finalY - prev.finalY - labelHeight;
62
+ if (gap < minGap + 0.01) {
63
+ // Labels are touching or very close - part of same collision group
64
+ currentGroup.push(current);
65
+ } else {
66
+ // Gap is large enough - start new group
67
+ collisionGroups.push(currentGroup);
68
+ currentGroup = [current];
69
+ }
70
+ }
71
+ collisionGroups.push(currentGroup);
72
+
73
+ // Process each collision group - optimize positioning to minimize displacement
74
+ for (const group of collisionGroups) {
75
+ if (group.length === 1) {
76
+ // Single label, already at best position
77
+ continue;
78
+ }
79
+ const groupLastLabel = group[group.length - 1];
80
+ const groupFirstLabel = group[0];
81
+ const groupOverflow = groupLastLabel.finalY + labelHeight / 2 - (drawingArea.y + drawingArea.height);
82
+
83
+ // Calculate the ideal center point for this group
84
+ const groupPreferredCenter = group.reduce((sum, label) => sum + label.preferredY, 0) / group.length;
85
+ const groupTotalNeeded = group.length * labelHeight + (group.length - 1) * minGap;
86
+ if (groupOverflow <= 0) {
87
+ // Group fits, but let's center it better if possible
88
+ // Calculate how much we can shift up/down to center around preferred positions
89
+ const currentCenter = (groupFirstLabel.finalY + groupLastLabel.finalY) / 2;
90
+ const desiredShift = groupPreferredCenter - currentCenter;
91
+
92
+ // Calculate max shift in each direction
93
+ const maxShiftUp = groupFirstLabel.finalY - minY;
94
+ const maxShiftDown = maxY - groupLastLabel.finalY;
95
+
96
+ // Apply the shift, constrained by boundaries
97
+ const actualShift = Math.max(-maxShiftUp, Math.min(maxShiftDown, desiredShift));
98
+ if (Math.abs(actualShift) > 0.01) {
99
+ for (const label of group) {
100
+ label.finalY += actualShift;
101
+ }
102
+ }
103
+ } else {
104
+ // Group overflows - need to adjust
105
+ const groupStartY = groupFirstLabel.finalY - labelHeight / 2;
106
+ const availableSpace = drawingArea.y + drawingArea.height - groupStartY;
107
+ const maxShiftUp = groupFirstLabel.finalY - minY;
108
+ if (maxShiftUp >= groupOverflow) {
109
+ // Can shift entire group up to fit
110
+ for (const label of group) {
111
+ label.finalY -= groupOverflow;
112
+ }
113
+ } else if (groupTotalNeeded <= availableSpace) {
114
+ // Can't shift enough, but there's room - redistribute with proper spacing
115
+ let currentY = Math.max(minY, groupFirstLabel.finalY - maxShiftUp);
116
+ const gap = (availableSpace - group.length * labelHeight) / Math.max(1, group.length - 1);
117
+ for (const label of group) {
118
+ label.finalY = currentY;
119
+ currentY += labelHeight + gap;
120
+ }
121
+ } else {
122
+ // Not enough space even with compression - compress gaps and fit to bottom
123
+ const compressedGap = Math.max(1, (availableSpace - group.length * labelHeight) / Math.max(1, group.length - 1));
124
+ // Position so last label is at maxY
125
+ let currentY = maxY - (group.length - 1) * (labelHeight + compressedGap);
126
+ currentY = Math.max(minY, currentY);
127
+ for (const label of group) {
128
+ label.finalY = currentY;
129
+ currentY += labelHeight + compressedGap;
130
+ }
131
+ }
132
+ }
133
+ }
134
+ const result = new Map();
135
+ for (const label of sortedLabels) {
136
+ result.set(label.seriesId, label.finalY);
137
+ }
138
+ return result;
139
+ };