@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,110 +1,54 @@
1
- const _excluded = ["d", "fill", "fillOpacity", "peakColor", "baselineColor", "peakOpacity", "baselineOpacity", "baseline", "yAxisId", "clipRect"];
1
+ const _excluded = ["d", "fill", "fillOpacity", "gradient", "peakOpacity", "baselineOpacity", "baseline", "yAxisId", "animate", "transition"];
2
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
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 { memo, useId } from 'react';
5
- import { Defs, LinearGradient, Stop } from 'react-native-svg';
4
+ import { memo, useMemo } from 'react';
6
5
  import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
7
6
  import { useCartesianChartContext } from '../ChartProvider';
7
+ import { Gradient } from '../gradient';
8
8
  import { Path } from '../Path';
9
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
9
+ import { createGradient, getBaseline } from '../utils';
10
+ import { jsx as _jsx } from "react/jsx-runtime";
10
11
  /**
11
- * A customizable gradient area component which uses Path.
12
+ * A customizable gradient area component.
13
+ * When no gradient is provided, renders a default gradient based
14
+ * on the fill color and peak/baseline opacities.
12
15
  */
13
16
  export const GradientArea = /*#__PURE__*/memo(_ref => {
14
17
  let {
15
18
  d,
16
- fill,
19
+ fill: fillProp,
17
20
  fillOpacity = 1,
18
- peakColor,
19
- baselineColor,
21
+ gradient: gradientProp,
20
22
  peakOpacity = 0.3,
21
23
  baselineOpacity = 0,
22
24
  baseline,
23
25
  yAxisId,
24
- clipRect
26
+ animate,
27
+ transition
25
28
  } = _ref,
26
29
  pathProps = _objectWithoutPropertiesLoose(_ref, _excluded);
27
- const context = useCartesianChartContext();
30
+ const {
31
+ getYAxis
32
+ } = useCartesianChartContext();
28
33
  const theme = useTheme();
29
- const patternId = useId();
30
-
31
- // Get the y-scale for the specified axis (or default)
32
- const yScale = context.getYScale(yAxisId);
33
- const yRange = yScale == null ? void 0 : yScale.range();
34
- const yDomain = yScale == null ? void 0 : yScale.domain();
35
-
36
- // Use chart range if available, otherwise fall back to percentage
37
- const useUserSpaceUnits = yRange !== undefined;
38
- const gradientY1 = useUserSpaceUnits && yRange ? String(yRange[1]) : '0%';
39
- const gradientY2 = useUserSpaceUnits && yRange ? String(yRange[0]) : '100%';
40
-
41
- // Auto-calculate baseline position based on domain
42
- let baselinePosition;
43
- let baselinePercentage;
44
- if (yScale && yDomain) {
45
- const [minValue, maxValue] = yDomain;
46
- let dataBaseline;
47
- if (minValue >= 0) {
48
- // All positive: baseline at min
49
- dataBaseline = minValue;
50
- } else if (maxValue <= 0) {
51
- // All negative: baseline at max
52
- dataBaseline = maxValue;
53
- } else {
54
- // Crosses zero: baseline at 0
55
- dataBaseline = 0;
56
- }
57
- if (useUserSpaceUnits && yRange) {
58
- // Get the actual y coordinate for the baseline
59
- const scaledValue = yScale(baseline != null ? baseline : dataBaseline);
60
- if (typeof scaledValue === 'number') {
61
- baselinePosition = scaledValue;
62
- }
63
- } else {
64
- // Calculate percentage position
65
- baselinePercentage = (maxValue - (baseline != null ? baseline : dataBaseline)) / (maxValue - minValue) * 100 + "%";
66
- }
67
- }
68
- const effectiveFill = fill != null ? fill : theme.color.fgPrimary;
69
- const effectivePeakColor = peakColor != null ? peakColor : effectiveFill;
70
- const effectiveBaselineColor = baselineColor != null ? baselineColor : effectiveFill;
71
- return /*#__PURE__*/_jsxs(_Fragment, {
72
- children: [/*#__PURE__*/_jsx(Defs, {
73
- children: /*#__PURE__*/_jsx(LinearGradient, {
74
- gradientUnits: useUserSpaceUnits ? 'userSpaceOnUse' : 'objectBoundingBox',
75
- id: patternId,
76
- x1: useUserSpaceUnits ? '0' : '0%',
77
- x2: useUserSpaceUnits ? '0' : '0%',
78
- y1: gradientY1,
79
- y2: gradientY2,
80
- children: baselinePosition !== undefined || baselinePercentage !== undefined ? /* Diverging gradient: peak opacity at extremes, baseline opacity at baseline */
81
- [/*#__PURE__*/_jsx(Stop, {
82
- offset: "0%",
83
- stopColor: effectivePeakColor,
84
- stopOpacity: peakOpacity
85
- }, "0"), /*#__PURE__*/_jsx(Stop, {
86
- offset: baselinePercentage != null ? baselinePercentage : (baselinePosition - yRange[1]) / (yRange[0] - yRange[1]) * 100 + "%",
87
- stopColor: effectiveBaselineColor,
88
- stopOpacity: baselineOpacity
89
- }, "1"), /*#__PURE__*/_jsx(Stop, {
90
- offset: "100%",
91
- stopColor: effectivePeakColor,
92
- stopOpacity: peakOpacity
93
- }, "2")] : /* Simple gradient from peak to baseline */
94
- [/*#__PURE__*/_jsx(Stop, {
95
- offset: "0%",
96
- stopColor: effectivePeakColor,
97
- stopOpacity: peakOpacity
98
- }, "0"), /*#__PURE__*/_jsx(Stop, {
99
- offset: "100%",
100
- stopColor: effectiveBaselineColor,
101
- stopOpacity: baselineOpacity
102
- }, "1")]
103
- })
104
- }), /*#__PURE__*/_jsx(Path, _extends({
105
- clipRect: clipRect,
106
- d: d,
107
- fill: "url(#" + patternId + ")"
108
- }, pathProps))]
109
- });
34
+ const yAxisConfig = getYAxis(yAxisId);
35
+ const fill = useMemo(() => fillProp != null ? fillProp : theme.color.fgPrimary, [fillProp, theme.color.fgPrimary]);
36
+ const gradient = useMemo(() => {
37
+ if (gradientProp) return gradientProp;
38
+ if (!yAxisConfig) return;
39
+ const baselineValue = getBaseline(yAxisConfig.domain, baseline);
40
+ return createGradient(yAxisConfig.domain, baselineValue, fill, peakOpacity, baselineOpacity);
41
+ }, [gradientProp, yAxisConfig, fill, baseline, peakOpacity, baselineOpacity]);
42
+ return /*#__PURE__*/_jsx(Path, _extends({
43
+ animate: animate,
44
+ d: d,
45
+ fill: fill,
46
+ fillOpacity: fillOpacity,
47
+ transition: transition
48
+ }, pathProps, {
49
+ children: gradient && /*#__PURE__*/_jsx(Gradient, {
50
+ gradient: gradient,
51
+ yAxisId: yAxisId
52
+ })
53
+ }));
110
54
  });
