@coinbase/cds-mobile-visualization 3.4.0-beta.2 → 3.4.0-beta.20

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 (211) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/dts/chart/CartesianChart.d.ts +92 -34
  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/ChartProvider.d.ts +3 -0
  7. package/dts/chart/ChartProvider.d.ts.map +1 -1
  8. package/dts/chart/Path.d.ts +97 -32
  9. package/dts/chart/Path.d.ts.map +1 -1
  10. package/dts/chart/PeriodSelector.d.ts +6 -13
  11. package/dts/chart/PeriodSelector.d.ts.map +1 -1
  12. package/dts/chart/area/Area.d.ts +39 -28
  13. package/dts/chart/area/Area.d.ts.map +1 -1
  14. package/dts/chart/area/AreaChart.d.ts +51 -10
  15. package/dts/chart/area/AreaChart.d.ts.map +1 -1
  16. package/dts/chart/area/DottedArea.d.ts +21 -2
  17. package/dts/chart/area/DottedArea.d.ts.map +1 -1
  18. package/dts/chart/area/GradientArea.d.ts +19 -13
  19. package/dts/chart/area/GradientArea.d.ts.map +1 -1
  20. package/dts/chart/area/SolidArea.d.ts +17 -2
  21. package/dts/chart/area/SolidArea.d.ts.map +1 -1
  22. package/dts/chart/axis/Axis.d.ts +86 -118
  23. package/dts/chart/axis/Axis.d.ts.map +1 -1
  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 +1 -1
  27. package/dts/chart/axis/XAxis.d.ts.map +1 -1
  28. package/dts/chart/axis/YAxis.d.ts +2 -2
  29. package/dts/chart/axis/YAxis.d.ts.map +1 -1
  30. package/dts/chart/axis/index.d.ts +1 -0
  31. package/dts/chart/axis/index.d.ts.map +1 -1
  32. package/dts/chart/bar/Bar.d.ts +49 -12
  33. package/dts/chart/bar/Bar.d.ts.map +1 -1
  34. package/dts/chart/bar/BarChart.d.ts +40 -19
  35. package/dts/chart/bar/BarChart.d.ts.map +1 -1
  36. package/dts/chart/bar/BarPlot.d.ts +3 -1
  37. package/dts/chart/bar/BarPlot.d.ts.map +1 -1
  38. package/dts/chart/bar/BarStack.d.ts +41 -46
  39. package/dts/chart/bar/BarStack.d.ts.map +1 -1
  40. package/dts/chart/bar/BarStackGroup.d.ts +2 -0
  41. package/dts/chart/bar/BarStackGroup.d.ts.map +1 -1
  42. package/dts/chart/bar/DefaultBar.d.ts +1 -1
  43. package/dts/chart/bar/DefaultBar.d.ts.map +1 -1
  44. package/dts/chart/bar/DefaultBarStack.d.ts.map +1 -1
  45. package/dts/chart/gradient/Gradient.d.ts +25 -0
  46. package/dts/chart/gradient/Gradient.d.ts.map +1 -0
  47. package/dts/chart/gradient/index.d.ts +2 -0
  48. package/dts/chart/gradient/index.d.ts.map +1 -0
  49. package/dts/chart/index.d.ts +4 -1
  50. package/dts/chart/index.d.ts.map +1 -1
  51. package/dts/chart/legend/DefaultLegendEntry.d.ts +5 -0
  52. package/dts/chart/legend/DefaultLegendEntry.d.ts.map +1 -0
  53. package/dts/chart/legend/DefaultLegendShape.d.ts +5 -0
  54. package/dts/chart/legend/DefaultLegendShape.d.ts.map +1 -0
  55. package/dts/chart/legend/Legend.d.ts +168 -0
  56. package/dts/chart/legend/Legend.d.ts.map +1 -0
  57. package/dts/chart/legend/index.d.ts +4 -0
  58. package/dts/chart/legend/index.d.ts.map +1 -0
  59. package/dts/chart/line/DefaultReferenceLineLabel.d.ts +9 -0
  60. package/dts/chart/line/DefaultReferenceLineLabel.d.ts.map +1 -0
  61. package/dts/chart/line/DottedLine.d.ts +13 -5
  62. package/dts/chart/line/DottedLine.d.ts.map +1 -1
  63. package/dts/chart/line/Line.d.ts +61 -27
  64. package/dts/chart/line/Line.d.ts.map +1 -1
  65. package/dts/chart/line/LineChart.d.ts +43 -9
  66. package/dts/chart/line/LineChart.d.ts.map +1 -1
  67. package/dts/chart/line/ReferenceLine.d.ts +68 -20
  68. package/dts/chart/line/ReferenceLine.d.ts.map +1 -1
  69. package/dts/chart/line/SolidLine.d.ts +8 -5
  70. package/dts/chart/line/SolidLine.d.ts.map +1 -1
  71. package/dts/chart/line/index.d.ts +1 -1
  72. package/dts/chart/line/index.d.ts.map +1 -1
  73. package/dts/chart/point/DefaultPointLabel.d.ts +10 -0
  74. package/dts/chart/point/DefaultPointLabel.d.ts.map +1 -0
  75. package/dts/chart/point/Point.d.ts +136 -0
  76. package/dts/chart/point/Point.d.ts.map +1 -0
  77. package/dts/chart/point/index.d.ts +3 -0
  78. package/dts/chart/point/index.d.ts.map +1 -0
  79. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts +38 -0
  80. package/dts/chart/scrubber/DefaultScrubberBeacon.d.ts.map +1 -0
  81. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts +12 -0
  82. package/dts/chart/scrubber/DefaultScrubberBeaconLabel.d.ts.map +1 -0
  83. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts +11 -0
  84. package/dts/chart/scrubber/DefaultScrubberLabel.d.ts.map +1 -0
  85. package/dts/chart/scrubber/Scrubber.d.ts +230 -42
  86. package/dts/chart/scrubber/Scrubber.d.ts.map +1 -1
  87. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts +54 -0
  88. package/dts/chart/scrubber/ScrubberBeaconGroup.d.ts.map +1 -0
  89. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts +46 -0
  90. package/dts/chart/scrubber/ScrubberBeaconLabelGroup.d.ts.map +1 -0
  91. package/dts/chart/scrubber/ScrubberProvider.d.ts +6 -3
  92. package/dts/chart/scrubber/ScrubberProvider.d.ts.map +1 -1
  93. package/dts/chart/scrubber/index.d.ts +3 -0
  94. package/dts/chart/scrubber/index.d.ts.map +1 -1
  95. package/dts/chart/text/ChartText.d.ts +151 -77
  96. package/dts/chart/text/ChartText.d.ts.map +1 -1
  97. package/dts/chart/text/{SmartChartTextGroup.d.ts → ChartTextGroup.d.ts} +9 -3
  98. package/dts/chart/text/ChartTextGroup.d.ts.map +1 -0
  99. package/dts/chart/text/index.d.ts +1 -1
  100. package/dts/chart/text/index.d.ts.map +1 -1
  101. package/dts/chart/utils/axis.d.ts +25 -1
  102. package/dts/chart/utils/axis.d.ts.map +1 -1
  103. package/dts/chart/utils/bar.d.ts +34 -0
  104. package/dts/chart/utils/bar.d.ts.map +1 -1
  105. package/dts/chart/utils/chart.d.ts +52 -7
  106. package/dts/chart/utils/chart.d.ts.map +1 -1
  107. package/dts/chart/utils/context.d.ts +28 -7
  108. package/dts/chart/utils/context.d.ts.map +1 -1
  109. package/dts/chart/utils/gradient.d.ts +117 -0
  110. package/dts/chart/utils/gradient.d.ts.map +1 -0
  111. package/dts/chart/utils/index.d.ts +3 -0
  112. package/dts/chart/utils/index.d.ts.map +1 -1
  113. package/dts/chart/utils/path.d.ts +59 -0
  114. package/dts/chart/utils/path.d.ts.map +1 -1
  115. package/dts/chart/utils/point.d.ts +71 -7
  116. package/dts/chart/utils/point.d.ts.map +1 -1
  117. package/dts/chart/utils/scale.d.ts +102 -0
  118. package/dts/chart/utils/scale.d.ts.map +1 -1
  119. package/dts/chart/utils/scrubber.d.ts +40 -0
  120. package/dts/chart/utils/scrubber.d.ts.map +1 -0
  121. package/dts/chart/utils/transition.d.ts +178 -0
  122. package/dts/chart/utils/transition.d.ts.map +1 -0
  123. package/esm/chart/CartesianChart.js +199 -75
  124. package/esm/chart/ChartContextBridge.js +159 -0
  125. package/esm/chart/ChartProvider.js +2 -2
  126. package/esm/chart/Path.js +200 -114
  127. package/esm/chart/PeriodSelector.js +7 -3
  128. package/esm/chart/__stories__/CartesianChart.stories.js +307 -134
  129. package/esm/chart/__stories__/ChartTransitions.stories.js +629 -0
  130. package/esm/chart/__stories__/PeriodSelector.stories.js +201 -75
  131. package/esm/chart/area/Area.js +27 -35
  132. package/esm/chart/area/AreaChart.js +17 -12
  133. package/esm/chart/area/DottedArea.js +64 -108
  134. package/esm/chart/area/GradientArea.js +37 -91
  135. package/esm/chart/area/SolidArea.js +24 -8
  136. package/esm/chart/area/__stories__/AreaChart.stories.js +1 -1
  137. package/esm/chart/axis/Axis.js +5 -39
  138. package/esm/chart/axis/DefaultAxisTickLabel.js +11 -0
  139. package/esm/chart/axis/XAxis.js +148 -66
  140. package/esm/chart/axis/YAxis.js +149 -65
  141. package/esm/chart/axis/__stories__/Axis.stories.js +259 -1
  142. package/esm/chart/axis/index.js +1 -0
  143. package/esm/chart/bar/Bar.js +7 -1
  144. package/esm/chart/bar/BarChart.js +17 -37
  145. package/esm/chart/bar/BarPlot.js +43 -35
  146. package/esm/chart/bar/BarStack.js +84 -37
  147. package/esm/chart/bar/BarStackGroup.js +7 -17
  148. package/esm/chart/bar/DefaultBar.js +29 -51
  149. package/esm/chart/bar/DefaultBarStack.js +34 -58
  150. package/esm/chart/bar/__stories__/BarChart.stories.js +948 -88
  151. package/esm/chart/gradient/Gradient.js +53 -0
  152. package/esm/chart/gradient/index.js +1 -0
  153. package/esm/chart/index.js +4 -1
  154. package/esm/chart/legend/DefaultLegendEntry.js +42 -0
  155. package/esm/chart/legend/DefaultLegendShape.js +64 -0
  156. package/esm/chart/legend/Legend.js +59 -0
  157. package/esm/chart/legend/__stories__/Legend.stories.js +574 -0
  158. package/esm/chart/legend/index.js +3 -0
  159. package/esm/chart/line/DefaultReferenceLineLabel.js +66 -0
  160. package/esm/chart/line/DottedLine.js +31 -14
  161. package/esm/chart/line/Line.js +96 -68
  162. package/esm/chart/line/LineChart.js +21 -14
  163. package/esm/chart/line/ReferenceLine.js +80 -63
  164. package/esm/chart/line/SolidLine.js +27 -10
  165. package/esm/chart/line/__stories__/LineChart.stories.js +1748 -2048
  166. package/esm/chart/line/__stories__/ReferenceLine.stories.js +177 -28
  167. package/esm/chart/line/index.js +1 -1
  168. package/esm/chart/point/DefaultPointLabel.js +39 -0
  169. package/esm/chart/point/Point.js +186 -0
  170. package/esm/chart/point/index.js +2 -0
  171. package/esm/chart/scrubber/DefaultScrubberBeacon.js +180 -0
  172. package/esm/chart/scrubber/DefaultScrubberBeaconLabel.js +43 -0
  173. package/esm/chart/scrubber/DefaultScrubberLabel.js +28 -0
  174. package/esm/chart/scrubber/Scrubber.js +130 -144
  175. package/esm/chart/scrubber/ScrubberBeaconGroup.js +165 -0
  176. package/esm/chart/scrubber/ScrubberBeaconLabelGroup.js +208 -0
  177. package/esm/chart/scrubber/ScrubberProvider.js +46 -54
  178. package/esm/chart/scrubber/__stories__/Scrubber.stories.js +760 -0
  179. package/esm/chart/scrubber/index.js +3 -1
  180. package/esm/chart/text/ChartText.js +242 -174
  181. package/esm/chart/text/{SmartChartTextGroup.js → ChartTextGroup.js} +6 -5
  182. package/esm/chart/text/index.js +1 -1
  183. package/esm/chart/utils/axis.js +47 -31
  184. package/esm/chart/utils/bar.js +43 -0
  185. package/esm/chart/utils/chart.js +57 -3
  186. package/esm/chart/utils/gradient.js +305 -0
  187. package/esm/chart/utils/index.js +3 -0
  188. package/esm/chart/utils/path.js +84 -8
  189. package/esm/chart/utils/point.js +171 -17
  190. package/esm/chart/utils/scale.js +242 -2
  191. package/esm/chart/utils/scrubber.js +146 -0
  192. package/esm/chart/utils/transition.js +220 -0
  193. package/esm/sparkline/__figma__/Sparkline.figma.js +1 -1
  194. package/esm/sparkline/__stories__/Sparkline.stories.js +11 -7
  195. package/esm/sparkline/__stories__/SparklineGradient.stories.js +7 -4
  196. package/esm/sparkline/sparkline-interactive/__figma__/SparklineInteractive.figma.js +1 -1
  197. package/esm/sparkline/sparkline-interactive/__stories__/SparklineInteractive.stories.js +51 -26
  198. package/esm/sparkline/sparkline-interactive-header/__figma__/SparklineInteractiveHeader.figma.js +1 -1
  199. package/esm/sparkline/sparkline-interactive-header/__stories__/SparklineInteractiveHeader.stories.js +17 -9
  200. package/package.json +15 -10
  201. package/dts/chart/Point.d.ts +0 -103
  202. package/dts/chart/Point.d.ts.map +0 -1
  203. package/dts/chart/line/GradientLine.d.ts +0 -45
  204. package/dts/chart/line/GradientLine.d.ts.map +0 -1
  205. package/dts/chart/scrubber/ScrubberBeacon.d.ts +0 -75
  206. package/dts/chart/scrubber/ScrubberBeacon.d.ts.map +0 -1
  207. package/dts/chart/text/SmartChartTextGroup.d.ts.map +0 -1
  208. package/esm/chart/Point.js +0 -111
  209. package/esm/chart/__stories__/Chart.stories.js +0 -79
  210. package/esm/chart/line/GradientLine.js +0 -62
  211. package/esm/chart/scrubber/ScrubberBeacon.js +0 -199
@@ -1,3 +1,46 @@
1
+ const _excluded = ["staggerDelay"];
2
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
3
+ 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; }
4
+ import { defaultTransition } from './transition';
5
+
6
+ /**
7
+ * A bar-specific transition that extends Transition with stagger support.
8
+ * When `staggerDelay` is provided, bars will animate with increasing delays
9
+ * based on their horizontal position (leftmost starts first, rightmost last).
10
+ *
11
+ * @example
12
+ * // Bars stagger in from left to right over 250ms, each animating for 750ms
13
+ * { type: 'timing', duration: 750, staggerDelay: 250 }
14
+ */
15
+
16
+ /**
17
+ * Strips `staggerDelay` from a transition and computes a positional delay.
18
+ *
19
+ * @param transition - The transition config (may include staggerDelay)
20
+ * @param normalizedX - The bar's normalized x position (0 = left edge, 1 = right edge)
21
+ * @returns A standard Transition with computed delay
22
+ */
23
+ export const withStaggerDelayTransition = (transition, normalizedX) => {
24
+ var _baseTransition$delay;
25
+ const {
26
+ staggerDelay
27
+ } = transition,
28
+ baseTransition = _objectWithoutPropertiesLoose(transition, _excluded);
29
+ if (!staggerDelay) return transition;
30
+ return _extends({}, baseTransition, {
31
+ delay: ((_baseTransition$delay = baseTransition == null ? void 0 : baseTransition.delay) != null ? _baseTransition$delay : 0) + normalizedX * staggerDelay
32
+ });
33
+ };
34
+
35
+ /**
36
+ * Default bar enter transition. Uses the default spring with a stagger delay
37
+ * so bars spring into place from left to right.
38
+ * `{ type: 'spring', stiffness: 900, damping: 120, staggerDelay: 250 }`
39
+ */
40
+ export const defaultBarEnterTransition = _extends({}, defaultTransition, {
41
+ staggerDelay: 250
42
+ });
43
+
1
44
  /**
2
45
  * Calculates the size adjustment needed for bars when accounting for gaps between them.
3
46
  * This function helps determine how much to reduce each bar's width to accommodate
@@ -1,5 +1,19 @@
1
+ import { isSharedValue } from 'react-native-reanimated';
1
2
  import { stack as d3Stack, stackOffsetDiverging, stackOrderNone } from 'd3-shape';
2
3
  export const defaultStackId = 'DEFAULT_STACK_ID';
4
+
5
+ /**
6
+ * Shape variants available for legend items.
7
+ */
8
+
9
+ /**
10
+ * Shape for legend items. Can be a preset variant or a custom ReactNode.
11
+ */
12
+
13
+ /**
14
+ * Position of the legend relative to the chart.
15
+ */
16
+
3
17
  /**
4
18
  * Type guard to check if bounds are complete with both min and max values.
5
19
  * @param bounds - The bounds to validate
@@ -19,13 +33,13 @@ export const getChartDomain = (series, min, max) => {
19
33
  return domain;
20
34
  }
21
35
  if (series.length > 0) {
22
- const maxDataLength = Math.max(...series.map(s => {
36
+ const dataLength = Math.max(...series.map(s => {
23
37
  var _s$data;
24
38
  return ((_s$data = s.data) == null ? void 0 : _s$data.length) || 0;
25
39
  }));
26
- if (maxDataLength > 0) {
40
+ if (dataLength > 0) {
27
41
  if (domain.min === undefined) domain.min = 0;
28
- if (domain.max === undefined) domain.max = maxDataLength - 1;
42
+ if (domain.max === undefined) domain.max = dataLength - 1;
29
43
  }
30
44
  }
31
45
  return domain;
@@ -111,6 +125,32 @@ export const getStackedSeriesData = series => {
111
125
  return stackedDataMap;
112
126
  };
113
127
 
128
+ /**
129
+ * Extracts line data values from series data that may contain tuples.
130
+ * For tuple data [[baseline, value]], extracts the last value.
131
+ * For numeric data [value], returns as-is.
132
+ *
133
+ * @param data - Array of numbers, tuples, or null values
134
+ * @returns Array of numbers or null values
135
+ */
136
+ export const getLineData = data => {
137
+ if (!data) return [];
138
+
139
+ // Check if this is tuple data by finding first non-null entry
140
+ const firstNonNull = data.find(d => d !== null);
141
+ if (Array.isArray(firstNonNull)) {
142
+ return data.map(d => {
143
+ var _d$at;
144
+ if (d === null) return null;
145
+ if (Array.isArray(d)) return (_d$at = d.at(-1)) != null ? _d$at : null;
146
+ return d;
147
+ });
148
+ }
149
+
150
+ // Already numeric data
151
+ return data;
152
+ };
153
+
114
154
  /**
115
155
  * Calculates the range of a chart from series data.
116
156
  * Range represents the range of y-values from the data.
@@ -226,4 +266,18 @@ export const getChartInset = (inset, defaults) => {
226
266
  bottom: (_inset$bottom = inset == null ? void 0 : inset.bottom) != null ? _inset$bottom : baseDefaults.bottom,
227
267
  right: (_inset$right = inset == null ? void 0 : inset.right) != null ? _inset$right : baseDefaults.right
228
268
  };
269
+ };
270
+
271
+ /**
272
+ * Unwraps an optionally animated value to get the raw value.
273
+ * @param value - The value to unwrap.
274
+ * @returns The raw value.
275
+ */
276
+ export const unwrapAnimatedValue = value => {
277
+ 'worklet';
278
+
279
+ if (isSharedValue(value)) {
280
+ return value.value;
281
+ }
282
+ return value;
229
283
  };