@@ -1,24 +1,38 @@
1
- const _excluded = ["d", "fill", "fillOpacity", "clipRect"];
1
+ const _excluded = ["d", "fill", "fillOpacity", "yAxisId", "animate", "transition", "gradient"];
2
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
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
4
  import { memo } from 'react';
5
+ import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
6
+ import { Gradient } from '../gradient';
5
7
  import { Path } from '../Path';
6
8
  import { jsx as _jsx } from "react/jsx-runtime";
7
9
  /**
8
- * A customizable solid area component which uses Path.
10
+ * A customizable solid area component.
11
+ * When a gradient is provided, renders with gradient fill.
12
+ * Otherwise, renders with solid fill.
9
13
  */
10
14
  export const SolidArea = /*#__PURE__*/memo(_ref => {
11
15
  let {
12
16
  d,
13
17
  fill,
14
18
  fillOpacity = 1,
15
- clipRect
19
+ yAxisId,
20
+ animate,
21
+ transition,
22
+ gradient
16
23
  } = _ref,
17
- props = _objectWithoutPropertiesLoose(_ref, _excluded);
24
+ pathProps = _objectWithoutPropertiesLoose(_ref, _excluded);
25
+ const theme = useTheme();
18
26
  return /*#__PURE__*/_jsx(Path, _extends({
19
- clipRect: clipRect,
27
+ animate: animate,
20
28
  d: d,
21
- fill: fill,
22
- fillOpacity: fillOpacity
23
- }, props));
29
+ fill: fill != null ? fill : theme.color.fgPrimary,
30
+ fillOpacity: fillOpacity,
31
+ transition: transition
32
+ }, pathProps, {
33
+ children: gradient && /*#__PURE__*/_jsx(Gradient, {
34
+ gradient: gradient,
35
+ yAxisId: yAxisId
36
+ })
37
+ }));
24
38
  });