@@ -0,0 +1,305 @@
1
+ import { Skia } from '@shopify/react-native-skia';
2
+ import { applySerializableScale, isCategoricalScale, isSerializableScale } from './scale';
3
+
4
+ /**
5
+ * Defines a color transition point in the gradient
6
+ */
7
+
8
+ /**
9
+ * Defines a gradient.
10
+ */
11
+
12
+ /**
13
+ * Resolves gradient stops, handling both static arrays and function forms.
14
+ * When stops is a function, calls it with the domain bounds.
15
+ */
16
+ export const getGradientStops = (stops, domain) => {
17
+ if (typeof stops === 'function') {
18
+ return stops(domain);
19
+ }
20
+ return stops;
21
+ };
22
+
23
+ /**
24
+ * Processes Gradient to gradient configuration for SVG linearGradient.
25
+ * Colors are smoothly interpolated between stops by the browser.
26
+ * Multiple stops at the same offset create hard color transitions.
27
+ */
28
+ const processGradientStops = (stops, scale) => {
29
+ if (stops.length === 0) {
30
+ console.warn('Gradient has no stops - falling back to default');
31
+ return;
32
+ }
33
+
34
+ // Check if stops are in ascending order
35
+ const isOutOfOrder = stops.some((stop, i) => {
36
+ return i > 0 && stop.offset < stops[i - 1].offset;
37
+ });
38
+ if (isOutOfOrder) {
39
+ console.warn("Gradient: stop offsets must be in ascending order");
40
+ return;
41
+ }
42
+ const [rangeMin, rangeMax] = scale.range();
43
+ const rangeSpan = Math.abs(rangeMax - rangeMin);
44
+
45
+ // Convert data value offsets to normalized positions (0-1) using scale
46
+ const normalizedStops = stops.map(stop => {
47
+ var _stop$opacity;
48
+ const stopPosition = scale(stop.offset);
49
+ const normalized = stopPosition === undefined ? 0 : Math.max(0, Math.min(1, Math.abs(stopPosition - rangeMin) / rangeSpan));
50
+ return {
51
+ offset: normalized,
52
+ // Now 0-1 normalized (not data space)
53
+ color: stop.color,
54
+ opacity: (_stop$opacity = stop.opacity) != null ? _stop$opacity : 1
55
+ };
56
+ }).sort((a, b) => a.offset - b.offset);
57
+ return normalizedStops;
58
+ };
59
+
60
+ /**
61
+ * Interpolates between two colors using linear interpolation.
62
+ * Returns an rgba string.
63
+ */
64
+ const interpolateColor = (color1, color2, t) => {
65
+ 'worklet';
66
+
67
+ const c1 = Skia.Color(color1);
68
+ const c2 = Skia.Color(color2);
69
+ const r = Math.round((c1[0] + (c2[0] - c1[0]) * t) * 255);
70
+ const g = Math.round((c1[1] + (c2[1] - c1[1]) * t) * 255);
71
+ const b = Math.round((c1[2] + (c2[2] - c1[2]) * t) * 255);
72
+ const a = c1[3] + (c2[3] - c1[3]) * t;
73
+ return "rgba(" + r + ", " + g + ", " + b + ", " + a + ")";
74
+ };
75
+
76
+ /**
77
+ * Adds an opacity to a color
78
+ * Returns an rgba string.
79
+ */
80
+ export const getColorWithOpacity = (color1, opacity) => {
81
+ const c = Skia.Color(color1);
82
+ return "rgba(" + c[0] * 255 + ", " + c[1] * 255 + ", " + c[2] * 255 + ", " + opacity + ")";
83
+ };
84
+
85
+ /**
86
+ * Creates a gradient configuration for SVG components.
87
+ * Processes a GradientDefinition into a renderable GradientConfig.
88
+ * Supports both numeric scales (linear, log) and categorical scales (band).
89
+ *
90
+ * @param gradient - GradientDefinition configuration (required)
91
+ * @param xScale - X-axis scale (required)
92
+ * @param yScale - Y-axis scale (required)
93
+ * @returns GradientConfig or null if gradient processing fails
94
+ *
95
+ * @example
96
+ * const gradientConfig = useMemo(() => {
97
+ * if (!gradient || !xScale || !yScale) return;
98
+ * return getGradientConfig(gradient, xScale, yScale);
99
+ * }, [gradient, xScale, yScale]);
100
+ *
101
+ * if (gradientConfig) {
102
+ * return (
103
+ * <defs>
104
+ * <Gradient
105
+ * config={gradientConfig}
106
+ * direction={gradient.axis === 'x' ? 'horizontal' : 'vertical'}
107
+ * id={gradientId}
108
+ * />
109
+ * </defs>
110
+ * );
111
+ * }
112
+ */
113
+ export const getGradientConfig = (gradient, xScale, yScale) => {
114
+ if (!gradient) return;
115
+
116
+ // Get the scale based on axis
117
+ const scale = gradient.axis === 'x' ? xScale : yScale;
118
+ if (!scale) return;
119
+
120
+ // Extract domain from scale
121
+ const scaleDomain = scale.domain();
122
+ let domain;
123
+ if (isCategoricalScale(scale)) {
124
+ const domainArray = scaleDomain;
125
+ domain = {
126
+ min: domainArray[0],
127
+ max: domainArray[domainArray.length - 1]
128
+ };
129
+ } else {
130
+ const [min, max] = scaleDomain;
131
+ domain = {
132
+ min,
133
+ max
134
+ };
135
+ }
136
+ const resolvedStops = getGradientStops(gradient.stops, domain);
137
+ return processGradientStops(resolvedStops, scale);
138
+ };
139
+
140
+ /**
141
+ * Evaluates the color at a specific data value based on the gradient stops, ignoring opacity.
142
+ * @param stops - The gradient stops configuration
143
+ * @param dataValue - The data value to evaluate (for band scales, this is the index)
144
+ * @param scale - The scale to use for value mapping (handles log scales correctly)
145
+ * @returns The color string at this data value, or undefined if invalid
146
+ */
147
+ export const evaluateGradientAtValue = (stops, dataValue, scale) => {
148
+ 'worklet';
149
+
150
+ if (stops.length === 0) return;
151
+
152
+ // Determine range based on scale type
153
+ let rangeMin;
154
+ let rangeMax;
155
+ if (isSerializableScale(scale)) {
156
+ // SerializableScale has range as [number, number]
157
+ [rangeMin, rangeMax] = scale.range;
158
+ } else {
159
+ // ChartScaleFunction has range() method
160
+ const scaleRange = scale.range();
161
+ [rangeMin, rangeMax] = Array.isArray(scaleRange) ? scaleRange : [scaleRange, scaleRange]; // fallback for band scales
162
+ }
163
+ const rangeSpan = Math.abs(rangeMax - rangeMin);
164
+ if (rangeSpan === 0) return stops[0].color;
165
+
166
+ // Map dataValue through scale to get position
167
+ let dataPosition;
168
+ if (isSerializableScale(scale)) {
169
+ dataPosition = applySerializableScale(dataValue, scale);
170
+ } else {
171
+ const result = scale(dataValue);
172
+ if (result === undefined) return stops[0].color;
173
+ dataPosition = result;
174
+ }
175
+
176
+ // Normalize to 0-1 based on range
177
+ const normalizedValue = Math.max(0, Math.min(1, Math.abs(dataPosition - rangeMin) / rangeSpan));
178
+
179
+ // Map stop offsets through scale and normalize to 0-1
180
+ const positions = stops.map(stop => {
181
+ let stopPosition;
182
+ if (isSerializableScale(scale)) {
183
+ stopPosition = applySerializableScale(stop.offset, scale);
184
+ } else {
185
+ const result = scale(stop.offset);
186
+ if (result === undefined) return 0;
187
+ stopPosition = result;
188
+ }
189
+ return Math.max(0, Math.min(1, Math.abs(stopPosition - rangeMin) / rangeSpan));
190
+ });
191
+
192
+ // Find which segment we're in
193
+ if (normalizedValue < positions[0]) {
194
+ return stops[0].color;
195
+ }
196
+ if (normalizedValue >= positions[positions.length - 1]) {
197
+ return stops[stops.length - 1].color;
198
+ }
199
+
200
+ // Check if dataValue matches any stop offset exactly (for hard transitions)
201
+ for (let i = 0; i < stops.length; i++) {
202
+ if (dataValue === stops[i].offset) {
203
+ // Found exact match - check if there are multiple stops at this offset (hard transition)
204
+ // Use the LAST color at this offset for hard transitions
205
+ let lastIndexAtOffset = i;
206
+ while (lastIndexAtOffset + 1 < stops.length && stops[lastIndexAtOffset + 1].offset === stops[i].offset) {
207
+ lastIndexAtOffset++;
208
+ }
209
+ return stops[lastIndexAtOffset].color;
210
+ }
211
+ }
212
+
213
+ // Find the segment to interpolate between
214
+ for (let i = 0; i < positions.length - 1; i++) {
215
+ if (normalizedValue >= positions[i] && normalizedValue <= positions[i + 1]) {
216
+ const segmentStart = positions[i];
217
+ const segmentEnd = positions[i + 1];
218
+ if (segmentEnd === segmentStart) {
219
+ return stops[i].color;
220
+ }
221
+ const t = (normalizedValue - segmentStart) / (segmentEnd - segmentStart);
222
+ return interpolateColor(stops[i].color, stops[i + 1].color, t);
223
+ }
224
+ }
225
+ return stops[0].color;
226
+ };
227
+
228
+ /**
229
+ * Determines the baseline value for the gradient area by finding the value
230
+ * within the axis bounds that is closest to the target baseline.
231
+ *
232
+ * @param axisBounds - The min and max bounds of the axis
233
+ * @param baseline - The target baseline value (defaults to 0)
234
+ * @returns The value within bounds closest to the baseline
235
+ */
236
+ export const getBaseline = function (axisBounds, baseline) {
237
+ if (baseline === void 0) {
238
+ baseline = 0;
239
+ }
240
+ const {
241
+ min,
242
+ max
243
+ } = axisBounds;
244
+
245
+ // Normalize to ensure lowerBound <= upperBound
246
+ const lowerBound = Math.min(min, max);
247
+ const upperBound = Math.max(min, max);
248
+
249
+ // If baseline is within the range, use it
250
+ if (lowerBound <= baseline && baseline <= upperBound) return baseline;
251
+
252
+ // Otherwise, return the bound closest to baseline
253
+ return Math.abs(lowerBound - baseline) < Math.abs(upperBound - baseline) ? lowerBound : upperBound;
254
+ };
255
+
256
+ /**
257
+ * Generates a gradient definition for the area chart based on the axis bounds
258
+ * and styling parameters. Ensures gradient stops are in ascending order.
259
+ *
260
+ * @param axisBounds - The min and max bounds of the axis
261
+ * @param baselineValue - The baseline value for the gradient
262
+ * @param fill - The color to use for the gradient
263
+ * @param peakOpacity - Opacity at the peak of the gradient
264
+ * @param baselineOpacity - Opacity at the baseline
265
+ * @returns A gradient definition with y-axis stops in ascending order
266
+ */
267
+ export const createGradient = (axisBounds, baselineValue, fill, peakOpacity, baselineOpacity) => {
268
+ const {
269
+ min,
270
+ max
271
+ } = axisBounds;
272
+ const lowerBound = Math.min(min, max);
273
+ const upperBound = Math.max(min, max);
274
+ if (lowerBound < baselineValue && baselineValue < upperBound) {
275
+ return {
276
+ axis: 'y',
277
+ stops: [{
278
+ offset: lowerBound,
279
+ color: fill,
280
+ opacity: peakOpacity
281
+ }, {
282
+ offset: baselineValue,
283
+ color: fill,
284
+ opacity: baselineOpacity
285
+ }, {
286
+ offset: upperBound,
287
+ color: fill,
288
+ opacity: peakOpacity
289
+ }]
290
+ };
291
+ }
292
+ const peakValue = Math.abs(min - baselineValue) > Math.abs(max - baselineValue) ? min : max;
293
+ return {
294
+ axis: 'y',
295
+ stops: [{
296
+ offset: peakValue,
297
+ color: fill,
298
+ opacity: peakOpacity
299
+ }, {
300
+ offset: baselineValue,
301
+ color: fill,
302
+ opacity: baselineOpacity
303
+ }].sort((a, b) => a.offset - b.offset)
304
+ };
305
+ };
@@ -3,7 +3,10 @@ export * from './axis';
3
3
  export * from './bar';