@@ -64,7 +64,7 @@ const AreaChartStories = () => {
64
64
  id: 'pageViews',
65
65
  data: [24, 13, -98, 39, 48, 38, 43]
66
66
  }],
67
- type: "gradient",
67
+ type: "solid",
68
68
  yAxis: {
69
69
  showGrid: true
70
70
  },
@@ -1,43 +1,9 @@
1
- /**
2
- * Animation variants for grouped axis tick labels - initial mount
3
- */
4
- export const axisTickLabelsInitialAnimationVariants = {
5
- initial: {
6
- opacity: 0
7
- },
8
- animate: {
9
- opacity: 1,
10
- transition: {
11
- duration: 0,
12
- delay: 0
13
- }
14
- },
15
- exit: {
16
- opacity: 0,
17
- transition: {
18
- duration: 0.15
19
- }
20
- }
21
- };
1
+ import { accessoryFadeTransitionDuration } from '../utils';
22
2
 
23
3
  /**
24
- * Animation variants for axis elements - updates (used for both grid lines and tick labels)
4
+ * Animation transition for axis elements (grid lines, tick marks, tick labels).
5
+ * Matches web's axisUpdateAnimationTransition timing.
25
6
  */
26
- export const axisUpdateAnimationVariants = {
27
- initial: {
28
- opacity: 0
29
- },
30
- animate: {
31
- opacity: 1,
32
- transition: {
33
- duration: 0.15,
34
- delay: 0.15 // For updates: fade out 150ms, then fade in 150ms
35
- }
36
- },
37
- exit: {
38
- opacity: 0,
39
- transition: {
40
- duration: 0.15
41
- }
42
- }
7
+ export const axisUpdateAnimationTransition = {
8
+ duration: accessoryFadeTransitionDuration
43
9
  };
@@ -0,0 +1,11 @@
1
+ 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); }
2
+ import { memo } from 'react';
3
+ import { ChartText } from '../text';
4
+ import { jsx as _jsx } from "react/jsx-runtime";
5
+ /**
6
+ * DefaultAxisTickLabel is the default label component for axis tick labels.
7
+ * Provides standard styling for both X and Y axis tick labels.
8
+ */
9
+ export const DefaultAxisTickLabel = /*#__PURE__*/memo(props => {
10
+ return /*#__PURE__*/_jsx(ChartText, _extends({}, props));
11
+ });
@@ -1,17 +1,18 @@
1
- const _excluded = ["position", "showGrid", "requestedTickCount", "ticks", "tickLabelFormatter", "style", "className", "styles", "classNames", "GridLineComponent", "tickMarkLabelGap", "height", "minTickLabelGap", "showTickMarks", "showLine", "tickMarkSize", "tickInterval", "tickMinStep", "tickMaxStep"];
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); }
1
+ const _excluded = ["position", "showGrid", "requestedTickCount", "ticks", "tickLabelFormatter", "TickLabelComponent", "GridLineComponent", "LineComponent", "TickMarkLineComponent", "tickMarkLabelGap", "minTickLabelGap", "showTickMarks", "showLine", "tickMarkSize", "tickInterval", "tickMinStep", "tickMaxStep", "label", "labelGap", "height", "bandGridLinePlacement", "bandTickMarkPlacement"];
3
2
  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
3
  import { memo, useCallback, useEffect, useId, useMemo } from 'react';
5
- import Animated, { useAnimatedStyle, useSharedValue } from 'react-native-reanimated';
6
- import { G, Line } from 'react-native-svg';
7
4
  import { useTheme } from '@coinbase/cds-mobile/hooks/useTheme';
5
+ import { Group } from '@shopify/react-native-skia';
8
6
  import { useCartesianChartContext } from '../ChartProvider';
9
7
  import { DottedLine } from '../line/DottedLine';
10
- import { ReferenceLine } from '../line/ReferenceLine';
11
- import { SmartChartTextGroup } from '../text/SmartChartTextGroup';
12
- import { getAxisTicksData, isCategoricalScale } from '../utils';
8
+ import { SolidLine } from '../line/SolidLine';
9
+ import { ChartText } from '../text/ChartText';
10
+ import { ChartTextGroup } from '../text/ChartTextGroup';
11
+ import { getAxisTicksData, getPointOnScale, isCategoricalScale, lineToPath, toPointAnchor } from '../utils';
12
+ import { DefaultAxisTickLabel } from './DefaultAxisTickLabel';
13
13
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
- const AnimatedG = Animated.createAnimatedComponent(G);
14
+ const AXIS_HEIGHT = 32;
15
+ const LABEL_SIZE = 20;
15
16
  export const XAxis = /*#__PURE__*/memo(_ref => {
16
17
  let {
17
18
  position = 'bottom',
@@ -19,24 +20,30 @@ export const XAxis = /*#__PURE__*/memo(_ref => {
19
20
  requestedTickCount,
20
21
  ticks,
21
22
  tickLabelFormatter,
22
- styles,
23
- classNames,
23
+ TickLabelComponent = DefaultAxisTickLabel,
24
24
  GridLineComponent = DottedLine,
25
+ LineComponent = SolidLine,
26
+ TickMarkLineComponent = SolidLine,
25
27
  tickMarkLabelGap = 2,
26
- height = 32,
27
28
  minTickLabelGap = 4,
28
29
  showTickMarks,
29
30
  showLine,
30
31
  tickMarkSize = 4,
31
32
  tickInterval = 32,
32
33
  tickMinStep = 1,
33
- tickMaxStep
34
+ tickMaxStep,
35
+ label,
36
+ labelGap = 4,
37
+ height = label ? AXIS_HEIGHT + LABEL_SIZE : AXIS_HEIGHT,
38
+ bandGridLinePlacement = 'edges',
39
+ bandTickMarkPlacement = 'middle'
34
40
  } = _ref,
35
41
  props = _objectWithoutPropertiesLoose(_ref, _excluded);
36
42
  const theme = useTheme();
37
43
  const registrationId = useId();
38
44
  const {
39
45
  animate,
46
+ drawingArea,
40
47
  getXScale,
41
48
  getXAxis,
42
49
  registerAxis,
@@ -46,17 +53,6 @@ export const XAxis = /*#__PURE__*/memo(_ref => {
46
53
  const xScale = getXScale();
47
54
  const xAxis = getXAxis();
48
55
  const axisBounds = getAxisBounds(registrationId);
49
- const gridOpacity = useSharedValue(1);
50
- const axisLineProps = useMemo(() => ({
51
- stroke: theme.color.fg,
52
- strokeLinecap: 'square',
53
- strokeWidth: 1
54
- }), [theme.color.fg]);
55
- const axisTickMarkProps = useMemo(() => ({
56
- stroke: theme.color.fg,
57
- strokeLinecap: 'square',
58
- strokeWidth: 1
59
- }), [theme.color.fg]);
60
56
  useEffect(() => {
61
57
  registerAxis(registrationId, position, height);
62
58
  return () => unregisterAxis(registrationId);
@@ -114,68 +110,154 @@ export const XAxis = /*#__PURE__*/memo(_ref => {
114
110
  }
115
111
  });
116
112
  }, [ticks, xScale, requestedTickCount, tickInterval, tickMinStep, tickMaxStep, xAxis == null ? void 0 : xAxis.data]);
113
+ const isBandScale = useMemo(() => {
114
+ if (!xScale) return false;
115
+ return isCategoricalScale(xScale);
116
+ }, [xScale]);
117
+
118
+ // Compute grid line positions (including bounds closing line for band scales)
119
+ const gridLinePositions = useMemo(() => {
120
+ if (!xScale) return [];
121
+ return ticksData.flatMap((tick, index) => {
122
+ if (!isBandScale) {
123
+ return [{
124
+ x: tick.position,
125
+ key: "grid-" + tick.tick + "-" + index
126
+ }];
127
+ }
128
+ const bandScale = xScale;
129
+ const isLastTick = index === ticksData.length - 1;
130
+ const isEdges = bandGridLinePlacement === 'edges';
131
+ const startX = getPointOnScale(tick.tick, bandScale, toPointAnchor(bandGridLinePlacement));
132
+ const positions = [{
133
+ x: startX,
134
+ key: "grid-" + tick.tick + "-" + index
135
+ }];
136
+
137
+ // For edges on last tick, add the closing line at stepEnd
138
+ if (isLastTick && isEdges) {
139
+ const endX = getPointOnScale(tick.tick, bandScale, 'stepEnd');
140
+ positions.push({
141
+ x: endX,
142
+ key: "grid-" + tick.tick + "-" + index + "-end"
143
+ });
144
+ }
145
+ return positions;
146
+ });
147
+ }, [ticksData, xScale, isBandScale, bandGridLinePlacement]);
148
+
149
+ // Compute tick mark positions (including bounds closing tick for band scales)
150
+ const tickMarkPositions = useMemo(() => {
151
+ if (!xScale) return [];
152
+ return ticksData.flatMap((tick, index) => {
153
+ if (!isBandScale) {
154
+ return [{
155
+ x: tick.position,
156
+ key: "tick-mark-" + tick.tick + "-" + index
157
+ }];
158
+ }
159
+ const bandScale = xScale;
160
+ const isLastTick = index === ticksData.length - 1;
161
+ const isEdges = bandTickMarkPlacement === 'edges';
162
+ const startX = getPointOnScale(tick.tick, bandScale, toPointAnchor(bandTickMarkPlacement));
163
+ const positions = [{
164
+ x: startX,
165
+ key: "tick-mark-" + tick.tick + "-" + index
166
+ }];
167
+
168
+ // For edges on last tick, add the closing tick mark at stepEnd
169
+ if (isLastTick && isEdges) {
170
+ const endX = getPointOnScale(tick.tick, bandScale, 'stepEnd');
171
+ positions.push({
172
+ x: endX,
173
+ key: "tick-mark-" + tick.tick + "-" + index + "-end"
174
+ });
175
+ }
176
+ return positions;
177
+ });
178
+ }, [ticksData, xScale, isBandScale, bandTickMarkPlacement]);
117
179
  const chartTextData = useMemo(() => {
118
180
  if (!axisBounds) return null;
119
181
  return ticksData.map(tick => {
120
182
  const tickOffset = tickMarkLabelGap + (showTickMarks ? tickMarkSize : 0);
121
- const availableSpace = axisBounds.height - tickOffset;
183
+
184
+ // Use AXIS_HEIGHT for centering, not full axisBounds.height
185
+ // This ensures tick labels are centered in the axis area, not including label space
186
+ const availableSpace = AXIS_HEIGHT - tickOffset;
122
187
  const labelOffset = availableSpace / 2;
123
- const labelY = position === 'top' ? axisBounds.y + labelOffset - tickOffset : axisBounds.y + labelOffset + tickOffset;
188
+ const labelY = position === 'top' ? axisBounds.y + axisBounds.height - tickOffset - labelOffset : axisBounds.y + labelOffset + tickOffset;
124
189
  return {
125
190
  x: tick.position,
126
191
  y: labelY,
127
192
  label: String(formatTick(tick.tick)),
128
193
  chartTextProps: {
129
- className: classNames == null ? void 0 : classNames.tickLabel,
130
194
  color: theme.color.fgMuted,
131
195
  verticalAlignment: 'middle',
132
- style: styles == null ? void 0 : styles.tickLabel,
133
196
  horizontalAlignment: 'center'
134
197
  }
135
198
  };
136
199
  });
137
- }, [axisBounds, ticksData, theme.color.fgMuted, tickMarkLabelGap, showTickMarks, tickMarkSize, position, formatTick, classNames == null ? void 0 : classNames.tickLabel, styles == null ? void 0 : styles.tickLabel]);
138
- const gridAnimatedStyle = useAnimatedStyle(() => ({
139
- opacity: gridOpacity.value
140
- }));
141
- if (!xScale) return;
142
- return /*#__PURE__*/_jsxs(G, _extends({
143
- "data-axis": "x",
144
- "data-position": position
145
- }, props, {
146
- children: [showGrid && /*#__PURE__*/_jsx(AnimatedG, {
147
- animatedProps: gridAnimatedStyle,
148
- children: ticksData.map((tick, index) => {
149
- const verticalLine = /*#__PURE__*/_jsx(ReferenceLine, {
150
- LineComponent: GridLineComponent,
151
- dataX: tick.tick
152
- });
153
- return /*#__PURE__*/_jsx(G, {
154
- children: verticalLine
155
- }, "grid-" + tick.tick + "-" + index);
200
+ }, [axisBounds, ticksData, theme.color.fgMuted, tickMarkLabelGap, showTickMarks, tickMarkSize, position, formatTick]);
201
+ if (!xScale || !axisBounds) return;
202
+ const labelX = axisBounds.x + axisBounds.width / 2;
203
+ const labelY = position === 'bottom' ? axisBounds.y + axisBounds.height - LABEL_SIZE / 2 : axisBounds.y + LABEL_SIZE / 2;
204
+
205
+ // Pre-compute tick mark Y coordinates
206
+ const tickYTop = axisBounds.y;
207
+ const tickYBottom = axisBounds.y + axisBounds.height;
208
+ const tickYStart = position === 'bottom' ? tickYTop : tickYBottom;
209
+ const tickYEnd = position === 'bottom' ? tickYTop + tickMarkSize : tickYBottom - tickMarkSize;
210
+
211
+ // Note: Unlike web, mobile renders grid lines and tick marks immediately without fade animation.
212
+ // This is because Skia can measure text dimensions synchronously, so there's no need to hide
213
+ // elements while waiting for measurements (web uses async ResizeObserver).
214
+ return /*#__PURE__*/_jsxs(Group, {
215
+ children: [showGrid && /*#__PURE__*/_jsx(Group, {
216
+ children: gridLinePositions.map(_ref2 => {
217
+ let {
218
+ x,
219
+ key
220
+ } = _ref2;
221
+ return /*#__PURE__*/_jsx(GridLineComponent, {
222
+ animate: false,
223
+ clipPath: null,
224
+ d: lineToPath(x, drawingArea.y, x, drawingArea.y + drawingArea.height),
225
+ stroke: theme.color.bgLine
226
+ }, key);
156
227
  })
157
- }), chartTextData && /*#__PURE__*/_jsx(SmartChartTextGroup, {
228
+ }), chartTextData && /*#__PURE__*/_jsx(ChartTextGroup, {
158
229
  prioritizeEndLabels: true,
230
+ LabelComponent: TickLabelComponent,
159
231
  labels: chartTextData,
160
232
  minGap: minTickLabelGap
161
- }), axisBounds && showTickMarks && /*#__PURE__*/_jsx(G, {
162
- "data-testid": "tick-marks",
163
- children: ticksData.map((tick, index) => {
164
- const tickY = position === 'bottom' ? axisBounds.y : axisBounds.y + axisBounds.height;
165
- const tickMarkSizePixels = tickMarkSize;
166
- const tickY2 = position === 'bottom' ? axisBounds.y + tickMarkSizePixels : axisBounds.y + axisBounds.height - tickMarkSizePixels;
167
- return /*#__PURE__*/_jsx(Line, _extends({}, axisTickMarkProps, {
168
- x1: tick.position,
169
- x2: tick.position,
170
- y1: tickY,
171
- y2: tickY2
172
- }), "tick-mark-" + tick.tick + "-" + index);
233
+ }), axisBounds && showTickMarks && /*#__PURE__*/_jsx(Group, {
234
+ children: tickMarkPositions.map(_ref3 => {
235
+ let {
236
+ x,
237
+ key
238
+ } = _ref3;
239
+ return /*#__PURE__*/_jsx(TickMarkLineComponent, {
240
+ animate: false,
241
+ clipPath: null,
242
+ d: lineToPath(x, tickYStart, x, tickYEnd),
243
+ stroke: theme.color.fg,
244
+ strokeCap: "square",
245
+ strokeWidth: 1
246
+ }, key);
173
247
  })
174
- }), axisBounds && showLine && /*#__PURE__*/_jsx(Line, _extends({}, axisLineProps, {
175
- x1: axisBounds.x,
176
- x2: axisBounds.x + axisBounds.width,
177
- y1: position === 'bottom' ? axisBounds.y : axisBounds.y + axisBounds.height,
178
- y2: position === 'bottom' ? axisBounds.y : axisBounds.y + axisBounds.height
179
- }))]
180
- }));
248
+ }), showLine && /*#__PURE__*/_jsx(LineComponent, {
249
+ animate: false,
250
+ clipPath: null,
251
+ d: lineToPath(axisBounds.x, position === 'bottom' ? axisBounds.y : axisBounds.y + axisBounds.height, axisBounds.x + axisBounds.width, position === 'bottom' ? axisBounds.y : axisBounds.y + axisBounds.height),
252
+ stroke: theme.color.fg,
253
+ strokeCap: "square",
254
+ strokeWidth: 1
255
+ }), label && /*#__PURE__*/_jsx(ChartText, {
256
+ horizontalAlignment: "center",
257
+ verticalAlignment: "middle",
258
+ x: labelX,
259
+ y: labelY,
260
+ children: label
261
+ })]
262
+ });
181
263
  });