4
4
  export * from './chart';
5
5
  export * from './context';
6
+ export * from './gradient';
6
7
  export * from './path';
7
8
  export * from './point';
8
9
  export * from './scale';
10
+ export * from './scrubber';
11
+ export * from './transition';
9
12
  // codegen:end
@@ -1,6 +1,14 @@
1
1
  import { area as d3Area, curveBumpX, curveCatmullRom, curveLinear, curveLinearClosed, curveMonotoneX, curveNatural, curveStep, curveStepAfter, curveStepBefore, line as d3Line } from 'd3-shape';
2
2
  import { projectPoint, projectPoints } from './point';
3
3
  import { isCategoricalScale } from './scale';
4
+ /**
5
+ * Default enter transition for path-based components (Line, Area).
6
+ * `{ type: 'timing', duration: 500 }`
7
+ */
8
+ export const defaultPathEnterTransition = {
9
+ type: 'timing',
10
+ duration: 500
11
+ };
4
12
  /**
5
13
  * Get the d3 curve function for a path.
6
14
  * See https://d3js.org/d3-shape/curve
@@ -49,10 +57,11 @@ export const getLinePath = _ref => {
49
57
  var _pathGenerator;
50
58
  let {
51
59
  data,
52
- curve = 'linear',
60
+ curve = 'bump',
53
61
  xScale,
54
62
  yScale,
55
- xData
63
+ xData,
64
+ connectNulls = false
56
65
  } = _ref;
57
66
  if (data.length === 0) {
58
67
  return '';
@@ -64,9 +73,12 @@ export const getLinePath = _ref => {
64
73
  yScale,
65
74
  xData
66
75
  });
67
- 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
68
76
 
69
- return (_pathGenerator = pathGenerator(dataPoints)) != null ? _pathGenerator : '';
77
+ // When connectNulls is true, filter out null values before rendering
78
+ // When false, use defined() to create gaps in the line
79
+ const filteredPoints = connectNulls ? dataPoints.filter(d => d !== null) : dataPoints;
80
+ const pathGenerator = d3Line().x(d => d.x).y(d => d.y).curve(curveFunction).defined(d => connectNulls || d !== null);
81
+ return (_pathGenerator = pathGenerator(filteredPoints)) != null ? _pathGenerator : '';
70
82
  };
71
83
 
72
84
  /**
@@ -94,10 +106,11 @@ export const getLinePath = _ref => {
94
106
  export const getAreaPath = _ref2 => {
95
107
  let {
96
108
  data,
97
- curve = 'linear',
109
+ curve = 'bump',
98
110
  xScale,
99
111
  yScale,
100
- xData
112
+ xData,
113
+ connectNulls = false
101
114
  } = _ref2;
102
115
  if (data.length === 0) {
103
116
  return '';
@@ -158,6 +171,10 @@ export const getAreaPath = _ref2 => {
158
171
  isValid: true
159
172
  };
160
173
  });
174
+
175
+ // When connectNulls is true, filter out invalid points before rendering
176
+ // When false, use defined() to create gaps in the area
177
+ const filteredPoints = connectNulls ? dataPoints.filter(d => d.isValid) : dataPoints;
161
178
  const areaGenerator = d3Area().x(d => d.x).y0(d => {
162
179
  var _d$low;
163
180
  return (_d$low = d.low) != null ? _d$low : 0;
@@ -166,12 +183,26 @@ export const getAreaPath = _ref2 => {
166
183
  var _d$high;
167
184
  return (_d$high = d.high) != null ? _d$high : 0;
168
185
  }) // Top boundary (high values), fallback to 0
169
- .curve(curveFunction).defined(d => d.isValid && d.low != null && d.high != null); // Only draw where both values exist
186
+ .curve(curveFunction).defined(d => connectNulls || d.isValid && d.low != null && d.high != null); // Only draw where both values exist
170
187
 
171
- const result = areaGenerator(dataPoints);
188
+ const result = areaGenerator(filteredPoints);
172
189
  return result != null ? result : '';
173
190
  };
174
191
 
192
+ /**
193
+ * Converts line coordinates to an SVG path string.
194
+ * Useful for rendering axis lines and tick marks.
195
+ *
196
+ * @example
197
+ * ```typescript
198
+ * const path = lineToPath(0, 0, 100, 100);
199
+ * // Returns: "M 0 0 L 100 100"
200
+ * ```
201
+ */
202
+ export const lineToPath = (x1, y1, x2, y2) => {
203
+ return "M " + x1 + " " + y1 + " L " + x2 + " " + y2;
204
+ };
205
+
175
206
  /**
176
207
  * Creates an SVG path string for a rectangle with selective corner rounding.
177
208
  * Useful for creating bars in charts with optional rounded corners.
@@ -203,4 +234,49 @@ export const getBarPath = (x, y, width, height, radius, roundTop, roundBottom) =
203
234
  path += " A " + topR + " " + topR + " 0 0 1 " + (x + topR) + " " + y;
204
235
  path += ' Z';
205
236
  return path;
237
+ };
238
+
239
+ /**
240
+ * Generates an SVG path string with circles arranged in a dotted pattern within a bounding area.
241
+ * Creates circles at regular intervals based on the pattern size and dot size parameters.
242
+ *
243
+ * @param bounds - The bounding rectangle to fill with dots
244
+ * @param patternSize - Size of the pattern unit (spacing between dots)
245
+ * @param dotSize - Radius of each dot
246
+ * @returns SVG path string containing all the circles
247
+ *
248
+ * @example
249
+ * ```typescript
250
+ * const dotsPath = getDottedAreaPath(
251
+ * { x: 0, y: 0, width: 100, height: 50 },
252
+ * 8, // 8px spacing
253
+ * 2 // 2px radius dots
254
+ * );
255
+ * ```
256
+ */
257
+ export const getDottedAreaPath = (bounds, patternSize, dotSize) => {
258
+ if (bounds.width <= 0 || bounds.height <= 0 || patternSize <= 0 || dotSize <= 0) {
259
+ return '';
260
+ }
261
+ let path = '';
262
+
263
+ // Calculate the number of dots that fit in each dimension
264
+ const dotsX = Math.ceil(bounds.width / patternSize);
265
+ const dotsY = Math.ceil(bounds.height / patternSize);
266
+
267
+ // Generate circles in a grid pattern
268
+ for (let row = 0; row < dotsY; row++) {
269
+ for (let col = 0; col < dotsX; col++) {
270
+ const centerX = bounds.x + col * patternSize + patternSize / 2;
271
+ const centerY = bounds.y + row * patternSize + patternSize / 2;
272
+
273
+ // Only draw dots that are within the bounds
274
+ if (centerX >= bounds.x && centerX <= bounds.x + bounds.width && centerY >= bounds.y && centerY <= bounds.y + bounds.height) {
275
+ // Create circle using SVG arc commands
276
+ // M cx,cy-r a r,r 0 1,0 0,2r a r,r 0 1,0 0,-2r
277
+ path += "M " + centerX + "," + (centerY - dotSize) + " a " + dotSize + "," + dotSize + " 0 1,0 0," + dotSize * 2 + " a " + dotSize + "," + dotSize + " 0 1,0 0," + -dotSize * 2 + " ";
278
+ }
279
+ }
280
+ }
281
+ return path.trim();
206
282
  };