@coinbase/cds-mcp-server 8.21.8 → 8.22.2

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 (153) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/mcp-docs/mobile/components/AreaChart.txt +39 -37
  3. package/mcp-docs/mobile/components/Avatar.txt +18 -18
  4. package/mcp-docs/mobile/components/AvatarButton.txt +19 -19
  5. package/mcp-docs/mobile/components/Banner.txt +62 -23
  6. package/mcp-docs/mobile/components/BarChart.txt +37 -35
  7. package/mcp-docs/mobile/components/Box.txt +18 -18
  8. package/mcp-docs/mobile/components/BrowserBar.txt +18 -18
  9. package/mcp-docs/mobile/components/Button.txt +19 -19
  10. package/mcp-docs/mobile/components/Carousel.txt +18 -18
  11. package/mcp-docs/mobile/components/CartesianChart.txt +75 -44
  12. package/mcp-docs/mobile/components/CheckboxCell.txt +19 -19
  13. package/mcp-docs/mobile/components/Chip.txt +20 -20
  14. package/mcp-docs/mobile/components/Coachmark.txt +18 -18
  15. package/mcp-docs/mobile/components/ContentCard.txt +18 -18
  16. package/mcp-docs/mobile/components/ContentCardBody.txt +18 -18
  17. package/mcp-docs/mobile/components/ContentCardFooter.txt +18 -18
  18. package/mcp-docs/mobile/components/ContentCardHeader.txt +18 -18
  19. package/mcp-docs/mobile/components/ContentCell.txt +18 -18
  20. package/mcp-docs/mobile/components/ControlGroup.txt +18 -18
  21. package/mcp-docs/mobile/components/DatePicker.txt +1 -1
  22. package/mcp-docs/mobile/components/Divider.txt +18 -18
  23. package/mcp-docs/mobile/components/DotCount.txt +1 -1
  24. package/mcp-docs/mobile/components/DotSymbol.txt +2 -2
  25. package/mcp-docs/mobile/components/Fallback.txt +18 -18
  26. package/mcp-docs/mobile/components/HStack.txt +18 -18
  27. package/mcp-docs/mobile/components/Icon.txt +6 -0
  28. package/mcp-docs/mobile/components/IconButton.txt +19 -19
  29. package/mcp-docs/mobile/components/InputChip.txt +20 -20
  30. package/mcp-docs/mobile/components/Interactable.txt +19 -19
  31. package/mcp-docs/mobile/components/LineChart.txt +1608 -898
  32. package/mcp-docs/mobile/components/Link.txt +18 -18
  33. package/mcp-docs/mobile/components/ListCell.txt +37 -19
  34. package/mcp-docs/mobile/components/Lottie.txt +18 -18
  35. package/mcp-docs/mobile/components/MediaChip.txt +20 -20
  36. package/mcp-docs/mobile/components/MultiContentModule.txt +18 -18
  37. package/mcp-docs/mobile/components/NavigationTitle.txt +18 -18
  38. package/mcp-docs/mobile/components/NavigationTitleSelect.txt +18 -18
  39. package/mcp-docs/mobile/components/Numpad.txt +18 -18
  40. package/mcp-docs/mobile/components/Overlay.txt +18 -18
  41. package/mcp-docs/mobile/components/PageFooter.txt +17 -17
  42. package/mcp-docs/mobile/components/PageHeader.txt +17 -17
  43. package/mcp-docs/mobile/components/PeriodSelector.txt +26 -26
  44. package/mcp-docs/mobile/components/Point.txt +203 -98
  45. package/mcp-docs/mobile/components/Pressable.txt +19 -19
  46. package/mcp-docs/mobile/components/ProgressBar.txt +1 -1
  47. package/mcp-docs/mobile/components/ProgressBarWithFixedLabels.txt +1 -1
  48. package/mcp-docs/mobile/components/ProgressBarWithFloatLabel.txt +1 -1
  49. package/mcp-docs/mobile/components/ProgressCircle.txt +1 -1
  50. package/mcp-docs/mobile/components/RadioCell.txt +19 -19
  51. package/mcp-docs/mobile/components/ReferenceLine.txt +197 -54
  52. package/mcp-docs/mobile/components/RollingNumber.txt +18 -18
  53. package/mcp-docs/mobile/components/Scrubber.txt +597 -79
  54. package/mcp-docs/mobile/components/SegmentedTabs.txt +18 -18
  55. package/mcp-docs/mobile/components/SelectAlpha.txt +1 -1
  56. package/mcp-docs/mobile/components/SelectChip.txt +20 -20
  57. package/mcp-docs/mobile/components/SlideButton.txt +19 -19
  58. package/mcp-docs/mobile/components/Spacer.txt +6 -6
  59. package/mcp-docs/mobile/components/SparklineInteractive.txt +3 -3
  60. package/mcp-docs/mobile/components/Spinner.txt +1 -1
  61. package/mcp-docs/mobile/components/Stepper.txt +18 -18
  62. package/mcp-docs/mobile/components/TabLabel.txt +18 -18
  63. package/mcp-docs/mobile/components/TabNavigation.txt +18 -18
  64. package/mcp-docs/mobile/components/TabbedChips.txt +18 -18
  65. package/mcp-docs/mobile/components/TabbedChipsAlpha.txt +1 -1
  66. package/mcp-docs/mobile/components/Tabs.txt +18 -18
  67. package/mcp-docs/mobile/components/Tag.txt +18 -18
  68. package/mcp-docs/mobile/components/Text.txt +18 -18
  69. package/mcp-docs/mobile/components/Toast.txt +18 -18
  70. package/mcp-docs/mobile/components/Tooltip.txt +17 -1
  71. package/mcp-docs/mobile/components/TopNavBar.txt +18 -18
  72. package/mcp-docs/mobile/components/VStack.txt +18 -18
  73. package/mcp-docs/mobile/components/XAxis.txt +86 -24
  74. package/mcp-docs/mobile/components/YAxis.txt +75 -17
  75. package/mcp-docs/mobile/routes.txt +1 -1
  76. package/mcp-docs/web/components/AreaChart.txt +523 -301
  77. package/mcp-docs/web/components/Avatar.txt +27 -27
  78. package/mcp-docs/web/components/AvatarButton.txt +28 -28
  79. package/mcp-docs/web/components/Banner.txt +71 -32
  80. package/mcp-docs/web/components/BarChart.txt +182 -313
  81. package/mcp-docs/web/components/Box.txt +28 -28
  82. package/mcp-docs/web/components/Button.txt +28 -28
  83. package/mcp-docs/web/components/Calendar.txt +27 -27
  84. package/mcp-docs/web/components/Carousel.txt +27 -27
  85. package/mcp-docs/web/components/CartesianChart.txt +62 -309
  86. package/mcp-docs/web/components/CheckboxCell.txt +25 -25
  87. package/mcp-docs/web/components/Chip.txt +27 -27
  88. package/mcp-docs/web/components/Coachmark.txt +27 -27
  89. package/mcp-docs/web/components/ContainedAssetCard.txt +27 -27
  90. package/mcp-docs/web/components/ContentCard.txt +28 -28
  91. package/mcp-docs/web/components/ContentCardBody.txt +28 -28
  92. package/mcp-docs/web/components/ContentCardFooter.txt +28 -28
  93. package/mcp-docs/web/components/ContentCardHeader.txt +28 -28
  94. package/mcp-docs/web/components/ContentCell.txt +28 -28
  95. package/mcp-docs/web/components/ControlGroup.txt +27 -27
  96. package/mcp-docs/web/components/Divider.txt +27 -27
  97. package/mcp-docs/web/components/Fallback.txt +28 -28
  98. package/mcp-docs/web/components/FloatingAssetCard.txt +27 -27
  99. package/mcp-docs/web/components/Grid.txt +28 -28
  100. package/mcp-docs/web/components/GridColumn.txt +27 -27
  101. package/mcp-docs/web/components/HStack.txt +28 -28
  102. package/mcp-docs/web/components/Icon.txt +27 -27
  103. package/mcp-docs/web/components/IconButton.txt +28 -28
  104. package/mcp-docs/web/components/InputChip.txt +27 -27
  105. package/mcp-docs/web/components/Interactable.txt +28 -28
  106. package/mcp-docs/web/components/LineChart.txt +1598 -1116
  107. package/mcp-docs/web/components/Link.txt +28 -28
  108. package/mcp-docs/web/components/ListCell.txt +48 -30
  109. package/mcp-docs/web/components/Lottie.txt +27 -27
  110. package/mcp-docs/web/components/MediaChip.txt +27 -27
  111. package/mcp-docs/web/components/Modal.txt +27 -27
  112. package/mcp-docs/web/components/ModalBody.txt +27 -27
  113. package/mcp-docs/web/components/ModalFooter.txt +27 -27
  114. package/mcp-docs/web/components/ModalHeader.txt +27 -27
  115. package/mcp-docs/web/components/MultiContentModule.txt +28 -28
  116. package/mcp-docs/web/components/NavigationBar.txt +5 -5
  117. package/mcp-docs/web/components/NudgeCard.txt +27 -27
  118. package/mcp-docs/web/components/Overlay.txt +27 -27
  119. package/mcp-docs/web/components/PageFooter.txt +26 -26
  120. package/mcp-docs/web/components/PageHeader.txt +26 -26
  121. package/mcp-docs/web/components/Pagination.txt +27 -27
  122. package/mcp-docs/web/components/PeriodSelector.txt +49 -49
  123. package/mcp-docs/web/components/Point.txt +228 -79
  124. package/mcp-docs/web/components/Pressable.txt +28 -28
  125. package/mcp-docs/web/components/RadioCell.txt +25 -25
  126. package/mcp-docs/web/components/ReferenceLine.txt +208 -60
  127. package/mcp-docs/web/components/RemoteImage.txt +26 -26
  128. package/mcp-docs/web/components/RollingNumber.txt +28 -28
  129. package/mcp-docs/web/components/Scrubber.txt +463 -68
  130. package/mcp-docs/web/components/SectionHeader.txt +27 -27
  131. package/mcp-docs/web/components/SegmentedTabs.txt +27 -27
  132. package/mcp-docs/web/components/SelectChip.txt +27 -27
  133. package/mcp-docs/web/components/SelectOption.txt +27 -27
  134. package/mcp-docs/web/components/Sidebar.txt +27 -27
  135. package/mcp-docs/web/components/SidebarItem.txt +27 -27
  136. package/mcp-docs/web/components/Spacer.txt +34 -34
  137. package/mcp-docs/web/components/SparklineInteractive.txt +1 -1
  138. package/mcp-docs/web/components/Spinner.txt +27 -27
  139. package/mcp-docs/web/components/Stepper.txt +27 -27
  140. package/mcp-docs/web/components/TabLabel.txt +27 -27
  141. package/mcp-docs/web/components/TabNavigation.txt +26 -26
  142. package/mcp-docs/web/components/TabbedChips.txt +26 -26
  143. package/mcp-docs/web/components/TabbedChipsAlpha.txt +1 -1
  144. package/mcp-docs/web/components/Tabs.txt +27 -27
  145. package/mcp-docs/web/components/Tag.txt +27 -27
  146. package/mcp-docs/web/components/Text.txt +28 -28
  147. package/mcp-docs/web/components/TileButton.txt +28 -28
  148. package/mcp-docs/web/components/Toast.txt +27 -27
  149. package/mcp-docs/web/components/Tooltip.txt +17 -1
  150. package/mcp-docs/web/components/VStack.txt +28 -28
  151. package/mcp-docs/web/components/XAxis.txt +86 -22
  152. package/mcp-docs/web/components/YAxis.txt +133 -89
  153. package/package.json +1 -1
@@ -10,405 +10,150 @@ import { LineChart } from '@coinbase/cds-web-visualization'
10
10
 
11
11
  ## Examples
12
12
 
13
- ### Basic Example
13
+ LineChart is a wrapper for [CartesianChart](/components/graphs/CartesianChart) that makes it easy to create standard line charts, supporting a single x/y axis pair. Charts are built using SVGs.
14
14
 
15
- ```jsx live
16
- function BasicExample() {
17
- const [scrubIndex, setScrubIndex] = useState(undefined);
18
- const data = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];
19
-
20
- const accessibilityLabel = useMemo(() => {
21
- if (scrubIndex === undefined) return undefined;
22
- return `Value: ${data[scrubIndex]} at index ${scrubIndex}`;
23
- }, [scrubIndex, data]);
24
-
25
- return (
26
- <LineChart
27
- enableScrubbing
28
- onScrubberPositionChange={setScrubIndex}
29
- height={{ base: 150, tablet: 200, desktop: 250 }}
30
- series={[
31
- {
32
- id: 'prices',
33
- data: data,
34
- },
35
- ]}
36
- curve="monotone"
37
- showYAxis
38
- showArea
39
- yAxis={{
40
- showGrid: true,
41
- }}
42
- accessibilityLabel={accessibilityLabel}
43
- >
44
- <Scrubber />
45
- </LineChart>
46
- );
47
- }
48
- ```
15
+ ### Basics
49
16
 
50
- ### Simple
17
+ The only prop required is `series`, which takes an array of series objects. Each series object needs an `id` and a `data` array of numbers.
51
18
 
52
19
  ```jsx live
53
20
  <LineChart
54
- height={{ base: 150, tablet: 200, desktop: 250 }}
21
+ showArea
22
+ height={{ base: 200, tablet: 225, desktop: 250 }}
55
23
  series={[
56
24
  {
57
25
  id: 'prices',
58
26
  data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58],
59
27
  },
60
28
  ]}
61
- curve="monotone"
62
29
  />
63
30
  ```
64
31
 
65
- ### Compact
66
-
67
- You can specify the dimensions of the chart to make it more compact.
68
-
69
- ```jsx live
70
- function CompactLineChart() {
71
- const dimensions = { width: 62, height: 18 };
72
-
73
- const sparklineData = prices
74
- .map((price) => parseFloat(price))
75
- .filter((price, index) => index % 10 === 0);
76
- const positiveFloor = Math.min(...sparklineData) - 10;
77
-
78
- const negativeData = sparklineData.map((price) => -1 * price).reverse();
79
- const negativeCeiling = Math.max(...negativeData) + 10;
80
-
81
- const formatPrice = useCallback((price: number) => {
82
- return `$${price.toLocaleString('en-US', {
83
- minimumFractionDigits: 2,
84
- maximumFractionDigits: 2,
85
- })}`;
86
- }, []);
87
-
88
- const CompactChart = memo(({ data, showArea, color, referenceY }) => (
89
- <Box style={{ padding: 1 }}>
90
- <LineChart
91
- {...dimensions}
92
- enableScrubbing={false}
93
- overflow="visible"
94
- inset={0}
95
- showArea={showArea}
96
- series={[
97
- {
98
- id: 'btc',
99
- data,
100
- color,
101
- },
102
- ]}
103
- >
104
- <ReferenceLine dataY={referenceY} />
105
- </LineChart>
106
- </Box>
107
- ));
108
-
109
- const ChartCell = memo(({ data, showArea, color, referenceY, subdetail, variant }) => {
110
- const { isPhone } = useBreakpoints();
111
-
112
- return (
113
- <ListCell
114
- spacingVariant="condensed"
115
- description={isPhone ? undefined : assets.btc.symbol}
116
- detail={formatPrice(parseFloat(prices[0]))}
117
- intermediary={
118
- <CompactChart data={data} showArea={showArea} color={color} referenceY={referenceY} />
119
- }
120
- media={<Avatar src={assets.btc.imageUrl} />}
121
- onClick={() => console.log('clicked')}
122
- subdetail={subdetail}
123
- title={isPhone ? undefined : assets.btc.name}
124
- variant={variant}
125
- style={{ padding: 0 }}
126
- />
127
- );
128
- });
129
-
130
- return (
131
- <VStack>
132
- <ChartCell
133
- data={sparklineData}
134
- color={assets.btc.color}
135
- referenceY={parseFloat(prices[Math.floor(prices.length / 4)])}
136
- subdetail="-4.55%"
137
- variant="negative"
138
- />
139
- <ChartCell
140
- data={sparklineData}
141
- showArea
142
- color={assets.btc.color}
143
- referenceY={parseFloat(prices[Math.floor(prices.length / 4)])}
144
- subdetail="-4.55%"
145
- variant="negative"
146
- />
147
- <ChartCell
148
- data={sparklineData}
149
- showArea
150
- color="var(--color-fgPositive)"
151
- referenceY={positiveFloor}
152
- subdetail="+0.25%"
153
- variant="positive"
154
- />
155
- <ChartCell
156
- data={negativeData}
157
- showArea
158
- color="var(--color-fgNegative)"
159
- referenceY={negativeCeiling}
160
- subdetail="-4.55%"
161
- variant="negative"
162
- />
163
- </VStack>
164
- );
165
- };
166
- ```
167
-
168
- ### Gain/Loss
169
-
170
- You can use the y-axis scale and a [linearGradient](https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/linearGradient) to create a gain/loss chart.
32
+ LineChart also supports multiple lines, interaction, and axes.
33
+ Other props, such as `areaType` can be applied to the chart as a whole or per series.
171
34
 
172
35
  ```jsx live
173
- function GainLossChart() {
174
- const gradientId = useId();
175
-
176
- const data = [-40, -28, -21, -5, 48, -5, -28, 2, -29, -46, 16, -30, -29, 8];
177
-
178
- const priceFormatter = useCallback(
179
- (value) =>
180
- new Intl.NumberFormat('en-US', {
181
- style: 'currency',
182
- currency: 'USD',
183
- maximumFractionDigits: 0,
184
- }).format(value),
36
+ function MultipleLine() {
37
+ const pages = useMemo(
38
+ () => ['Page A', 'Page B', 'Page C', 'Page D', 'Page E', 'Page F', 'Page G'],
185
39
  [],
186
40
  );
41
+ const pageViews = useMemo(() => [2400, 1398, 9800, 3908, 4800, 3800, 4300], []);
42
+ const uniqueVisitors = useMemo(() => [4000, 3000, 2000, 2780, 1890, 2390, 3490], []);
187
43
 
188
- const ChartDefs = ({ threshold = 0 }) => {
189
- const { getYScale } = useCartesianChartContext();
190
- // get the default y-axis scale
191
- const yScale = getYScale();
192
-
193
- if (yScale) {
194
- const domain = yScale.domain();
195
- const range = yScale.range();
44
+ const chartAccessibilityLabel = `Website visitors across ${pageViews.length} pages.`;
196
45
 
197
- const baselinePercentage = ((threshold - domain[0]) / (domain[1] - domain[0])) * 100;
198
-
199
- const negativeColor = 'rgb(var(--gray15))';
200
- const positiveColor = 'var(--color-fgPositive)';
201
-
202
- return (
203
- <defs>
204
- <linearGradient
205
- gradientUnits="userSpaceOnUse"
206
- id={`${gradientId}-solid`}
207
- x1="0%"
208
- x2="0%"
209
- y1={range[0]}
210
- y2={range[1]}
211
- >
212
- <stop offset="0%" stopColor={negativeColor} />
213
- <stop offset={`${baselinePercentage}%`} stopColor={negativeColor} />
214
- <stop offset={`${baselinePercentage}%`} stopColor={positiveColor} />
215
- <stop offset="100%" stopColor={positiveColor} />
216
- </linearGradient>
217
- <linearGradient
218
- gradientUnits="userSpaceOnUse"
219
- id={`${gradientId}-gradient`}
220
- x1="0%"
221
- x2="0%"
222
- y1={range[0]}
223
- y2={range[1]}
224
- >
225
- <stop offset="0%" stopColor={negativeColor} stopOpacity={0.3} />
226
- <stop offset={`${baselinePercentage}%`} stopColor={negativeColor} stopOpacity={0} />
227
- <stop offset={`${baselinePercentage}%`} stopColor={positiveColor} stopOpacity={0} />
228
- <stop offset="100%" stopColor={positiveColor} stopOpacity={0.3} />
229
- </linearGradient>
230
- </defs>
231
- );
232
- }
233
-
234
- return null;
235
- };
236
-
237
- const solidColor = `url(#${gradientId}-solid)`;
238
-
239
- return (
240
- <CartesianChart
241
- enableScrubbing
242
- height={{ base: 150, tablet: 200, desktop: 250 }}
243
- series={[
244
- {
245
- id: 'prices',
246
- data: data,
247
- color: solidColor,
248
- },
249
- ]}
250
- padding={{ top: 1.5, bottom: 1.5, left: 2, right: 0 }}
251
- >
252
- <ChartDefs />
253
- <YAxis requestedTickCount={2} showGrid tickLabelFormatter={priceFormatter} />
254
- <Area seriesId="prices" curve="monotone" fill={`url(#${gradientId}-gradient)`} />
255
- <Line strokeWidth={3} curve="monotone" seriesId="prices" stroke={solidColor} />
256
- <Scrubber hideOverlay />
257
- </CartesianChart>
46
+ const scrubberAccessibilityLabel = useCallback(
47
+ (index: number) => {
48
+ return `${pages[index]} has ${pageViews[index]} views and ${uniqueVisitors[index]} unique visitors.`;
49
+ },
50
+ [pages, pageViews, uniqueVisitors],
258
51
  );
259
- }
260
- ```
261
-
262
- ### Multiple Series
263
-
264
- You can add multiple series to a line chart.
265
-
266
- ```jsx live
267
- function MultipleSeriesChart() {
268
- const [scrubIndex, setScrubIndex] = useState(undefined);
269
52
 
270
- const prices = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];
271
- const volume = [4, 8, 11, 15, 16, 14, 16, 10, 12, 14, 16, 14, 16, 10];
53
+ const numberFormatter = useCallback(
54
+ (value: number) => new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(value),
55
+ [],
56
+ );
272
57
 
273
58
  return (
274
59
  <LineChart
275
60
  enableScrubbing
276
- height={{ base: 150, tablet: 200, desktop: 250 }}
61
+ showArea
62
+ showXAxis
63
+ showYAxis
64
+ accessibilityLabel={chartAccessibilityLabel}
65
+ height={{ base: 200, tablet: 225, desktop: 250 }}
277
66
  series={[
278
67
  {
279
- id: 'prices',
280
- data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58],
281
- label: 'Prices',
282
- color: 'var(--color-accentBoldBlue)',
68
+ id: 'pageViews',
69
+ data: pageViews,
70
+ color: 'var(--color-accentBoldGreen)',
71
+ // Label will render next to scrubber beacon
72
+ label: 'Page Views',
283
73
  },
284
74
  {
285
- id: 'volume',
286
- data: [4, 8, 11, 15, 16, 14, 16, 10, 12, 14, 16, 14, 16, 10],
287
- label: 'Volume',
288
- color: 'var(--color-accentBoldGreen)',
75
+ id: 'uniqueVisitors',
76
+ data: uniqueVisitors,
77
+ color: 'var(--color-accentBoldPurple)',
78
+ label: 'Unique Visitors',
79
+ // Default area is gradient
80
+ areaType: 'dotted',
289
81
  },
290
82
  ]}
291
- showYAxis
83
+ xAxis={{
84
+ // Used on the x-axis to provide context for each index from the series data array
85
+ data: pages,
86
+ }}
292
87
  yAxis={{
293
- domain: {
294
- min: 0,
295
- },
296
88
  showGrid: true,
89
+ tickLabelFormatter: numberFormatter,
297
90
  }}
298
- curve="monotone"
299
91
  >
300
- <Scrubber />
92
+ <Scrubber accessibilityLabel={scrubberAccessibilityLabel} />
301
93
  </LineChart>
302
94
  );
303
95
  }
304
96
  ```
305
97
 
306
- ### Points
98
+ ### Data
307
99
 
308
- You can use the `renderPoints` prop to dynamically show points on a line.
100
+ The data array for each series defines the y values for that series. You can adjust the y values for a series of data by setting the `data` prop on the xAxis.
309
101
 
310
102
  ```jsx live
311
- function PointsChart() {
312
- const keyMarketShiftIndices = [4, 6, 7, 9, 10];
313
- const data = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];
103
+ function DataFormat() {
104
+ const yData = useMemo(() => [2, 5.5, 2, 8.5, 1.5, 5], []);
105
+ const xData = useMemo(() => [1, 2, 3, 5, 8, 10], []);
106
+
107
+ const chartAccessibilityLabel = `Chart with custom X and Y data. ${yData.length} data points`;
108
+
109
+ const scrubberAccessibilityLabel = useCallback(
110
+ (index: number) => {
111
+ return `Point ${index + 1}: X value ${xData[index]}, Y value ${yData[index]}`;
112
+ },
113
+ [xData, yData],
114
+ );
314
115
 
315
116
  return (
316
- <CartesianChart
317
- height={{ base: 150, tablet: 200, desktop: 250 }}
117
+ <LineChart
118
+ enableScrubbing
119
+ showArea
120
+ showXAxis
121
+ showYAxis
122
+ accessibilityLabel={chartAccessibilityLabel}
123
+ curve="natural"
124
+ height={{ base: 200, tablet: 225, desktop: 250 }}
125
+ inset={{ top: 16, right: 16, bottom: 0, left: 0 }}
126
+ points
318
127
  series={[
319
128
  {
320
- id: 'prices',
321
- data: data,
129
+ id: 'line',
130
+ data: yData,
322
131
  },
323
132
  ]}
133
+ xAxis={{ data: xData, showLine: true, showTickMarks: true, showGrid: true }}
134
+ yAxis={{
135
+ domain: { min: 0 },
136
+ position: 'left',
137
+ showLine: true,
138
+ showTickMarks: true,
139
+ showGrid: true,
140
+ }}
324
141
  >
325
- <Area seriesId="prices" curve="monotone" fill="rgb(var(--blue5))" />
326
- <Line
327
- seriesId="prices"
328
- renderPoints={({ dataX, dataY, ...props }) =>
329
- keyMarketShiftIndices.includes(dataX)
330
- ? {
331
- ...props,
332
- strokeWidth: 2,
333
- stroke: 'var(--color-bg)',
334
- radius: 5,
335
- onClick: () =>
336
- alert(
337
- `You have clicked a key market shift at position ${dataX + 1} with value ${dataY}!`,
338
- ),
339
- accessibilityLabel: `Key market shift point at position ${dataX + 1}, value ${dataY}. Click to view details.`,
340
- }
341
- : false
342
- }
343
- curve="monotone"
344
- />
345
- </CartesianChart>
142
+ <Scrubber hideOverlay accessibilityLabel={scrubberAccessibilityLabel} />
143
+ </LineChart>
346
144
  );
347
145
  }
348
146
  ```
349
147
 
350
- ### Empty State
351
-
352
- This example shows how to use an empty state for a line chart.
353
-
354
- ```jsx live
355
- <LineChart
356
- series={[
357
- {
358
- id: 'line',
359
- color: 'rgb(var(--gray50))',
360
- data: [1, 1],
361
- showArea: true,
362
- },
363
- ]}
364
- yAxis={{ domain: { min: -1, max: 3 } }}
365
- height={{ base: 150, tablet: 200, desktop: 250 }}
366
- />
367
- ```
368
-
369
- ### Line Styles
148
+ #### Live Updates
370
149
 
371
- ```jsx live
372
- <LineChart
373
- height={{ base: 150, tablet: 200, desktop: 250 }}
374
- series={[
375
- {
376
- id: 'top',
377
- data: [15, 28, 32, 44, 46, 36, 40, 45, 48, 38],
378
- },
379
- {
380
- id: 'upperMiddle',
381
- data: [12, 23, 21, 29, 34, 28, 31, 38, 42, 35],
382
- color: '#ef4444',
383
- type: 'dotted',
384
- },
385
- {
386
- id: 'lowerMiddle',
387
- data: [8, 15, 14, 25, 20, 18, 22, 28, 24, 30],
388
- color: '#f59e0b',
389
- curve: 'natural',
390
- LineComponent: (props) => (
391
- <GradientLine {...props} endColor="#F7931A" startColor="#E3D74D" strokeWidth={4} />
392
- ),
393
- },
394
- {
395
- id: 'bottom',
396
- data: [4, 8, 11, 15, 16, 14, 16, 10, 12, 14],
397
- color: '#800080',
398
- curve: 'step',
399
- AreaComponent: DottedArea,
400
- showArea: true,
401
- },
402
- ]}
403
- />
404
- ```
150
+ You can change the data passed in via `series` prop to update the chart.
405
151
 
406
- ### Live Data
152
+ You can also use the `useRef` hook to reference the scrubber and pulse it on each update.
407
153
 
408
154
  ```jsx live
409
- function LiveAssetPrice() {
410
- const scrubberRef = useRef(null);
411
- const [scrubIndex, setScrubIndex] = useState(undefined);
155
+ function LiveUpdates() {
156
+ const scrubberRef = useRef<ScrubberRef>(null);
412
157
 
413
158
  const initialData = useMemo(() => {
414
159
  return sparklineInteractiveData.hour.map((d) => d.value);
@@ -461,19 +206,25 @@ function LiveAssetPrice() {
461
206
  return () => clearInterval(priceUpdateInterval);
462
207
  }, [intervalSeconds, maxPercentChange]);
463
208
 
464
- const accessibilityLabel = useMemo(() => {
465
- if (scrubIndex === undefined)
466
- return `Bitcoin Price: $${priceData[priceData.length - 1].toFixed(2)}`;
467
- const price = priceData[scrubIndex];
468
- return `Bitcoin Price: $${price.toFixed(2)} at position ${scrubIndex + 1}`;
469
- }, [scrubIndex, priceData]);
209
+ const chartAccessibilityLabel = useMemo(() => {
210
+ return `Live Bitcoin price chart. Current price: $${priceData[priceData.length - 1].toFixed(2)}`;
211
+ }, [priceData]);
212
+
213
+ const scrubberAccessibilityLabel = useCallback(
214
+ (index: number) => {
215
+ const price = priceData[index];
216
+ return `Bitcoin price at position ${index + 1}: $${price.toFixed(2)}`;
217
+ },
218
+ [priceData],
219
+ );
470
220
 
471
221
  return (
472
222
  <LineChart
473
223
  enableScrubbing
474
- onScrubberPositionChange={setScrubIndex}
475
224
  showArea
476
- height={{ base: 150, tablet: 200, desktop: 250 }}
225
+ accessibilityLabel={chartAccessibilityLabel}
226
+ height={{ base: 200, tablet: 225, desktop: 250 }}
227
+ inset={{ right: 64 }}
477
228
  series={[
478
229
  {
479
230
  id: 'btc',
@@ -481,154 +232,1030 @@ function LiveAssetPrice() {
481
232
  color: assets.btc.color,
482
233
  },
483
234
  ]}
484
- inset={{ right: 64 }}
485
- accessibilityLabel={accessibilityLabel}
486
235
  >
487
- <Scrubber ref={scrubberRef} labelProps={{ elevation: 1 }} />
236
+ <Scrubber
237
+ ref={scrubberRef}
238
+ accessibilityLabel={scrubberAccessibilityLabel}
239
+ />
488
240
  </LineChart>
489
241
  );
490
242
  }
491
243
  ```
492
244
 
493
- ### Data Format
245
+ #### Missing Data
494
246
 
495
- You can adjust the y values for a series of data by setting the `data` prop on the xAxis.
247
+ By default, null values in data create gaps in a line. Use `connectNulls` to skip null values and draw a continuous line.
248
+ Note that scrubber beacons and points are still only shown at non-null data values.
496
249
 
497
250
  ```jsx live
498
- function DataFormatChart() {
499
- const [scrubIndex, setScrubIndex] = useState(undefined);
500
-
501
- const yData = [2, 5.5, 2, 8.5, 1.5, 5];
502
- const xData = [1, 2, 3, 5, 8, 10];
251
+ function MissingData() {
252
+ const pages = ['Page A', 'Page B', 'Page C', 'Page D', 'Page E', 'Page F', 'Page G'];
253
+ const pageViews = [2400, 1398, null, 3908, 4800, 3800, 4300];
254
+ const uniqueVisitors = [4000, 3000, null, 2780, 1890, 2390, 3490];
503
255
 
504
- const accessibilityLabel = useMemo(() => {
505
- if (scrubIndex === undefined) return undefined;
506
- return `X: ${xData[scrubIndex]}, Y: ${yData[scrubIndex]} at point ${scrubIndex + 1}`;
507
- }, [scrubIndex, xData, yData]);
256
+ const numberFormatter = useCallback(
257
+ (value: number) => new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(value),
258
+ [],
259
+ );
508
260
 
509
261
  return (
510
262
  <LineChart
511
263
  enableScrubbing
512
- onScrubberPositionChange={setScrubIndex}
264
+ showArea
265
+ showXAxis
266
+ showYAxis
267
+ height={{ base: 200, tablet: 225, desktop: 250 }}
268
+ // You can render points at every valid data point by always returning true
269
+ points
513
270
  series={[
514
271
  {
515
- id: 'line',
516
- data: yData,
272
+ id: 'pageViews',
273
+ data: pageViews,
274
+ color: 'var(--color-accentBoldGreen)',
275
+ // Label will render next to scrubber beacon
276
+ label: 'Page Views',
277
+ connectNulls: true,
278
+ },
279
+ {
280
+ id: 'uniqueVisitors',
281
+ data: uniqueVisitors,
282
+ color: 'var(--color-accentBoldPurple)',
283
+ label: 'Unique Visitors',
517
284
  },
518
285
  ]}
519
- height={{ base: 150, tablet: 200, desktop: 250 }}
286
+ xAxis={{
287
+ // Used on the x-axis to provide context for each index from the series data array
288
+ data: pages,
289
+ }}
290
+ yAxis={{
291
+ showGrid: true,
292
+ tickLabelFormatter: numberFormatter,
293
+ }}
294
+ >
295
+ {/* We can offset the overlay to account for the points being drawn on the lines */}
296
+ <Scrubber overlayOffset={6} />
297
+ </LineChart>
298
+ );
299
+ }
300
+ ```
301
+
302
+ ##### Empty State
303
+
304
+ ```jsx live
305
+ <LineChart
306
+ height={{ base: 200, tablet: 225, desktop: 250 }}
307
+ series={[
308
+ {
309
+ id: 'line',
310
+ color: 'rgb(var(--gray50))',
311
+ data: [1, 1],
312
+ showArea: true,
313
+ },
314
+ ]}
315
+ yAxis={{ domain: { min: -1, max: 3 } }}
316
+ />
317
+ ```
318
+
319
+ #### Scales
320
+
321
+ LineChart uses `linear` scaling on axes by default, but you can also use other types, such as `log`. See [XAxis](/components/graphs/XAxis) and [YAxis](/components/graphs/YAxis) for more information.
322
+
323
+ ```jsx live
324
+ <LineChart
325
+ showArea
326
+ showYAxis
327
+ height={{ base: 200, tablet: 225, desktop: 250 }}
328
+ series={[
329
+ {
330
+ id: 'prices',
331
+ data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58],
332
+ },
333
+ ]}
334
+ yAxis={{
335
+ scaleType: 'log',
336
+ showGrid: true,
337
+ ticks: [1, 10, 100],
338
+ }}
339
+ />
340
+ ```
341
+
342
+ ### Interaction
343
+
344
+ Charts have built in functionality enabled through scrubbing, which can be used by setting `enableScrubbing` to true. You can listen to value changes through `onScrubberPositionChange`. Adding `Scrubber` to LineChart showcases the current scrubber position.
345
+
346
+ ```jsx live
347
+ function Interaction() {
348
+ const [scrubberPosition, setScrubberPosition] = useState<number | undefined>();
349
+
350
+ return (
351
+ <VStack gap={2}>
352
+ <Text font="label1">
353
+ {scrubberPosition !== undefined
354
+ ? `Scrubber position: ${scrubberPosition}`
355
+ : 'Not scrubbing'}
356
+ </Text>
357
+ <LineChart
358
+ enableScrubbing
359
+ showArea
360
+ height={{ base: 200, tablet: 225, desktop: 250 }}
361
+ onScrubberPositionChange={setScrubberPosition}
362
+ series={[
363
+ {
364
+ id: 'prices',
365
+ data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58],
366
+ },
367
+ ]}
368
+ >
369
+ <Scrubber />
370
+ </LineChart>
371
+ </VStack>
372
+ );
373
+ }
374
+ ```
375
+
376
+ #### Points
377
+
378
+ You can use `points` from LineChart with `onClick` listeners to render instances of [Point](/components/graphs/Point) that are interactable.
379
+
380
+ ```jsx live
381
+ function Points() {
382
+ const keyMarketShiftIndices = [4, 6, 7, 9, 10];
383
+ const data = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];
384
+
385
+ return (
386
+ <CartesianChart
387
+ height={{ base: 200, tablet: 225, desktop: 250 }}
388
+ series={[
389
+ {
390
+ id: 'prices',
391
+ data: data,
392
+ },
393
+ ]}
394
+ >
395
+ <Area fill="rgb(var(--blue5))" seriesId="prices" />
396
+ <Line
397
+ points={({ dataX, dataY, ...props }) =>
398
+ keyMarketShiftIndices.includes(dataX)
399
+ ? {
400
+ ...props,
401
+ strokeWidth: 2,
402
+ stroke: 'var(--color-bg)',
403
+ radius: 5,
404
+ onClick: () =>
405
+ alert(
406
+ `You have clicked a key market shift at position ${dataX + 1} with value ${dataY}!`,
407
+ ),
408
+ accessibilityLabel: `Key market shift point at position ${dataX + 1}, value ${dataY}. Click to view details.`,
409
+ }
410
+ : false
411
+ }
412
+ seriesId="prices"
413
+ />
414
+ </CartesianChart>
415
+ );
416
+ }
417
+ ```
418
+
419
+ ### Animations
420
+
421
+ You can configure chart transitions using `transition` on LineChart and `beaconTransitions` on [Scrubber](/components/graphs/Scrubber). You can also disable animations by setting the `animate` on LineChart to `false`.
422
+
423
+ ```jsx live
424
+ function Transitions() {
425
+ const dataCount = 20;
426
+ const maxDataOffset = 15000;
427
+ const minStepOffset = 2500;
428
+ const maxStepOffset = 10000;
429
+ const domainLimit = 20000;
430
+ const updateInterval = 500;
431
+
432
+ const myTransitionConfig = { type: 'spring', stiffness: 700, damping: 20 };
433
+ const negativeColor = 'rgb(var(--gray15))';
434
+ const positiveColor = 'var(--color-fgPositive)';
435
+
436
+ function generateNextValue(previousValue: number) {
437
+ const range = maxStepOffset - minStepOffset;
438
+ const offset = Math.random() * range + minStepOffset;
439
+
440
+ let direction;
441
+ if (previousValue >= maxDataOffset) {
442
+ direction = -1;
443
+ } else if (previousValue <= -maxDataOffset) {
444
+ direction = 1;
445
+ } else {
446
+ direction = Math.random() < 0.5 ? -1 : 1;
447
+ }
448
+
449
+ let newValue = previousValue + offset * direction;
450
+ newValue = Math.max(-maxDataOffset, Math.min(maxDataOffset, newValue));
451
+ return newValue;
452
+ }
453
+
454
+ function generateInitialData() {
455
+ const data = [];
456
+
457
+ let previousValue = Math.random() * 2 * maxDataOffset - maxDataOffset;
458
+ data.push(previousValue);
459
+
460
+ for (let i = 1; i < dataCount; i++) {
461
+ const newValue = generateNextValue(previousValue);
462
+ data.push(newValue);
463
+ previousValue = newValue;
464
+ }
465
+
466
+ return data;
467
+ }
468
+
469
+ const MyGradient = memo((props: DottedAreaProps) => {
470
+ const areaGradient = {
471
+ stops: ({ min, max }: AxisBounds) => [
472
+ { offset: min, color: negativeColor, opacity: 1 },
473
+ { offset: 0, color: negativeColor, opacity: 0 },
474
+ { offset: 0, color: positiveColor, opacity: 0 },
475
+ { offset: max, color: positiveColor, opacity: 1 },
476
+ ],
477
+ };
478
+
479
+ return <DottedArea {...props} gradient={areaGradient} />;
480
+ });
481
+
482
+ function CustomTransitionsChart() {
483
+ const [data, setData] = useState(generateInitialData);
484
+
485
+ useEffect(() => {
486
+ const intervalId = setInterval(() => {
487
+ setData((currentData) => {
488
+ const lastValue = currentData[currentData.length - 1] ?? 0;
489
+ const newValue = generateNextValue(lastValue);
490
+
491
+ return [...currentData.slice(1), newValue];
492
+ });
493
+ }, updateInterval);
494
+
495
+ return () => clearInterval(intervalId);
496
+ }, []);
497
+
498
+ const tickLabelFormatter = useCallback(
499
+ (value: number) =>
500
+ new Intl.NumberFormat('en-US', {
501
+ style: 'currency',
502
+ currency: 'USD',
503
+ maximumFractionDigits: 0,
504
+ }).format(value),
505
+ [],
506
+ );
507
+
508
+ const valueAtIndexFormatter = useCallback(
509
+ (dataIndex: number) =>
510
+ new Intl.NumberFormat('en-US', {
511
+ style: 'currency',
512
+ currency: 'USD',
513
+ }).format(data[dataIndex]),
514
+ [data],
515
+ );
516
+
517
+ const lineGradient = {
518
+ stops: [
519
+ { offset: 0, color: negativeColor },
520
+ { offset: 0, color: positiveColor },
521
+ ],
522
+ };
523
+
524
+ return (
525
+ <CartesianChart
526
+ enableScrubbing
527
+ height={{ base: 200, tablet: 250, desktop: 300 }}
528
+ inset={{ top: 32, bottom: 32, left: 16, right: 16 }}
529
+ series={[
530
+ {
531
+ id: 'prices',
532
+ data: data,
533
+ gradient: lineGradient,
534
+ },
535
+ ]}
536
+ yAxis={{ domain: { min: -domainLimit, max: domainLimit } }}
537
+ >
538
+ <YAxis showGrid requestedTickCount={2} tickLabelFormatter={tickLabelFormatter} />
539
+ <Line
540
+ showArea
541
+ AreaComponent={MyGradient}
542
+ seriesId="prices"
543
+ strokeWidth={3}
544
+ transition={myTransitionConfig}
545
+ />
546
+ <Scrubber
547
+ hideOverlay
548
+ beaconTransitions={{ update: myTransitionConfig }}
549
+ label={valueAtIndexFormatter}
550
+ />
551
+ </CartesianChart>
552
+ );
553
+ }
554
+
555
+ return <CustomTransitionsChart />;
556
+ }
557
+ ```
558
+
559
+ ### Accessibility
560
+
561
+ You can use `accessibilityLabel` on both the chart and the scrubber to provide descriptive labels. The chart's label gives an overview, while the scrubber's label provides specific information about the current data point being viewed.
562
+
563
+ ```jsx live
564
+ function BasicAccessible() {
565
+ const data = useMemo(() => [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58], []);
566
+
567
+ // Chart-level accessibility label provides overview
568
+ const chartAccessibilityLabel = useMemo(() => {
569
+ const currentPrice = data[data.length - 1];
570
+ return `Price chart showing trend over ${data.length} data points. Current value: ${currentPrice}. Use arrow keys to adjust view`;
571
+ }, [data]);
572
+
573
+ // Scrubber-level accessibility label provides specific position info
574
+ const scrubberAccessibilityLabel = useCallback(
575
+ (index: number) => {
576
+ return `Price at position ${index + 1} of ${data.length}: ${data[index]}`;
577
+ },
578
+ [data],
579
+ );
580
+
581
+ return (
582
+ <LineChart
583
+ enableScrubbing
520
584
  showArea
521
- renderPoints={() => true}
522
- curve="natural"
523
- showXAxis
524
- xAxis={{ data: xData, showLine: true, showTickMarks: true, showGrid: true }}
525
585
  showYAxis
586
+ accessibilityLabel={chartAccessibilityLabel}
587
+ height={{ base: 200, tablet: 225, desktop: 250 }}
588
+ series={[
589
+ {
590
+ id: 'prices',
591
+ data: data,
592
+ },
593
+ ]}
526
594
  yAxis={{
527
- domain: { min: 0 },
528
- position: 'left',
529
- showLine: true,
530
- showTickMarks: true,
531
595
  showGrid: true,
532
596
  }}
533
- inset={{ top: 16, right: 16, bottom: 0, left: 0 }}
534
- accessibilityLabel={accessibilityLabel}
535
597
  >
536
- <Scrubber hideOverlay />
598
+ <Scrubber accessibilityLabel={scrubberAccessibilityLabel} />
599
+ </LineChart>
600
+ );
601
+ }
602
+ ```
603
+
604
+ When a chart has a visible header or title, you can use `aria-labelledby` to reference it, and still provide a dynamic scrubber accessibility label.
605
+
606
+ ```jsx live
607
+ function AccessibleWithHeader() {
608
+ const headerId = useId();
609
+ const data = useMemo(() => [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58], []);
610
+
611
+ // Display label provides overview
612
+ const displayLabel = useMemo(
613
+ () => `Revenue chart showing trend. Current value: ${data[data.length - 1]}`,
614
+ [data],
615
+ );
616
+
617
+ // Scrubber-specific accessibility label
618
+ const scrubberAccessibilityLabel = useCallback(
619
+ (index: number) => {
620
+ return `Viewing position ${index + 1} of ${data.length}, value: ${data[index]}`;
621
+ },
622
+ [data],
623
+ );
624
+
625
+ return (
626
+ <VStack gap={2}>
627
+ <Text font="label1" id={headerId}>
628
+ {displayLabel}
629
+ </Text>
630
+ <LineChart
631
+ enableScrubbing
632
+ showArea
633
+ showYAxis
634
+ aria-labelledby={headerId}
635
+ height={{ base: 200, tablet: 225, desktop: 250 }}
636
+ series={[
637
+ {
638
+ id: 'revenue',
639
+ data: data,
640
+ },
641
+ ]}
642
+ yAxis={{
643
+ showGrid: true,
644
+ }}
645
+ >
646
+ <Scrubber accessibilityLabel={scrubberAccessibilityLabel} />
647
+ </LineChart>
648
+ </VStack>
649
+ );
650
+ }
651
+ ```
652
+
653
+ ### Styling
654
+
655
+ #### Axes
656
+
657
+ Using `showXAxis` and `showYAxis` allows you to display the axes. For more information, such as adjusting domain and range, see [XAxis](/components/graphs/XAxis) and [YAxis](/components/graphs/YAxis).
658
+
659
+ ```jsx live
660
+ <LineChart
661
+ showArea
662
+ showXAxis
663
+ showYAxis
664
+ height={{ base: 200, tablet: 225, desktop: 250 }}
665
+ series={[
666
+ {
667
+ id: 'prices',
668
+ data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58],
669
+ },
670
+ ]}
671
+ xAxis={{
672
+ showGrid: true,
673
+ showLine: true,
674
+ showTickMarks: true,
675
+ tickLabelFormatter: (dataX: number) => `Day ${dataX}`,
676
+ }}
677
+ yAxis={{
678
+ showGrid: true,
679
+ showLine: true,
680
+ showTickMarks: true,
681
+ }}
682
+ />
683
+ ```
684
+
685
+ #### Gradients
686
+
687
+ Gradients can be applied to the y-axis (default) or x-axis. Each stop requires an `offset`, which is based on the data within the x/y scale and `color`, with an optional `opacity` (defaults to 1).
688
+
689
+ Values in between stops will be interpolated smoothly using [srgb color space](https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperty).
690
+
691
+ ```jsx live
692
+ function Gradients() {
693
+ const spectrumColors = [
694
+ 'blue',
695
+ 'green',
696
+ 'orange',
697
+ 'yellow',
698
+ 'gray',
699
+ 'indigo',
700
+ 'pink',
701
+ 'purple',
702
+ 'red',
703
+ 'teal',
704
+ 'chartreuse',
705
+ ];
706
+ const data = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];
707
+
708
+ const [currentSpectrumColor, setCurrentSpectrumColor] = useState('pink');
709
+
710
+ return (
711
+ <VStack gap={2}>
712
+ <HStack flexWrap="wrap" gap={1} justifyContent="flex-end">
713
+ {spectrumColors.map((color) => (
714
+ <Pressable
715
+ key={color}
716
+ accessibilityLabel={`Select ${color}`}
717
+ borderRadius={1000}
718
+ height={{ base: 16, tablet: 24, desktop: 24 }}
719
+ onClick={() => setCurrentSpectrumColor(color)}
720
+ style={{
721
+ backgroundColor: `rgb(var(--${color}20))`,
722
+ border: `2px solid rgb(var(--${color}50))`,
723
+ outlineColor: `rgb(var(--${color}80))`,
724
+ outline:
725
+ currentSpectrumColor === color ? `2px solid rgb(var(--${color}80))` : undefined,
726
+ }}
727
+ width={{ base: 16, tablet: 24, desktop: 24 }}
728
+ />
729
+ ))}
730
+ </HStack>
731
+ <LineChart
732
+ showYAxis
733
+ height={{ base: 200, tablet: 225, desktop: 250 }}
734
+ points
735
+ series={[
736
+ {
737
+ id: 'continuousGradient',
738
+ data: data,
739
+ gradient: {
740
+ stops: [
741
+ { offset: 0, color: `rgb(var(--${currentSpectrumColor}80))` },
742
+ { offset: Math.max(...data), color: `rgb(var(--${currentSpectrumColor}20))` },
743
+ ],
744
+ },
745
+ },
746
+ {
747
+ id: 'discreteGradient',
748
+ data: data.map((d) => d + 50),
749
+ // You can create a "discrete" gradient by having multiple stops at the same offset
750
+ gradient: {
751
+ stops: ({ min, max }) => [
752
+ // Allows a function which accepts min/max or direct array
753
+ { offset: min, color: `rgb(var(--${currentSpectrumColor}80))` },
754
+ { offset: min + (max - min) / 3, color: `rgb(var(--${currentSpectrumColor}80))` },
755
+ { offset: min + (max - min) / 3, color: `rgb(var(--${currentSpectrumColor}50))` },
756
+ {
757
+ offset: min + ((max - min) / 3) * 2,
758
+ color: `rgb(var(--${currentSpectrumColor}50))`,
759
+ },
760
+ {
761
+ offset: min + ((max - min) / 3) * 2,
762
+ color: `rgb(var(--${currentSpectrumColor}20))`,
763
+ },
764
+ { offset: max, color: `rgb(var(--${currentSpectrumColor}20))` },
765
+ ],
766
+ },
767
+ },
768
+ {
769
+ id: 'xAxisGradient',
770
+ data: data.map((d) => d + 100),
771
+ gradient: {
772
+ // You can also configure by the x-axis.
773
+ axis: 'x',
774
+ stops: ({ min, max }) => [
775
+ { offset: min, color: `rgb(var(--${currentSpectrumColor}80))`, opacity: 0 },
776
+ { offset: max, color: `rgb(var(--${currentSpectrumColor}20))`, opacity: 1 },
777
+ ],
778
+ },
779
+ },
780
+ ]}
781
+ strokeWidth={4}
782
+ yAxis={{
783
+ showGrid: true,
784
+ }}
785
+ />
786
+ </VStack>
787
+ );
788
+ }
789
+ ```
790
+
791
+ You can even pass in a separate gradient for your `Line` and `Area` components.
792
+
793
+ ```jsx live
794
+ function GainLossChart() {
795
+ const data = useMemo(() => [-40, -28, -21, -5, 48, -5, -28, 2, -29, -46, 16, -30, -29, 8], []);
796
+ const negativeColor = 'rgb(var(--gray15))';
797
+ const positiveColor = 'var(--color-fgPositive)';
798
+
799
+ const tickLabelFormatter = useCallback(
800
+ (value: number) =>
801
+ new Intl.NumberFormat('en-US', {
802
+ style: 'currency',
803
+ currency: 'USD',
804
+ maximumFractionDigits: 0,
805
+ }).format(value),
806
+ [],
807
+ );
808
+
809
+ // Line gradient: hard color change at 0 (full opacity for line)
810
+ const lineGradient = {
811
+ stops: [
812
+ { offset: 0, color: negativeColor },
813
+ { offset: 0, color: positiveColor },
814
+ ],
815
+ };
816
+
817
+ const chartAccessibilityLabel = `Gain/Loss chart showing price changes. Current value: ${tickLabelFormatter(data[data.length - 1])}`;
818
+
819
+ const scrubberAccessibilityLabel = useCallback(
820
+ (index: number) => {
821
+ const value = data[index];
822
+ const status = value >= 0 ? 'gain' : 'loss';
823
+ return `Position ${index + 1} of ${data.length}: ${tickLabelFormatter(value)} ${status}`;
824
+ },
825
+ [data, tickLabelFormatter],
826
+ );
827
+
828
+ const GradientDottedArea = memo((props: DottedAreaProps) => (
829
+ <DottedArea
830
+ {...props}
831
+ gradient={{
832
+ stops: ({ min, max }) => [
833
+ { offset: min, color: negativeColor, opacity: 0.4 },
834
+ { offset: 0, color: negativeColor, opacity: 0 },
835
+ { offset: 0, color: positiveColor, opacity: 0 },
836
+ { offset: max, color: positiveColor, opacity: 0.4 },
837
+ ],
838
+ }}
839
+ />
840
+ ));
841
+
842
+ return (
843
+ <CartesianChart
844
+ enableScrubbing
845
+ accessibilityLabel={chartAccessibilityLabel}
846
+ height={{ base: 200, tablet: 225, desktop: 250 }}
847
+ series={[
848
+ {
849
+ id: 'prices',
850
+ data: data,
851
+ gradient: lineGradient,
852
+ },
853
+ ]}
854
+ xAxis={{
855
+ range: ({ min, max }) => ({ min, max: max - 16 }),
856
+ }}
857
+ >
858
+ <YAxis showGrid requestedTickCount={2} tickLabelFormatter={tickLabelFormatter} />
859
+ <Line showArea AreaComponent={GradientDottedArea} seriesId="prices" strokeWidth={3} />
860
+ <Scrubber hideOverlay accessibilityLabel={scrubberAccessibilityLabel} />
861
+ </CartesianChart>
862
+ );
863
+ }
864
+ ```
865
+
866
+ #### Lines
867
+
868
+ You can customize lines by placing props in `LineChart` or at each individual series. Lines can have a `type` of `solid` or `dotted`. They can optionally show an area underneath them (using `showArea`).
869
+
870
+ ```jsx live
871
+ <LineChart
872
+ height={{ base: 200, tablet: 225, desktop: 250 }}
873
+ series={[
874
+ {
875
+ id: 'top',
876
+ data: [15, 28, 32, 44, 46, 36, 40, 45, 48, 38],
877
+ },
878
+ {
879
+ id: 'upperMiddle',
880
+ data: [12, 23, 21, 29, 34, 28, 31, 38, 42, 35],
881
+ color: '#ef4444',
882
+ type: 'dotted',
883
+ },
884
+ {
885
+ id: 'lowerMiddle',
886
+ data: [8, 15, 14, 25, 20, 18, 22, 28, 24, 30],
887
+ color: '#f59e0b',
888
+ curve: 'natural',
889
+ gradient: {
890
+ axis: 'x',
891
+ stops: [
892
+ { offset: 0, color: '#E3D74D' },
893
+ { offset: 9, color: '#F7931A' },
894
+ ],
895
+ },
896
+ strokeWidth: 6,
897
+ },
898
+ {
899
+ id: 'bottom',
900
+ data: [4, 8, 11, 15, 16, 14, 16, 10, 12, 14],
901
+ color: '#800080',
902
+ curve: 'step',
903
+ AreaComponent: DottedArea,
904
+ showArea: true,
905
+ },
906
+ ]}
907
+ />
908
+ ```
909
+
910
+ You can also add instances of [ReferenceLine](/components/graphs/ReferenceLine) to your LineChart to highlight a specific x or y value.
911
+
912
+ ```jsx live
913
+ <LineChart
914
+ enableScrubbing
915
+ showArea
916
+ height={{ base: 200, tablet: 225, desktop: 250 }}
917
+ series={[
918
+ {
919
+ id: 'prices',
920
+ data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58],
921
+ color: 'var(--color-fgPositive)',
922
+ },
923
+ ]}
924
+ xAxis={{
925
+ // Give space before the end of the chart for the scrubber
926
+ range: ({ min, max }) => ({ min, max: max - 24 }),
927
+ }}
928
+ >
929
+ <ReferenceLine
930
+ LineComponent={(props) => <DottedLine {...props} strokeDasharray="0 16" strokeWidth={3} />}
931
+ dataY={10}
932
+ stroke="var(--color-fg)"
933
+ />
934
+ <Scrubber />
935
+ </LineChart>
936
+ ```
937
+
938
+ #### Points
939
+
940
+ You can also add instances of [Point](/components/graphs/Point) directly inside of a LineChart.
941
+
942
+ ```jsx live
943
+ function HighLowPrice() {
944
+ const data = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];
945
+ const minPrice = Math.min(...data);
946
+ const maxPrice = Math.max(...data);
947
+
948
+ const minPriceIndex = data.indexOf(minPrice);
949
+ const maxPriceIndex = data.indexOf(maxPrice);
950
+
951
+ const formatPrice = useCallback((price: number) => {
952
+ return `$${price.toLocaleString('en-US', {
953
+ minimumFractionDigits: 2,
954
+ maximumFractionDigits: 2,
955
+ })}`;
956
+ }, []);
957
+
958
+ return (
959
+ <LineChart
960
+ showArea
961
+ height={{ base: 200, tablet: 225, desktop: 250 }}
962
+ series={[
963
+ {
964
+ id: 'prices',
965
+ data: data,
966
+ },
967
+ ]}
968
+ >
969
+ <Point
970
+ dataX={minPriceIndex}
971
+ dataY={minPrice}
972
+ label={formatPrice(minPrice)}
973
+ labelPosition="bottom"
974
+ />
975
+ <Point
976
+ dataX={maxPriceIndex}
977
+ dataY={maxPrice}
978
+ label={formatPrice(maxPrice)}
979
+ labelPosition="top"
980
+ />
537
981
  </LineChart>
538
982
  );
539
983
  }
540
984
  ```
541
985
 
542
- ### Accessibility
986
+ #### Scrubber
987
+
988
+ When using [Scrubber](/components/graphs/Scrubber) with series that have labels, labels will automatically render to the side of the scrubber beacon.
543
989
 
544
- You can use `accessibilityLabel` to provide a descriptive label for the chart. This is especially important for charts with scrubbing enabled, where the label should update dynamically to reflect the current data point.
990
+ You can customize the line used for and which series will render a scrubber beacon.
991
+
992
+ You can have scrubber beacon's pulse by either adding `idlePulse` to Scrubber or use Scrubber's ref to dynamically pulse.
545
993
 
546
994
  ```jsx live
547
- function AccessibleBasicChart() {
548
- const [scrubIndex, setScrubIndex] = useState(undefined);
549
- const data = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];
995
+ function StylingScrubber() {
996
+ const pages = ['Page A', 'Page B', 'Page C', 'Page D', 'Page E', 'Page F', 'Page G'];
997
+ const pageViews = [2400, 1398, 9800, 3908, 4800, 3800, 4300];
998
+ const uniqueVisitors = [4000, 3000, 2000, 2780, 1890, 2390, 3490];
550
999
 
551
- const accessibilityLabel = useMemo(() => {
552
- if (scrubIndex === undefined) {
553
- return `Current price: ${data[data.length - 1]}`;
554
- }
555
- return `Price at position ${scrubIndex + 1}: ${data[scrubIndex]}`;
556
- }, [scrubIndex, data]);
1000
+ const numberFormatter = useCallback(
1001
+ (value: number) => new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(value),
1002
+ [],
1003
+ );
557
1004
 
558
1005
  return (
559
1006
  <LineChart
560
1007
  enableScrubbing
561
- onScrubberPositionChange={setScrubIndex}
562
- height={{ base: 150, tablet: 200, desktop: 250 }}
1008
+ showArea
1009
+ showXAxis
1010
+ showYAxis
1011
+ height={{ base: 200, tablet: 225, desktop: 250 }}
563
1012
  series={[
564
1013
  {
565
- id: 'prices',
566
- data: data,
1014
+ id: 'pageViews',
1015
+ data: pageViews,
1016
+ color: 'var(--color-accentBoldGreen)',
1017
+ // Label will render next to scrubber beacon
1018
+ label: 'Page Views',
1019
+ },
1020
+ {
1021
+ id: 'uniqueVisitors',
1022
+ data: uniqueVisitors,
1023
+ color: 'var(--color-accentBoldPurple)',
1024
+ label: 'Unique Visitors',
1025
+ // Default area is gradient
1026
+ areaType: 'dotted',
567
1027
  },
568
1028
  ]}
569
- curve="monotone"
570
- showYAxis
571
- showArea
1029
+ xAxis={{
1030
+ // Used on the x-axis to provide context for each index from the series data array
1031
+ data: pages,
1032
+ }}
572
1033
  yAxis={{
573
1034
  showGrid: true,
1035
+ tickLabelFormatter: numberFormatter,
574
1036
  }}
575
- accessibilityLabel={accessibilityLabel}
576
1037
  >
577
- <Scrubber />
1038
+ <Scrubber idlePulse LineComponent={SolidLine} seriesIds={['pageViews']} />
578
1039
  </LineChart>
579
1040
  );
580
1041
  }
581
1042
  ```
582
1043
 
583
- When a chart has a visible header or title, you can use `aria-labelledby` to reference it.
1044
+ #### Sizing
1045
+
1046
+ Charts by default take up `100%` of the `width` and `height` available, but can be customized as any other component.
584
1047
 
585
1048
  ```jsx live
586
- function AccessibleChartWithHeader() {
587
- const headerId = useId();
588
- const [scrubIndex, setScrubIndex] = useState(undefined);
589
- const data = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];
1049
+ function DynamicChartSizing() {
1050
+ const candles = [...btcCandles].reverse();
1051
+ const prices = candles.map((candle) => parseFloat(candle.close));
1052
+ const highs = candles.map((candle) => parseFloat(candle.high));
1053
+ const lows = candles.map((candle) => parseFloat(candle.low));
590
1054
 
591
- const accessibilityLabel = useMemo(() => {
592
- if (scrubIndex === undefined) {
593
- return `Line chart showing price trend. Current value: ${data[data.length - 1]}`;
1055
+ const latestPrice = prices[prices.length - 1];
1056
+ const previousPrice = prices[prices.length - 2];
1057
+ const change24h = ((latestPrice - previousPrice) / previousPrice) * 100;
1058
+
1059
+ function DetailCell({ title, description }: { title: string; description: string }) {
1060
+ return (
1061
+ <VStack>
1062
+ <Text color="fgMuted" font="label2">
1063
+ {title}
1064
+ </Text>
1065
+ <Text font="headline">{description}</Text>
1066
+ </VStack>
1067
+ );
1068
+ }
1069
+
1070
+ // Calculate 7-day moving average
1071
+ const calculateMA = (data: number[], period: number): number[] => {
1072
+ const ma: number[] = [];
1073
+ for (let i = 0; i < data.length; i++) {
1074
+ if (i >= period - 1) {
1075
+ const sum = data.slice(i - period + 1, i + 1).reduce((a, b) => a + b, 0);
1076
+ ma.push(sum / period);
1077
+ }
594
1078
  }
595
- return `Value: ${data[scrubIndex]} at position ${scrubIndex + 1}`;
596
- }, [scrubIndex, data]);
1079
+ return ma;
1080
+ };
1081
+
1082
+ const ma7 = calculateMA(prices, 7);
1083
+ const latestMA7: number = ma7[ma7.length - 1];
1084
+
1085
+ const periodHigh = Math.max(...highs);
1086
+ const periodLow = Math.min(...lows);
1087
+
1088
+ const formatPrice = useCallback((price: number) => {
1089
+ return `$${price.toLocaleString('en-US', {
1090
+ minimumFractionDigits: 2,
1091
+ maximumFractionDigits: 2,
1092
+ })}`;
1093
+ }, []);
1094
+
1095
+ const formatPercentage = useCallback((value: number) => {
1096
+ const sign = value >= 0 ? '+' : '';
1097
+ return `${sign}${value.toFixed(2)}%`;
1098
+ }, []);
597
1099
 
598
1100
  return (
599
- <VStack gap={2}>
600
- <Text id={headerId} font="label1">
601
- {accessibilityLabel}
602
- </Text>
1101
+ <HStack gap={3}>
1102
+ <Box
1103
+ borderBottomLeftRadius={300}
1104
+ borderTopLeftRadius={300}
1105
+ flexGrow={1}
1106
+ style={{
1107
+ background: 'linear-gradient(0deg, #D07609 0%, #F7931A 100%)',
1108
+ marginTop: 'calc(-1 * var(--space-3))',
1109
+ marginLeft: 'calc(-1 * var(--space-3))',
1110
+ marginBottom: 'calc(-1 * var(--space-3))',
1111
+ }}
1112
+ >
1113
+ {/* LineChart fills to take up available width and height */}
1114
+ <LineChart
1115
+ series={[
1116
+ {
1117
+ id: 'btc',
1118
+ data: prices,
1119
+ color: 'white',
1120
+ },
1121
+ ]}
1122
+ />
1123
+ </Box>
1124
+ <VStack gap={1}>
1125
+ <VStack>
1126
+ <Text font="title1">BTC</Text>
1127
+ <Text font="title2">{formatPrice(latestPrice)}</Text>
1128
+ </VStack>
1129
+ <DetailCell description={formatPrice(periodHigh)} title="High" />
1130
+ <DetailCell description={formatPrice(periodLow)} title="Low" />
1131
+ <VStack display={{ base: 'none', tablet: 'flex', desktop: 'flex' }} gap={1}>
1132
+ <DetailCell description={formatPercentage(change24h)} title="24h" />
1133
+ <DetailCell description={formatPrice(latestMA7)} title="7d MA" />
1134
+ </VStack>
1135
+ </VStack>
1136
+ </HStack>
1137
+ );
1138
+ }
1139
+ ```
1140
+
1141
+ ##### Compact
1142
+
1143
+ You can also have charts in a compact form.
1144
+
1145
+ ```jsx live
1146
+ function Compact() {
1147
+ const dimensions = { width: 62, height: 18 };
1148
+
1149
+ const sparklineData = prices
1150
+ .map((price) => parseFloat(price))
1151
+ .filter((price, index) => index % 10 === 0);
1152
+ const positiveFloor = Math.min(...sparklineData) - 10;
1153
+
1154
+ const negativeData = sparklineData.map((price) => -1 * price).reverse();
1155
+ const negativeCeiling = Math.max(...negativeData) + 10;
1156
+
1157
+ const formatPrice = useCallback((price: number) => {
1158
+ return `$${price.toLocaleString('en-US', {
1159
+ minimumFractionDigits: 2,
1160
+ maximumFractionDigits: 2,
1161
+ })}`;
1162
+ }, []);
1163
+
1164
+ type CompactChartProps = {
1165
+ data: number[];
1166
+ showArea?: boolean;
1167
+ color?: string;
1168
+ referenceY: number;
1169
+ };
1170
+
1171
+ const CompactChart = memo(({ data, showArea, color, referenceY }: CompactChartProps) => (
1172
+ <Box style={{ padding: 1 }}>
603
1173
  <LineChart
604
- enableScrubbing
605
- onScrubberPositionChange={setScrubIndex}
606
- height={{ base: 150, tablet: 200, desktop: 250 }}
1174
+ {...dimensions}
1175
+ enableScrubbing={false}
1176
+ inset={0}
607
1177
  series={[
608
1178
  {
609
- id: 'revenue',
610
- data: data,
1179
+ id: 'btc',
1180
+ data,
1181
+ color,
611
1182
  },
612
1183
  ]}
613
- curve="monotone"
614
- showYAxis
615
- showArea
616
- yAxis={{
617
- showGrid: true,
618
- }}
619
- aria-labelledby={headerId}
1184
+ showArea={showArea}
620
1185
  >
621
- <Scrubber />
1186
+ <ReferenceLine dataY={referenceY} />
622
1187
  </LineChart>
1188
+ </Box>
1189
+ ));
1190
+
1191
+ const ChartCell = memo(
1192
+ ({
1193
+ data,
1194
+ showArea,
1195
+ color,
1196
+ referenceY,
1197
+ subdetail,
1198
+ }: CompactChartProps & { subdetail: string }) => {
1199
+ const { isPhone } = useBreakpoints();
1200
+
1201
+ return (
1202
+ <ListCell
1203
+ description={isPhone ? undefined : assets.btc.symbol}
1204
+ detail={formatPrice(parseFloat(prices[0]))}
1205
+ intermediary={
1206
+ <CompactChart color={color} data={data} referenceY={referenceY} showArea={showArea} />
1207
+ }
1208
+ media={<Avatar src={assets.btc.imageUrl} />}
1209
+ onClick={() => console.log('clicked')}
1210
+ spacingVariant="condensed"
1211
+ style={{ padding: 0 }}
1212
+ subdetail={subdetail}
1213
+ title={isPhone ? undefined : assets.btc.name}
1214
+ />
1215
+ );
1216
+ },
1217
+ );
1218
+
1219
+ return (
1220
+ <VStack>
1221
+ <ChartCell
1222
+ color={assets.btc.color}
1223
+ data={sparklineData}
1224
+ referenceY={parseFloat(prices[Math.floor(prices.length / 4)])}
1225
+ subdetail="-4.55%"
1226
+ />
1227
+ <ChartCell
1228
+ showArea
1229
+ color={assets.btc.color}
1230
+ data={sparklineData}
1231
+ referenceY={parseFloat(prices[Math.floor(prices.length / 4)])}
1232
+ subdetail="-4.55%"
1233
+ />
1234
+ <ChartCell
1235
+ showArea
1236
+ color="var(--color-fgPositive)"
1237
+ data={sparklineData}
1238
+ referenceY={positiveFloor}
1239
+ subdetail="+0.25%"
1240
+ />
1241
+ <ChartCell
1242
+ showArea
1243
+ color="var(--color-fgNegative)"
1244
+ data={negativeData}
1245
+ referenceY={negativeCeiling}
1246
+ subdetail="-4.55%"
1247
+ />
623
1248
  </VStack>
624
1249
  );
625
1250
  }
626
1251
  ```
627
1252
 
628
- ### Customization
1253
+ ### Composed Examples
629
1254
 
630
1255
  #### Asset Price with Dotted Area
631
1256
 
1257
+ You can use [PeriodSelector](/components/graphs/PeriodSelector) to have a chart where the user can select a time period and the chart automatically animates.
1258
+
632
1259
  ```jsx live
633
1260
  function AssetPriceWithDottedArea() {
634
1261
  const BTCTab: TabComponent = memo(
@@ -657,7 +1284,7 @@ function AssetPriceWithDottedArea() {
657
1284
  ),
658
1285
  );
659
1286
 
660
- const BTCActiveIndicator = memo(({ style, ...props }: TabsActiveIndicatorProps) => (
1287
+ const BTCActiveIndicator = memo(({ style, ...props }: TabsActiveIndicatorProps) => (
661
1288
  <PeriodSelectorActiveIndicator
662
1289
  {...props}
663
1290
  style={{ ...style, backgroundColor: `${assets.btc.color}1A` }}
@@ -665,200 +1292,50 @@ const BTCActiveIndicator = memo(({ style, ...props }: TabsActiveIndicatorProps)
665
1292
  ));
666
1293
 
667
1294
  const AssetPriceDotted = memo(() => {
668
- const [scrubIndex, setScrubIndex] = useState<number | undefined>(undefined);
669
- const currentPrice =
670
- sparklineInteractiveData.hour[sparklineInteractiveData.hour.length - 1].value;
671
- const tabs = [
672
- { id: 'hour', label: '1H' },
673
- { id: 'day', label: '1D' },
674
- { id: 'week', label: '1W' },
675
- { id: 'month', label: '1M' },
676
- { id: 'year', label: '1Y' },
677
- { id: 'all', label: 'All' },
678
- ];
679
- const [timePeriod, setTimePeriod] = useState<TabValue>(tabs[0]);
680
-
681
- const sparklineTimePeriodData = useMemo(() => {
682
- return sparklineInteractiveData[timePeriod.id as keyof typeof sparklineInteractiveData];
683
- }, [timePeriod]);
684
-
685
- const sparklineTimePeriodDataValues = useMemo(() => {
686
- return sparklineTimePeriodData.map((d) => d.value);
687
- }, [sparklineTimePeriodData]);
688
-
689
- const sparklineTimePeriodDataTimestamps = useMemo(() => {
690
- return sparklineTimePeriodData.map((d) => d.date);
691
- }, [sparklineTimePeriodData]);
692
-
693
- const onPeriodChange = useCallback(
694
- (period: TabValue | null) => {
695
- setTimePeriod(period || tabs[0]);
696
- },
697
- [tabs, setTimePeriod],
698
- );
699
-
700
- const priceFormatter = useMemo(
701
- () =>
702
- new Intl.NumberFormat('en-US', {
703
- style: 'currency',
704
- currency: 'USD',
705
- }),
706
- [],
707
- );
708
-
709
- const scrubberPriceFormatter = useMemo(
710
- () =>
711
- new Intl.NumberFormat('en-US', {
712
- minimumFractionDigits: 2,
713
- maximumFractionDigits: 2,
714
- }),
715
- [],
716
- );
717
-
718
- const formatPrice = useCallback((price: number) => {
719
- return priceFormatter.format(price);
720
- }, [priceFormatter]);
721
-
722
- const formatDate = useCallback((date: Date) => {
723
- const dayOfWeek = date.toLocaleDateString('en-US', { weekday: 'short' });
724
-
725
- const monthDay = date.toLocaleDateString('en-US', {
726
- month: 'short',
727
- day: 'numeric',
728
- });
729
-
730
- const time = date.toLocaleTimeString('en-US', {
731
- hour: 'numeric',
732
- minute: '2-digit',
733
- hour12: true,
734
- });
735
-
736
- return `${dayOfWeek}, ${monthDay}, ${time}`;
737
- }, []);
738
-
739
- const scrubberLabel = useCallback(
740
- (index: number) => {
741
- const price = scrubberPriceFormatter.format(sparklineTimePeriodDataValues[index]);
742
- const date = formatDate(sparklineTimePeriodDataTimestamps[index]);
743
- return (
744
- <>
745
- <tspan style={{ fontWeight: 'bold' }}>{price} USD</tspan> {date}
746
- </>
747
- );
748
- },
749
- [scrubberPriceFormatter, sparklineTimePeriodDataValues, sparklineTimePeriodDataTimestamps, formatDate],
750
- );
751
-
752
- const accessibilityLabel = useCallback(
753
- (index: number) => {
754
- const price = scrubberPriceFormatter.format(sparklineTimePeriodDataValues[index]);
755
- const date = formatDate(sparklineTimePeriodDataTimestamps[index]);
756
- return `${price} USD ${date}`;
757
- },
758
- [scrubberPriceFormatter, sparklineTimePeriodDataValues, sparklineTimePeriodDataTimestamps, formatDate],
759
- );
760
-
761
- return (
762
- <VStack gap={2}>
763
- <SectionHeader
764
- style={{ padding: 0 }}
765
- title={<Text font="title1">Bitcoin</Text>}
766
- balance={<Text font="title2">{formatPrice(currentPrice)}</Text>}
767
- end={
768
- <VStack justifyContent="center">
769
- <RemoteImage source={assets.btc.imageUrl} size="xl" shape="circle" />
770
- </VStack>
771
- }
772
- />
773
- <LineChart
774
- overflow="visible"
775
- enableScrubbing
776
- onScrubberPositionChange={setScrubIndex}
777
- series={[
778
- {
779
- id: 'btc',
780
- data: sparklineTimePeriodDataValues,
781
- color: assets.btc.color,
782
- },
783
- ]}
784
- showArea
785
- areaType="dotted"
786
- height={{ base: 150, tablet: 200, desktop: 250 }}
787
- style={{ outlineColor: assets.btc.color }}
788
- accessibilityLabel={scrubberLabel}
789
- padding={{ left: 2, right: 2 }}
790
- >
791
- <Scrubber label={scrubberLabel} labelProps={{ elevation: 1 }} idlePulse />
792
- </LineChart>
793
- <PeriodSelector
794
- TabComponent={BTCTab}
795
- TabsActiveIndicatorComponent={BTCActiveIndicator}
796
- tabs={tabs}
797
- activeTab={timePeriod}
798
- onChange={onPeriodChange}
799
- />
800
- </VStack>
801
- )});
802
-
803
- return <AssetPriceDotted />;
804
- };
805
- ```
806
-
807
- #### Forecast Asset Price
808
-
809
- ```jsx live
810
- function ForecastAssetPrice() {
811
- const ForecastAreaComponent = memo(
812
- (props: AreaComponentProps) => (
813
- <DottedArea {...props} peakOpacity={0.4} baselineOpacity={0.4} />
814
- ),
815
- );
816
-
817
- const ForecastChart = memo(() => {
818
- const [scrubIndex, setScrubIndex] = useState<number | undefined>(undefined);
819
-
820
- const getDataFromSparkline = useCallback((startDate: Date) => {
821
- const allData = sparklineInteractiveData.all;
822
- if (!allData || allData.length === 0) return [];
823
-
824
- const timelineData = allData.filter((point) => point.date >= startDate);
825
-
826
- return timelineData.map((point) => ({
827
- date: point.date,
828
- value: point.value,
829
- }));
830
- }, []);
831
-
832
- const historicalData = useMemo(() => getDataFromSparkline(new Date('2019-01-01')), [getDataFromSparkline]);
833
-
834
- const annualGrowthRate = 10;
1295
+ const currentPrice =
1296
+ sparklineInteractiveData.hour[sparklineInteractiveData.hour.length - 1].value;
1297
+ const tabs = useMemo(
1298
+ () => [
1299
+ { id: 'hour', label: '1H' },
1300
+ { id: 'day', label: '1D' },
1301
+ { id: 'week', label: '1W' },
1302
+ { id: 'month', label: '1M' },
1303
+ { id: 'year', label: '1Y' },
1304
+ { id: 'all', label: 'All' },
1305
+ ],
1306
+ [],
1307
+ );
1308
+ const [timePeriod, setTimePeriod] = useState<TabValue>(tabs[0]);
835
1309
 
836
- const generateForecastData = useCallback(
837
- (lastDate: Date, lastPrice: number, growthRate: number) => {
838
- const dailyGrowthRate = Math.pow(1 + growthRate / 100, 1 / 365) - 1;
839
- const forecastData = [];
840
- const fiveYearsFromNow = new Date(lastDate);
841
- fiveYearsFromNow.setFullYear(fiveYearsFromNow.getFullYear() + 5);
1310
+ const sparklineTimePeriodData = useMemo(() => {
1311
+ return sparklineInteractiveData[timePeriod.id as keyof typeof sparklineInteractiveData];
1312
+ }, [timePeriod]);
842
1313
 
843
- // Generate daily forecast points for 5 years
844
- const currentDate = new Date(lastDate);
845
- let currentPrice = lastPrice;
1314
+ const sparklineTimePeriodDataValues = useMemo(() => {
1315
+ return sparklineTimePeriodData.map((d) => d.value);
1316
+ }, [sparklineTimePeriodData]);
846
1317
 
847
- while (currentDate <= fiveYearsFromNow) {
848
- currentPrice = currentPrice * (1 + dailyGrowthRate * 10);
849
- forecastData.push({
850
- date: new Date(currentDate),
851
- value: Math.round(currentPrice),
852
- });
853
- currentDate.setDate(currentDate.getDate() + 10);
854
- }
1318
+ const sparklineTimePeriodDataTimestamps = useMemo(() => {
1319
+ return sparklineTimePeriodData.map((d) => d.date);
1320
+ }, [sparklineTimePeriodData]);
855
1321
 
856
- return forecastData;
1322
+ const onPeriodChange = useCallback(
1323
+ (period: TabValue | null) => {
1324
+ setTimePeriod(period || tabs[0]);
857
1325
  },
858
- [],
1326
+ [tabs, setTimePeriod],
859
1327
  );
860
1328
 
861
1329
  const priceFormatter = useMemo(
1330
+ () =>
1331
+ new Intl.NumberFormat('en-US', {
1332
+ style: 'currency',
1333
+ currency: 'USD',
1334
+ }),
1335
+ [],
1336
+ );
1337
+
1338
+ const scrubberPriceFormatter = useMemo(
862
1339
  () =>
863
1340
  new Intl.NumberFormat('en-US', {
864
1341
  minimumFractionDigits: 2,
@@ -867,6 +1344,13 @@ function ForecastAssetPrice() {
867
1344
  [],
868
1345
  );
869
1346
 
1347
+ const formatPrice = useCallback(
1348
+ (price: number) => {
1349
+ return priceFormatter.format(price);
1350
+ },
1351
+ [priceFormatter],
1352
+ );
1353
+
870
1354
  const formatDate = useCallback((date: Date) => {
871
1355
  const dayOfWeek = date.toLocaleDateString('en-US', { weekday: 'short' });
872
1356
 
@@ -884,250 +1368,253 @@ function ForecastAssetPrice() {
884
1368
  return `${dayOfWeek}, ${monthDay}, ${time}`;
885
1369
  }, []);
886
1370
 
887
- const forecastData = useMemo(() => {
888
- if (historicalData.length === 0) return [];
889
- const lastPoint = historicalData[historicalData.length - 1];
890
- return generateForecastData(lastPoint.date, lastPoint.value, annualGrowthRate);
891
- }, [generateForecastData, historicalData, annualGrowthRate]);
892
-
893
- // Combine all data points with dates converted to timestamps for x-axis
894
- const allDataPoints = useMemo(
895
- () => [...historicalData, ...forecastData],
896
- [historicalData, forecastData],
897
- );
898
-
899
- const historicalDataValues = useMemo(
900
- () => historicalData.map((d) => d.value),
901
- [historicalData],
902
- );
903
-
904
- const forecastDataValues = useMemo(
905
- () => [...historicalData.map((d) => null), ...forecastData.map((d) => d.value)],
906
- [historicalData, forecastData],
907
- );
908
-
909
- const xAxisData = useMemo(
910
- () => allDataPoints.map((d) => d.date.getTime()),
911
- [allDataPoints],
912
- );
913
-
914
1371
  const scrubberLabel = useCallback(
915
1372
  (index: number) => {
916
- const price = priceFormatter.format(allDataPoints[index].value);
917
- const date = formatDate(allDataPoints[index].date);
1373
+ const price = scrubberPriceFormatter.format(sparklineTimePeriodDataValues[index]);
1374
+ const date = formatDate(sparklineTimePeriodDataTimestamps[index]);
918
1375
  return (
919
1376
  <>
920
1377
  <tspan style={{ fontWeight: 'bold' }}>{price} USD</tspan> {date}
921
1378
  </>
922
1379
  );
923
1380
  },
924
- [priceFormatter, allDataPoints, formatDate],
1381
+ [
1382
+ scrubberPriceFormatter,
1383
+ sparklineTimePeriodDataValues,
1384
+ sparklineTimePeriodDataTimestamps,
1385
+ formatDate,
1386
+ ],
925
1387
  );
926
1388
 
927
- const accessibilityLabel = useCallback(
1389
+ const chartAccessibilityLabel = `Bitcoin price chart for ${timePeriod.label} period. Current price: ${formatPrice(currentPrice)}`;
1390
+
1391
+ const scrubberAccessibilityLabel = useCallback(
928
1392
  (index: number) => {
929
- const price = priceFormatter.format(allDataPoints[index].value);
930
- const date = formatDate(allDataPoints[index].date);
1393
+ const price = scrubberPriceFormatter.format(sparklineTimePeriodDataValues[index]);
1394
+ const date = formatDate(sparklineTimePeriodDataTimestamps[index]);
931
1395
  return `${price} USD ${date}`;
932
1396
  },
933
- [priceFormatter, allDataPoints, formatDate],
1397
+ [
1398
+ scrubberPriceFormatter,
1399
+ sparklineTimePeriodDataValues,
1400
+ sparklineTimePeriodDataTimestamps,
1401
+ formatDate,
1402
+ ],
934
1403
  );
935
1404
 
936
1405
  return (
937
- <LineChart
938
- overflow="visible"
939
- animate={false}
940
- enableScrubbing
941
- showArea
942
- showXAxis
943
- AreaComponent={ForecastAreaComponent}
944
- height={{ base: 150, tablet: 200, desktop: 250 }}
945
- padding={{
946
- top: 4,
947
- left: 2,
948
- right: 2,
949
- bottom: 0,
950
- }}
951
- series={[
952
- {
953
- id: 'historical',
954
- data: historicalDataValues,
955
- color: assets.btc.color,
956
- },
957
- {
958
- id: 'forecast',
959
- data: forecastDataValues,
960
- color: assets.btc.color,
961
- type: 'dotted',
962
- },
963
- ]}
964
- xAxis={{
965
- data: xAxisData,
966
- tickLabelFormatter: (value: number) => {
967
- return new Date(value).toLocaleDateString('en-US', {
968
- month: 'numeric',
969
- year: 'numeric',
970
- });
971
- },
972
- tickInterval: 2,
973
- }}
974
- accessibilityLabel={accessibilityLabel}
975
- style={{ outlineColor: assets.btc.color }}
976
- >
977
- <Scrubber label={scrubberLabel} labelProps={{ elevation: 1 }} />
978
- </LineChart>
1406
+ <VStack gap={2}>
1407
+ <SectionHeader
1408
+ balance={<Text font="title2">{formatPrice(currentPrice)}</Text>}
1409
+ end={
1410
+ <VStack justifyContent="center">
1411
+ <RemoteImage shape="circle" size="xl" source={assets.btc.imageUrl} />
1412
+ </VStack>
1413
+ }
1414
+ style={{ padding: 0 }}
1415
+ title={<Text font="title1">Bitcoin</Text>}
1416
+ />
1417
+ <LineChart
1418
+ enableScrubbing
1419
+ showArea
1420
+ accessibilityLabel={chartAccessibilityLabel}
1421
+ areaType="dotted"
1422
+ height={{ base: 200, tablet: 225, desktop: 250 }}
1423
+ series={[
1424
+ {
1425
+ id: 'btc',
1426
+ data: sparklineTimePeriodDataValues,
1427
+ color: assets.btc.color,
1428
+ },
1429
+ ]}
1430
+ style={{ outlineColor: assets.btc.color }}
1431
+ inset={{ top: 60 }}
1432
+ >
1433
+ <Scrubber
1434
+ idlePulse
1435
+ accessibilityLabel={scrubberAccessibilityLabel}
1436
+ label={scrubberLabel}
1437
+ labelElevated
1438
+ />
1439
+ </LineChart>
1440
+ <PeriodSelector
1441
+ TabComponent={BTCTab}
1442
+ TabsActiveIndicatorComponent={BTCActiveIndicator}
1443
+ activeTab={timePeriod}
1444
+ onChange={onPeriodChange}
1445
+ tabs={tabs}
1446
+ />
1447
+ </VStack>
979
1448
  );
980
1449
  });
981
1450
 
982
- return <ForecastChart />;
983
- };
1451
+ return <AssetPriceDotted />;
1452
+ }
984
1453
  ```
985
1454
 
986
- #### Availability
1455
+ #### Monotone Asset Price
1456
+
1457
+ You can adjust [YAxis](/components/graphs/YAxis) and [Scrubber](/components/graphs/Scrubber) to have a chart where the y-axis is overlaid and the beacon is inverted in style.
987
1458
 
988
1459
  ```jsx live
989
- function AvailabilityChart() {
990
- const [scrubIndex, setScrubIndex] = useState(undefined);
1460
+ function MonotoneAssetPrice() {
1461
+ const prices = sparklineInteractiveData.hour;
991
1462
 
992
- const availabilityEvents = [
993
- {
994
- date: new Date('2022-01-01'),
995
- availability: 79,
996
- },
997
- {
998
- date: new Date('2022-01-03'),
999
- availability: 81,
1000
- },
1001
- {
1002
- date: new Date('2022-01-04'),
1003
- availability: 82,
1004
- },
1005
- {
1006
- date: new Date('2022-01-06'),
1007
- availability: 91,
1008
- },
1009
- {
1010
- date: new Date('2022-01-07'),
1011
- availability: 92,
1012
- },
1013
- {
1014
- date: new Date('2022-01-10'),
1015
- availability: 86,
1463
+ const priceFormatter = useMemo(
1464
+ () =>
1465
+ new Intl.NumberFormat('en-US', {
1466
+ style: 'currency',
1467
+ currency: 'USD',
1468
+ }),
1469
+ [],
1470
+ );
1471
+
1472
+ const scrubberPriceFormatter = useMemo(
1473
+ () =>
1474
+ new Intl.NumberFormat('en-US', {
1475
+ minimumFractionDigits: 2,
1476
+ maximumFractionDigits: 2,
1477
+ }),
1478
+ [],
1479
+ );
1480
+
1481
+ const formatPrice = useCallback(
1482
+ (price: number) => {
1483
+ return priceFormatter.format(price);
1016
1484
  },
1017
- ];
1485
+ [priceFormatter],
1486
+ );
1487
+
1488
+ const CustomYAxisTickLabel = useCallback(
1489
+ (props) => (
1490
+ <DefaultAxisTickLabel
1491
+ {...props}
1492
+ dx={4}
1493
+ dy={-12}
1494
+ horizontalAlignment="left"
1495
+ />
1496
+ ),
1497
+ [],
1498
+ );
1499
+
1500
+ const formatDate = useCallback((date: Date) => {
1501
+ const dayOfWeek = date.toLocaleDateString('en-US', { weekday: 'short' });
1018
1502
 
1019
- const accessibilityLabel = useMemo(() => {
1020
- if (scrubIndex === undefined) return undefined;
1021
- const event = availabilityEvents[scrubIndex];
1022
- const formattedDate = event.date.toLocaleDateString('en-US', {
1023
- weekday: 'short',
1503
+ const monthDay = date.toLocaleDateString('en-US', {
1024
1504
  month: 'short',
1025
1505
  day: 'numeric',
1026
- year: 'numeric',
1027
1506
  });
1028
- const status =
1029
- event.availability >= 90 ? 'Good' : event.availability >= 85 ? 'Warning' : 'Critical';
1030
- return `${formattedDate}: Availability ${event.availability}% - Status: ${status}`;
1031
- }, [scrubIndex, availabilityEvents]);
1032
1507
 
1033
- const ChartDefs = memo(({ yellowThresholdPercentage = 85, greenThresholdPercentage = 90 }) => {
1034
- const { drawingArea, height, series, getYScale, getYAxis } = useCartesianChartContext();
1035
- const yScale = getYScale();
1036
- const yAxis = getYAxis();
1508
+ const time = date.toLocaleTimeString('en-US', {
1509
+ hour: 'numeric',
1510
+ minute: '2-digit',
1511
+ hour12: true,
1512
+ });
1513
+
1514
+ return `${dayOfWeek}, ${monthDay}, ${time}`;
1515
+ }, []);
1516
+
1517
+ const scrubberLabel = useCallback(
1518
+ (index: number) => {
1519
+ const price = scrubberPriceFormatter.format(prices[index].value);
1520
+ const date = formatDate(prices[index].date);
1521
+ return (
1522
+ <>
1523
+ <tspan style={{ fontWeight: 'bold' }}>{price} USD</tspan> {date}
1524
+ </>
1525
+ );
1526
+ },
1527
+ [scrubberPriceFormatter, prices, formatDate],
1528
+ );
1037
1529
 
1038
- if (!series || !drawingArea || !yScale) return null;
1530
+ const CustomScrubberBeacon = memo(({ dataX, dataY, seriesId, isIdle }: ScrubberBeaconProps) => {
1531
+ const { getSeries, getXScale, getYScale } = useCartesianChartContext();
1532
+ const targetSeries = getSeries(seriesId);
1533
+ const xScale = getXScale();
1534
+ const yScale = getYScale(targetSeries?.yAxisId);
1039
1535
 
1040
- const rangeBounds = yAxis?.domain;
1041
- const rangeMin = rangeBounds?.min ?? 0;
1042
- const rangeMax = rangeBounds?.max ?? 100;
1536
+ const pixelCoordinate = useMemo(() => {
1537
+ if (!xScale || !yScale) return;
1538
+ return projectPoint({ x: dataX, y: dataY, xScale, yScale });
1539
+ }, [dataX, dataY, xScale, yScale]);
1043
1540
 
1044
- // Calculate the Y positions in the chart coordinate system
1045
- const yellowThresholdY = yScale(yellowThresholdPercentage) ?? 0;
1046
- const greenThresholdY = yScale(greenThresholdPercentage) ?? 0;
1047
- const minY = yScale(rangeMax) ?? 0; // Top of chart (max value)
1048
- const maxY = yScale(rangeMin) ?? drawingArea.height; // Bottom of chart (min value)
1541
+ if (!pixelCoordinate) return;
1049
1542
 
1050
- // Calculate percentages based on actual chart positions
1051
- const yellowThreshold = ((yellowThresholdY - minY) / (maxY - minY)) * 100;
1052
- const greenThreshold = ((greenThresholdY - minY) / (maxY - minY)) * 100;
1543
+ if (isIdle) {
1544
+ return (
1545
+ <m.circle
1546
+ animate={{ cx: pixelCoordinate.x, cy: pixelCoordinate.y }}
1547
+ cx={pixelCoordinate.x}
1548
+ cy={pixelCoordinate.y}
1549
+ fill="var(--color-bg)"
1550
+ r={5}
1551
+ stroke="var(--color-fg)"
1552
+ strokeWidth={3}
1553
+ transition={defaultTransition}
1554
+ />
1555
+ );
1556
+ }
1053
1557
 
1054
1558
  return (
1055
- <defs>
1056
- <linearGradient
1057
- gradientUnits="userSpaceOnUse"
1058
- id="availabilityGradient"
1059
- x1="0%"
1060
- x2="0%"
1061
- y1={minY}
1062
- y2={maxY}
1063
- >
1064
- <stop offset="0%" stopColor="var(--color-fgPositive)" />
1065
- <stop offset={`${greenThreshold}%`} stopColor="var(--color-fgPositive)" />
1066
- <stop offset={`${greenThreshold}%`} stopColor="var(--color-fgWarning)" />
1067
- <stop offset={`${yellowThreshold}%`} stopColor="var(--color-fgWarning)" />
1068
- <stop offset={`${yellowThreshold}%`} stopColor="var(--color-fgNegative)" />
1069
- <stop offset="100%" stopColor="var(--color-fgNegative)" />
1070
- </linearGradient>
1071
- </defs>
1559
+ <circle
1560
+ cx={pixelCoordinate.x}
1561
+ cy={pixelCoordinate.y}
1562
+ fill="var(--color-bg)"
1563
+ r={5}
1564
+ stroke="var(--color-fg)"
1565
+ strokeWidth={3}
1566
+ />
1072
1567
  );
1073
1568
  });
1074
1569
 
1075
1570
  return (
1076
- <CartesianChart
1571
+ <LineChart
1077
1572
  enableScrubbing
1078
- onScrubberPositionChange={setScrubIndex}
1079
- height={{ base: 150, tablet: 200, desktop: 250 }}
1573
+ showYAxis
1574
+ height={{ base: 200, tablet: 250, desktop: 300 }}
1575
+ inset={{ top: 64 }}
1080
1576
  series={[
1081
1577
  {
1082
- id: 'availability',
1083
- data: availabilityEvents.map((event) => event.availability),
1084
- color: 'url(#availabilityGradient)',
1578
+ id: 'btc',
1579
+ data: prices.map((price) => price.value),
1580
+ color: 'var(--color-fg)',
1581
+ gradient: {
1582
+ axis: 'x',
1583
+ stops: ({ min, max }) => [
1584
+ { offset: min, color: 'var(--color-fg)', opacity: 0 },
1585
+ { offset: 32, color: 'var(--color-fg)', opacity: 1 },
1586
+ ],
1587
+ },
1085
1588
  },
1086
1589
  ]}
1590
+ style={{ outlineColor: 'var(--color-fg)' }}
1087
1591
  xAxis={{
1088
- data: availabilityEvents.map((event) => event.date.getTime()),
1592
+ range: ({ min, max }) => ({ min: 96, max: max }),
1089
1593
  }}
1090
1594
  yAxis={{
1091
- domain: ({ min, max }) => ({ min: Math.max(min - 2, 0), max: Math.min(max + 2, 100) }),
1595
+ position: 'left',
1596
+ width: 0,
1597
+ showGrid: true,
1598
+ tickLabelFormatter: formatPrice,
1599
+ TickLabelComponent: CustomYAxisTickLabel,
1092
1600
  }}
1093
- padding={{ left: 2, right: 2 }}
1094
- accessibilityLabel={accessibilityLabel}
1095
1601
  >
1096
- <ChartDefs />
1097
- <XAxis
1098
- showGrid
1099
- showLine
1100
- showTickMarks
1101
- tickLabelFormatter={(value) => new Date(value).toLocaleDateString()}
1102
- />
1103
- <YAxis
1104
- showGrid
1105
- showLine
1106
- showTickMarks
1107
- position="left"
1108
- tickLabelFormatter={(value) => `${value}%`}
1109
- />
1110
- <Line
1111
- curve="stepAfter"
1112
- renderPoints={() => ({
1113
- fill: 'var(--color-bg)',
1114
- stroke: 'url(#availabilityGradient)',
1115
- strokeWidth: 2,
1116
- })}
1117
- seriesId="availability"
1602
+ <Scrubber
1603
+ hideOverlay
1604
+ BeaconComponent={CustomScrubberBeacon}
1605
+ LineComponent={SolidLine}
1606
+ label={scrubberLabel}
1607
+ labelElevated
1118
1608
  />
1119
- <Scrubber hideOverlay />
1120
- </CartesianChart>
1609
+ </LineChart>
1121
1610
  );
1122
1611
  }
1123
1612
  ```
1124
1613
 
1125
1614
  #### Asset Price Widget
1126
1615
 
1127
- You can coordinate LineChart with custom styles to create a custom card that shows the latest price and percent change.
1128
-
1129
1616
  ```jsx live
1130
- function BitcoinChartWithScrubberBeacon() {
1617
+ function AssetPriceWidget() {
1131
1618
  const { isPhone } = useBreakpoints();
1132
1619
  const prices = [...btcCandles].reverse().map((candle) => parseFloat(candle.close));
1133
1620
  const latestPrice = prices[prices.length - 1];
@@ -1149,35 +1636,48 @@ function BitcoinChartWithScrubberBeacon() {
1149
1636
 
1150
1637
  const percentChange = (latestPrice - prices[0]) / prices[0];
1151
1638
 
1639
+ const chartAccessibilityLabel = `Bitcoin price chart. Current price: ${formatPrice(latestPrice)}. Change: ${formatPercentChange(percentChange)}`;
1640
+
1641
+ const scrubberAccessibilityLabel = useCallback(
1642
+ (index: number) => {
1643
+ return `Bitcoin price at position ${index + 1}: ${formatPrice(prices[index])}`;
1644
+ },
1645
+ [prices],
1646
+ );
1647
+
1152
1648
  return (
1153
1649
  <VStack
1154
- style={{
1155
- background:
1156
- 'linear-gradient(0deg, rgba(0, 0, 0, 0.80) 0%, rgba(0, 0, 0, 0.80) 100%), #ED702F',
1157
- }}
1158
1650
  borderRadius={300}
1159
1651
  gap={2}
1652
+ overflow="hidden"
1160
1653
  padding={2}
1161
1654
  paddingBottom={0}
1162
- overflow="hidden"
1655
+ style={{
1656
+ background:
1657
+ 'linear-gradient(0deg, rgba(0, 0, 0, 0.80) 0%, rgba(0, 0, 0, 0.80) 100%), #ED702F',
1658
+ }}
1163
1659
  >
1164
- <HStack gap={2} alignItems="center">
1165
- <RemoteImage source={assets.btc.imageUrl} size="xxl" shape="circle" aria-hidden />
1660
+ <HStack alignItems="center" gap={2}>
1661
+ <RemoteImage aria-hidden shape="circle" size="xxl" source={assets.btc.imageUrl} />
1166
1662
  {!isPhone && (
1167
- <VStack gap={0.25} flexGrow={1}>
1168
- <Text font="title1" style={{ color: 'white' }} aria-hidden>
1663
+ <VStack flexGrow={1} gap={0.25}>
1664
+ <Text aria-hidden font="title1" style={{ color: 'white' }}>
1169
1665
  BTC
1170
1666
  </Text>
1171
- <Text font="label1" color="fgMuted">
1667
+ <Text color="fgMuted" font="label1">
1172
1668
  Bitcoin
1173
1669
  </Text>
1174
1670
  </VStack>
1175
1671
  )}
1176
- <VStack gap={0.25} alignItems="flex-end" flexGrow={isPhone ? 1 : undefined}>
1672
+ <VStack alignItems="flex-end" flexGrow={isPhone ? 1 : undefined} gap={0.25}>
1177
1673
  <Text font="title1" style={{ color: 'white' }}>
1178
1674
  {formatPrice(latestPrice)}
1179
1675
  </Text>
1180
- <Text font="label1" color="fgPositive" accessibilityLabel={`Up ${formatPercentChange(percentChange)}`}>
1676
+ <Text
1677
+ accessibilityLabel={`Up ${formatPercentChange(percentChange)}`}
1678
+ color="fgPositive"
1679
+ font="label1"
1680
+ >
1181
1681
  +{formatPercentChange(percentChange)}
1182
1682
  </Text>
1183
1683
  </VStack>
@@ -1189,6 +1689,9 @@ function BitcoinChartWithScrubberBeacon() {
1189
1689
  }}
1190
1690
  >
1191
1691
  <LineChart
1692
+ showArea
1693
+ accessibilityLabel={chartAccessibilityLabel}
1694
+ height={92}
1192
1695
  inset={{ left: 0, right: 18, bottom: 0, top: 0 }}
1193
1696
  series={[
1194
1697
  {
@@ -1197,16 +1700,234 @@ function BitcoinChartWithScrubberBeacon() {
1197
1700
  color: assets.btc.color,
1198
1701
  },
1199
1702
  ]}
1200
- showArea
1201
1703
  width="100%"
1202
- height={92}
1203
1704
  >
1204
- <Scrubber idlePulse styles={{ beacon: { stroke: 'white' } }} />
1705
+ <Scrubber
1706
+ idlePulse
1707
+ accessibilityLabel={scrubberAccessibilityLabel}
1708
+ styles={{ beacon: { stroke: 'white' } }}
1709
+ />
1205
1710
  </LineChart>
1206
1711
  </div>
1207
1712
  </VStack>
1208
1713
  );
1209
- };
1714
+ }
1715
+ ```
1716
+
1717
+ #### Service Availability
1718
+
1719
+ You can have irregular data points by passing in `data` to `xAxis`.
1720
+
1721
+ ```jsx live
1722
+ function ServiceAvailability() {
1723
+ const availabilityEvents = useMemo(
1724
+ () => [
1725
+ { date: new Date('2022-01-01'), availability: 79 },
1726
+ { date: new Date('2022-01-03'), availability: 81 },
1727
+ { date: new Date('2022-01-04'), availability: 82 },
1728
+ { date: new Date('2022-01-06'), availability: 91 },
1729
+ { date: new Date('2022-01-07'), availability: 92 },
1730
+ { date: new Date('2022-01-10'), availability: 86 },
1731
+ ],
1732
+ [],
1733
+ );
1734
+
1735
+ const chartAccessibilityLabel = `Availability chart showing ${availabilityEvents.length} data points over time`;
1736
+
1737
+ const scrubberAccessibilityLabel = useCallback(
1738
+ (index: number) => {
1739
+ const event = availabilityEvents[index];
1740
+ const formattedDate = event.date.toLocaleDateString('en-US', {
1741
+ weekday: 'short',
1742
+ month: 'short',
1743
+ day: 'numeric',
1744
+ year: 'numeric',
1745
+ });
1746
+ const status =
1747
+ event.availability >= 90 ? 'Good' : event.availability >= 85 ? 'Warning' : 'Critical';
1748
+ return `${formattedDate}: Availability ${event.availability}% - Status: ${status}`;
1749
+ },
1750
+ [availabilityEvents],
1751
+ );
1752
+
1753
+ return (
1754
+ <CartesianChart
1755
+ enableScrubbing
1756
+ accessibilityLabel={chartAccessibilityLabel}
1757
+ height={{ base: 200, tablet: 225, desktop: 250 }}
1758
+ series={[
1759
+ {
1760
+ id: 'availability',
1761
+ data: availabilityEvents.map((event) => event.availability),
1762
+ gradient: {
1763
+ stops: ({ min, max }) => [
1764
+ { offset: min, color: 'var(--color-fgNegative)' },
1765
+ { offset: 85, color: 'var(--color-fgNegative)' },
1766
+ { offset: 85, color: 'var(--color-fgWarning)' },
1767
+ { offset: 90, color: 'var(--color-fgWarning)' },
1768
+ { offset: 90, color: 'var(--color-fgPositive)' },
1769
+ { offset: max, color: 'var(--color-fgPositive)' },
1770
+ ],
1771
+ },
1772
+ },
1773
+ ]}
1774
+ xAxis={{
1775
+ data: availabilityEvents.map((event) => event.date.getTime()),
1776
+ }}
1777
+ yAxis={{
1778
+ domain: ({ min, max }) => ({ min: Math.max(min - 2, 0), max: Math.min(max + 2, 100) }),
1779
+ }}
1780
+ >
1781
+ <XAxis
1782
+ showGrid
1783
+ showLine
1784
+ showTickMarks
1785
+ tickLabelFormatter={(value) => new Date(value).toLocaleDateString()}
1786
+ />
1787
+ <YAxis
1788
+ showGrid
1789
+ showLine
1790
+ showTickMarks
1791
+ position="left"
1792
+ tickLabelFormatter={(value) => `${value}%`}
1793
+ />
1794
+ <Line
1795
+ curve="stepAfter"
1796
+ points={(props) => ({
1797
+ ...props,
1798
+ fill: 'var(--color-bg)',
1799
+ stroke: props.fill,
1800
+ })}
1801
+ seriesId="availability"
1802
+ />
1803
+ <Scrubber hideOverlay accessibilityLabel={scrubberAccessibilityLabel} />
1804
+ </CartesianChart>
1805
+ );
1806
+ }
1807
+ ```
1808
+
1809
+ #### Forecast Asset Price
1810
+
1811
+ You can combine multiple lines within a series to change styles dynamically.
1812
+
1813
+ ```jsx live
1814
+ function ForecastAssetPrice() {
1815
+ const startYear = 2020;
1816
+ const data = [50, 45, 47, 46, 54, 54, 60, 61, 63, 66, 70];
1817
+ const currentIndex = 6;
1818
+
1819
+ const strokeWidth = 3;
1820
+ // To prevent cutting off the edge of our lines
1821
+ const clipOffset = strokeWidth;
1822
+
1823
+ const axisFormatter = useCallback(
1824
+ (dataIndex: number) => {
1825
+ return startYear + dataIndex;
1826
+ },
1827
+ [startYear],
1828
+ );
1829
+
1830
+ const HistoricalLineComponent = memo((props: SolidLineProps) => {
1831
+ const { drawingArea, getXScale } = useCartesianChartContext();
1832
+ const xScale = getXScale();
1833
+
1834
+ if (!xScale || !drawingArea) return;
1835
+
1836
+ const currentX = xScale(currentIndex);
1837
+
1838
+ if (currentX === undefined) return;
1839
+
1840
+ return (
1841
+ <>
1842
+ <defs>
1843
+ <clipPath id="historical-clip">
1844
+ <rect
1845
+ height={drawingArea.height + clipOffset * 2}
1846
+ width={currentX + clipOffset - drawingArea.x}
1847
+ x={drawingArea.x - clipOffset}
1848
+ y={drawingArea.y - clipOffset}
1849
+ />
1850
+ </clipPath>
1851
+ </defs>
1852
+ <g clipPath="url(#historical-clip)">
1853
+ <SolidLine strokeWidth={strokeWidth} {...props} />
1854
+ </g>
1855
+ </>
1856
+ );
1857
+ });
1858
+
1859
+ // Since the solid and dotted line have different curves,
1860
+ // we need two separate line components. Otherwise we could
1861
+ // have one line component with SolidLine and DottedLine inside
1862
+ // of it and two clipPaths.
1863
+ const ForecastLineComponent = memo((props: DottedLineProps) => {
1864
+ const { drawingArea, getXScale } = useCartesianChartContext();
1865
+ const xScale = getXScale();
1866
+
1867
+ if (!xScale || !drawingArea) return;
1868
+
1869
+ const currentX = xScale(currentIndex);
1870
+
1871
+ if (currentX === undefined) return;
1872
+
1873
+ return (
1874
+ <>
1875
+ <defs>
1876
+ <clipPath id="forecast-clip">
1877
+ <rect
1878
+ height={drawingArea.height + clipOffset * 2}
1879
+ width={drawingArea.x + drawingArea.width - currentX + clipOffset * 2}
1880
+ x={currentX}
1881
+ y={drawingArea.y - clipOffset}
1882
+ />
1883
+ </clipPath>
1884
+ </defs>
1885
+ <g clipPath="url(#forecast-clip)">
1886
+ <DottedLine
1887
+ strokeDasharray={`0 ${strokeWidth * 2}`}
1888
+ strokeWidth={strokeWidth}
1889
+ {...props}
1890
+ />
1891
+ </g>
1892
+ </>
1893
+ );
1894
+ });
1895
+
1896
+ const CustomScrubber = memo(() => {
1897
+ const { scrubberPosition } = useScrubberContext();
1898
+ const isScrubbing = scrubberPosition !== undefined;
1899
+ // We need a fade in animation for the Scrubber
1900
+ return (
1901
+ <m.g
1902
+ animate={{ opacity: 1 }}
1903
+ initial={{ opacity: 0 }}
1904
+ transition={{ duration: 0.15, delay: 0.35 }}
1905
+ >
1906
+ <g style={{ opacity: isScrubbing ? 1 : 0 }}>
1907
+ <Scrubber hideOverlay />
1908
+ </g>
1909
+ <g style={{ opacity: isScrubbing ? 0 : 1 }}>
1910
+ <DefaultScrubberBeacon dataX={currentIndex} dataY={data[currentIndex]} seriesId="price" />
1911
+ </g>
1912
+ </m.g>
1913
+ );
1914
+ });
1915
+
1916
+ return (
1917
+ <CartesianChart
1918
+ enableScrubbing
1919
+ height={{ base: 200, tablet: 225, desktop: 250 }}
1920
+ maxWidth={512}
1921
+ series={[{ id: 'price', data, color: assets.btc.color }]}
1922
+ style={{ margin: '0 auto' }}
1923
+ >
1924
+ <Line LineComponent={HistoricalLineComponent} curve="linear" seriesId="price" />
1925
+ <Line LineComponent={ForecastLineComponent} curve="monotone" seriesId="price" type="dotted" />
1926
+ <XAxis position="bottom" requestedTickCount={3} tickLabelFormatter={axisFormatter} />
1927
+ <CustomScrubber />
1928
+ </CartesianChart>
1929
+ );
1930
+ }
1210
1931
  ```
1211
1932
 
1212
1933
  ## Props
@@ -1215,33 +1936,14 @@ function BitcoinChartWithScrubberBeacon() {
1215
1936
  | --- | --- | --- | --- | --- |
1216
1937
  | `AreaComponent` | `AreaComponent` | No | `-` | Custom component to render line area fill. |
1217
1938
  | `LineComponent` | `LineComponent` | No | `-` | Component to render the line. Takes precedence over the type prop if provided. |
1218
- | `accentHeight` | `string \| number` | No | `-` | - |
1219
- | `accumulate` | `none \| sum` | No | `-` | - |
1220
- | `additive` | `sum \| replace` | No | `-` | - |
1221
- | `alignContent` | `ResponsiveProp<center \| end \| normal \| start \| flex-start \| flex-end \| space-between \| space-around \| space-evenly \| stretch \| baseline \| first baseline \| last baseline>` | No | `-` | - |
1222
- | `alignItems` | `ResponsiveProp<center \| end \| normal \| start \| flex-start \| flex-end \| stretch \| baseline \| first baseline \| last baseline \| self-start \| self-end>` | No | `-` | - |
1223
- | `alignSelf` | `ResponsiveProp<center \| end \| normal \| auto \| start \| flex-start \| flex-end \| stretch \| baseline \| first baseline \| last baseline \| self-start \| self-end>` | No | `-` | - |
1224
- | `alignmentBaseline` | `alphabetic \| hanging \| ideographic \| mathematical \| inherit \| auto \| baseline \| before-edge \| text-before-edge \| middle \| central \| after-edge \| text-after-edge` | No | `-` | - |
1225
- | `allowReorder` | `no \| yes` | No | `-` | - |
1226
- | `alphabetic` | `string \| number` | No | `-` | - |
1227
- | `amplitude` | `string \| number` | No | `-` | - |
1939
+ | `alignContent` | `ResponsiveProp<center \| normal \| start \| end \| flex-start \| flex-end \| space-between \| space-around \| space-evenly \| stretch \| baseline \| first baseline \| last baseline>` | No | `-` | - |
1940
+ | `alignItems` | `ResponsiveProp<center \| normal \| start \| end \| flex-start \| flex-end \| stretch \| baseline \| first baseline \| last baseline \| self-start \| self-end>` | No | `-` | - |
1941
+ | `alignSelf` | `ResponsiveProp<center \| auto \| normal \| start \| end \| flex-start \| flex-end \| stretch \| baseline \| first baseline \| last baseline \| self-start \| self-end>` | No | `-` | - |
1228
1942
  | `animate` | `boolean` | No | `true` | Whether to animate the chart. |
1229
- | `arabicForm` | `initial \| medial \| terminal \| isolated` | No | `-` | - |
1230
1943
  | `areaType` | `dotted \| solid \| gradient` | No | `'gradient'` | The type of area fill to add to the line. |
1231
- | `as` | `svg` | No | `-` | - |
1232
- | `ascent` | `string \| number` | No | `-` | - |
1233
- | `aspectRatio` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| auto` | No | `-` | - |
1234
- | `attributeName` | `string` | No | `-` | - |
1235
- | `attributeType` | `string` | No | `-` | - |
1236
- | `autoReverse` | `false \| true \| true \| false` | No | `-` | - |
1237
- | `azimuth` | `string \| number` | No | `-` | - |
1944
+ | `as` | `div` | No | `-` | - |
1945
+ | `aspectRatio` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
1238
1946
  | `background` | `currentColor \| fg \| fgMuted \| fgInverse \| fgPrimary \| fgWarning \| fgPositive \| fgNegative \| bg \| bgAlternate \| bgInverse \| bgOverlay \| bgElevation1 \| bgElevation2 \| bgPrimary \| bgPrimaryWash \| bgSecondary \| bgTertiary \| bgSecondaryWash \| bgNegative \| bgNegativeWash \| bgPositive \| bgPositiveWash \| bgWarning \| bgWarningWash \| bgLine \| bgLineHeavy \| bgLineInverse \| bgLinePrimary \| bgLinePrimarySubtle \| accentSubtleRed \| accentBoldRed \| accentSubtleGreen \| accentBoldGreen \| accentSubtleBlue \| accentBoldBlue \| accentSubtlePurple \| accentBoldPurple \| accentSubtleYellow \| accentBoldYellow \| accentSubtleGray \| accentBoldGray \| transparent` | No | `-` | - |
1239
- | `baseFrequency` | `string \| number` | No | `-` | - |
1240
- | `baseProfile` | `string \| number` | No | `-` | - |
1241
- | `baselineShift` | `string \| number` | No | `-` | - |
1242
- | `bbox` | `string \| number` | No | `-` | - |
1243
- | `begin` | `string \| number` | No | `-` | - |
1244
- | `bias` | `string \| number` | No | `-` | - |
1245
1947
  | `borderBottomLeftRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
1246
1948
  | `borderBottomRightRadius` | `0 \| 100 \| 200 \| 300 \| 400 \| 500 \| 600 \| 700 \| 800 \| 900 \| 1000` | No | `-` | - |
1247
1949
  | `borderBottomWidth` | `0 \| 100 \| 200 \| 300 \| 400 \| 500` | No | `-` | - |
@@ -1261,316 +1963,96 @@ function BitcoinChartWithScrubberBeacon() {
1261
1963
  | `borderedTop` | `boolean` | No | `-` | Add a border to the top side of the box. |
1262
1964
  | `borderedVertical` | `boolean` | No | `-` | Add a border to the top and bottom sides of the box. |
1263
1965
  | `bottom` | `ResponsiveProp<Bottom<string \| number>>` | No | `-` | - |
1264
- | `by` | `string \| number` | No | `-` | - |
1265
- | `calcMode` | `string \| number` | No | `-` | - |
1266
- | `capHeight` | `string \| number` | No | `-` | - |
1267
- | `className` | `string` | No | `-` | - |
1268
- | `clip` | `string \| number` | No | `-` | - |
1269
- | `clipPath` | `string` | No | `-` | - |
1270
- | `clipPathUnits` | `string \| number` | No | `-` | - |
1271
- | `clipRule` | `string \| number` | No | `-` | - |
1966
+ | `classNames` | `{ root?: string; chart?: string \| undefined; } \| undefined` | No | `-` | Custom class names for the component. |
1272
1967
  | `color` | `currentColor \| fg \| fgMuted \| fgInverse \| fgPrimary \| fgWarning \| fgPositive \| fgNegative \| bg \| bgAlternate \| bgInverse \| bgOverlay \| bgElevation1 \| bgElevation2 \| bgPrimary \| bgPrimaryWash \| bgSecondary \| bgTertiary \| bgSecondaryWash \| bgNegative \| bgNegativeWash \| bgPositive \| bgPositiveWash \| bgWarning \| bgWarningWash \| bgLine \| bgLineHeavy \| bgLineInverse \| bgLinePrimary \| bgLinePrimarySubtle \| accentSubtleRed \| accentBoldRed \| accentSubtleGreen \| accentBoldGreen \| accentSubtleBlue \| accentBoldBlue \| accentSubtlePurple \| accentBoldPurple \| accentSubtleYellow \| accentBoldYellow \| accentSubtleGray \| accentBoldGray \| transparent` | No | `-` | - |
1273
- | `colorInterpolation` | `string \| number` | No | `-` | - |
1274
- | `colorInterpolationFilters` | `inherit \| auto \| sRGB \| linearRGB` | No | `-` | - |
1275
- | `colorProfile` | `string \| number` | No | `-` | - |
1276
- | `colorRendering` | `string \| number` | No | `-` | - |
1277
- | `columnGap` | `0 \| 5 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 6 \| 7 \| 8 \| 9` | No | `-` | - |
1278
- | `contentScriptType` | `string \| number` | No | `-` | - |
1279
- | `contentStyleType` | `string \| number` | No | `-` | - |
1280
- | `crossOrigin` | ` \| anonymous \| use-credentials` | No | `-` | - |
1281
- | `cursor` | `string \| number` | No | `-` | - |
1282
- | `curve` | `bump \| catmullRom \| linear \| linearClosed \| monotone \| natural \| step \| stepBefore \| stepAfter` | No | `'linear'` | The curve interpolation method to use for the line. |
1283
- | `cx` | `string \| number` | No | `-` | - |
1284
- | `cy` | `string \| number` | No | `-` | - |
1285
- | `d` | `string` | No | `-` | - |
1968
+ | `columnGap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
1969
+ | `connectNulls` | `boolean` | No | `-` | When true, the area is connected across null values. |
1970
+ | `curve` | `bump \| catmullRom \| linear \| linearClosed \| monotone \| natural \| step \| stepBefore \| stepAfter` | No | `'bump'` | The curve interpolation method to use for the line. |
1286
1971
  | `dangerouslySetBackground` | `string` | No | `-` | - |
1287
- | `decelerate` | `string \| number` | No | `-` | - |
1288
- | `descent` | `string \| number` | No | `-` | - |
1289
- | `diffuseConstant` | `string \| number` | No | `-` | - |
1290
- | `direction` | `string \| number` | No | `-` | - |
1291
- | `display` | `ResponsiveProp<grid \| revert \| none \| block \| inline \| inline-block \| flex \| inline-flex \| inline-grid \| contents \| flow-root \| list-item>` | No | `-` | - |
1292
- | `divisor` | `string \| number` | No | `-` | - |
1293
- | `dominantBaseline` | `string \| number` | No | `-` | - |
1294
- | `dur` | `string \| number` | No | `-` | - |
1295
- | `dx` | `string \| number` | No | `-` | - |
1296
- | `dy` | `string \| number` | No | `-` | - |
1297
- | `edgeMode` | `string \| number` | No | `-` | - |
1972
+ | `display` | `ResponsiveProp<grid \| none \| block \| inline \| inline-block \| flex \| inline-flex \| inline-grid \| contents \| flow-root \| revert \| list-item>` | No | `-` | - |
1298
1973
  | `elevation` | `0 \| 1 \| 2` | No | `-` | - |
1299
- | `enableBackground` | `string \| number` | No | `-` | - |
1300
1974
  | `enableScrubbing` | `boolean` | No | `-` | Enables scrubbing interactions. When true, allows scrubbing and makes scrubber components interactive. |
1301
- | `end` | `string \| number` | No | `-` | - |
1302
- | `exponent` | `string \| number` | No | `-` | - |
1303
- | `externalResourcesRequired` | `false \| true \| true \| false` | No | `-` | - |
1304
- | `fill` | `string` | No | `-` | - |
1305
- | `fillOpacity` | `string \| number` | No | `-` | - |
1306
- | `fillRule` | `inherit \| nonzero \| evenodd` | No | `-` | - |
1307
- | `filter` | `string` | No | `-` | - |
1308
- | `filterRes` | `string \| number` | No | `-` | - |
1309
- | `filterUnits` | `string \| number` | No | `-` | - |
1310
1975
  | `flexBasis` | `ResponsiveProp<FlexBasis<string \| number>>` | No | `-` | - |
1311
1976
  | `flexDirection` | `ResponsiveProp<row \| row-reverse \| column \| column-reverse>` | No | `-` | - |
1312
- | `flexGrow` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset` | No | `-` | - |
1313
- | `flexShrink` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset` | No | `-` | - |
1977
+ | `flexGrow` | `inherit \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
1978
+ | `flexShrink` | `inherit \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
1314
1979
  | `flexWrap` | `ResponsiveProp<nowrap \| wrap \| wrap-reverse>` | No | `-` | - |
1315
- | `floodColor` | `string \| number` | No | `-` | - |
1316
- | `floodOpacity` | `string \| number` | No | `-` | - |
1317
- | `focusable` | `auto \| Booleanish` | No | `-` | - |
1318
1980
  | `font` | `ResponsiveProp<FontFamily \| inherit>` | No | `-` | - |
1319
1981
  | `fontFamily` | `ResponsiveProp<FontFamily \| inherit>` | No | `-` | - |
1320
- | `fontSize` | `ResponsiveProp<FontSize \| inherit>` | No | `-` | - |
1321
- | `fontSizeAdjust` | `string \| number` | No | `-` | - |
1322
- | `fontStretch` | `string \| number` | No | `-` | - |
1323
- | `fontStyle` | `string \| number` | No | `-` | - |
1324
- | `fontVariant` | `string \| number` | No | `-` | - |
1325
- | `fontWeight` | `ResponsiveProp<FontWeight \| inherit>` | No | `-` | - |
1326
- | `format` | `string \| number` | No | `-` | - |
1327
- | `fr` | `string \| number` | No | `-` | - |
1328
- | `from` | `string \| number` | No | `-` | - |
1329
- | `fx` | `string \| number` | No | `-` | - |
1330
- | `fy` | `string \| number` | No | `-` | - |
1331
- | `g1` | `string \| number` | No | `-` | - |
1332
- | `g2` | `string \| number` | No | `-` | - |
1333
- | `gap` | `0 \| 5 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 6 \| 7 \| 8 \| 9` | No | `-` | - |
1334
- | `glyphName` | `string \| number` | No | `-` | - |
1335
- | `glyphOrientationHorizontal` | `string \| number` | No | `-` | - |
1336
- | `glyphOrientationVertical` | `string \| number` | No | `-` | - |
1337
- | `glyphRef` | `string \| number` | No | `-` | - |
1338
- | `gradientTransform` | `string` | No | `-` | - |
1339
- | `gradientUnits` | `string` | No | `-` | - |
1340
- | `grid` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| none` | No | `-` | - |
1341
- | `gridArea` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| auto` | No | `-` | - |
1982
+ | `fontSize` | `ResponsiveProp<inherit \| FontSize>` | No | `-` | - |
1983
+ | `fontWeight` | `ResponsiveProp<inherit \| FontWeight>` | No | `-` | - |
1984
+ | `gap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
1985
+ | `grid` | `inherit \| none \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
1986
+ | `gridArea` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
1342
1987
  | `gridAutoColumns` | `ResponsiveProp<GridAutoColumns<string \| number>>` | No | `-` | - |
1343
- | `gridAutoFlow` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| row \| column \| dense` | No | `-` | - |
1988
+ | `gridAutoFlow` | `inherit \| revert \| row \| column \| -moz-initial \| initial \| revert-layer \| unset \| dense` | No | `-` | - |
1344
1989
  | `gridAutoRows` | `ResponsiveProp<GridAutoRows<string \| number>>` | No | `-` | - |
1345
- | `gridColumn` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| auto` | No | `-` | - |
1346
- | `gridColumnEnd` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| auto` | No | `-` | - |
1347
- | `gridColumnStart` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| auto` | No | `-` | - |
1348
- | `gridRow` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| auto` | No | `-` | - |
1349
- | `gridRowEnd` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| auto` | No | `-` | - |
1350
- | `gridRowStart` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| auto` | No | `-` | - |
1351
- | `gridTemplate` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| none` | No | `-` | - |
1352
- | `gridTemplateAreas` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| none` | No | `-` | - |
1990
+ | `gridColumn` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
1991
+ | `gridColumnEnd` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
1992
+ | `gridColumnStart` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
1993
+ | `gridRow` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
1994
+ | `gridRowEnd` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
1995
+ | `gridRowStart` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
1996
+ | `gridTemplate` | `inherit \| none \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
1997
+ | `gridTemplateAreas` | `inherit \| none \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
1353
1998
  | `gridTemplateColumns` | `ResponsiveProp<GridTemplateColumns<string \| number>>` | No | `-` | - |
1354
1999
  | `gridTemplateRows` | `ResponsiveProp<GridTemplateRows<string \| number>>` | No | `-` | - |
1355
- | `hanging` | `string \| number` | No | `-` | - |
1356
2000
  | `height` | `ResponsiveProp<Height<string \| number>>` | No | `-` | - |
1357
- | `horizAdvX` | `string \| number` | No | `-` | - |
1358
- | `horizOriginX` | `string \| number` | No | `-` | - |
1359
- | `href` | `string` | No | `-` | - |
1360
- | `id` | `string` | No | `-` | - |
1361
- | `ideographic` | `string \| number` | No | `-` | - |
1362
- | `imageRendering` | `string \| number` | No | `-` | - |
1363
- | `in` | `string` | No | `-` | - |
1364
- | `in2` | `string \| number` | No | `-` | - |
1365
2001
  | `inset` | `number \| Partial<ChartInset>` | No | `-` | Inset around the entire chart (outside the axes). |
1366
- | `intercept` | `string \| number` | No | `-` | - |
1367
- | `justifyContent` | `ResponsiveProp<left \| right \| center \| end \| normal \| start \| flex-start \| flex-end \| space-between \| space-around \| space-evenly \| stretch>` | No | `-` | - |
1368
- | `k` | `string \| number` | No | `-` | - |
1369
- | `k1` | `string \| number` | No | `-` | - |
1370
- | `k2` | `string \| number` | No | `-` | - |
1371
- | `k3` | `string \| number` | No | `-` | - |
1372
- | `k4` | `string \| number` | No | `-` | - |
1373
- | `kernelMatrix` | `string \| number` | No | `-` | - |
1374
- | `kernelUnitLength` | `string \| number` | No | `-` | - |
1375
- | `kerning` | `string \| number` | No | `-` | - |
2002
+ | `justifyContent` | `ResponsiveProp<left \| right \| center \| normal \| start \| end \| flex-start \| flex-end \| space-between \| space-around \| space-evenly \| stretch>` | No | `-` | - |
1376
2003
  | `key` | `Key \| null` | No | `-` | - |
1377
- | `keyPoints` | `string \| number` | No | `-` | - |
1378
- | `keySplines` | `string \| number` | No | `-` | - |
1379
- | `keyTimes` | `string \| number` | No | `-` | - |
1380
- | `lang` | `string` | No | `-` | - |
1381
2004
  | `left` | `ResponsiveProp<Left<string \| number>>` | No | `-` | - |
1382
- | `lengthAdjust` | `string \| number` | No | `-` | - |
1383
- | `letterSpacing` | `string \| number` | No | `-` | - |
1384
- | `lightingColor` | `string \| number` | No | `-` | - |
1385
- | `limitingConeAngle` | `string \| number` | No | `-` | - |
1386
- | `lineHeight` | `ResponsiveProp<LineHeight \| inherit>` | No | `-` | - |
1387
- | `local` | `string \| number` | No | `-` | - |
1388
- | `margin` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
1389
- | `marginBottom` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
1390
- | `marginEnd` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
1391
- | `marginStart` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
1392
- | `marginTop` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
1393
- | `marginX` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
1394
- | `marginY` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
1395
- | `markerEnd` | `string` | No | `-` | - |
1396
- | `markerHeight` | `string \| number` | No | `-` | - |
1397
- | `markerMid` | `string` | No | `-` | - |
1398
- | `markerStart` | `string` | No | `-` | - |
1399
- | `markerUnits` | `string \| number` | No | `-` | - |
1400
- | `markerWidth` | `string \| number` | No | `-` | - |
1401
- | `mask` | `string` | No | `-` | - |
1402
- | `maskContentUnits` | `string \| number` | No | `-` | - |
1403
- | `maskUnits` | `string \| number` | No | `-` | - |
1404
- | `mathematical` | `string \| number` | No | `-` | - |
1405
- | `max` | `string \| number` | No | `-` | - |
2005
+ | `lineHeight` | `ResponsiveProp<inherit \| LineHeight>` | No | `-` | - |
2006
+ | `margin` | `ResponsiveProp<0 \| -1 \| -2 \| -0.25 \| -0.5 \| -0.75 \| -1.5 \| -3 \| -4 \| -5 \| -6 \| -7 \| -8 \| -9 \| -10>` | No | `-` | - |
2007
+ | `marginBottom` | `ResponsiveProp<0 \| -1 \| -2 \| -0.25 \| -0.5 \| -0.75 \| -1.5 \| -3 \| -4 \| -5 \| -6 \| -7 \| -8 \| -9 \| -10>` | No | `-` | - |
2008
+ | `marginEnd` | `ResponsiveProp<0 \| -1 \| -2 \| -0.25 \| -0.5 \| -0.75 \| -1.5 \| -3 \| -4 \| -5 \| -6 \| -7 \| -8 \| -9 \| -10>` | No | `-` | - |
2009
+ | `marginStart` | `ResponsiveProp<0 \| -1 \| -2 \| -0.25 \| -0.5 \| -0.75 \| -1.5 \| -3 \| -4 \| -5 \| -6 \| -7 \| -8 \| -9 \| -10>` | No | `-` | - |
2010
+ | `marginTop` | `ResponsiveProp<0 \| -1 \| -2 \| -0.25 \| -0.5 \| -0.75 \| -1.5 \| -3 \| -4 \| -5 \| -6 \| -7 \| -8 \| -9 \| -10>` | No | `-` | - |
2011
+ | `marginX` | `ResponsiveProp<0 \| -1 \| -2 \| -0.25 \| -0.5 \| -0.75 \| -1.5 \| -3 \| -4 \| -5 \| -6 \| -7 \| -8 \| -9 \| -10>` | No | `-` | - |
2012
+ | `marginY` | `ResponsiveProp<0 \| -1 \| -2 \| -0.25 \| -0.5 \| -0.75 \| -1.5 \| -3 \| -4 \| -5 \| -6 \| -7 \| -8 \| -9 \| -10>` | No | `-` | - |
1406
2013
  | `maxHeight` | `ResponsiveProp<MaxHeight<string \| number>>` | No | `-` | - |
1407
2014
  | `maxWidth` | `ResponsiveProp<MaxWidth<string \| number>>` | No | `-` | - |
1408
- | `media` | `string` | No | `-` | - |
1409
- | `method` | `string` | No | `-` | - |
1410
- | `min` | `string \| number` | No | `-` | - |
1411
2015
  | `minHeight` | `ResponsiveProp<MinHeight<string \| number>>` | No | `-` | - |
1412
2016
  | `minWidth` | `ResponsiveProp<MinWidth<string \| number>>` | No | `-` | - |
1413
- | `mode` | `string \| number` | No | `-` | - |
1414
- | `name` | `string` | No | `-` | - |
1415
- | `numOctaves` | `string \| number` | No | `-` | - |
1416
- | `offset` | `string \| number` | No | `-` | - |
1417
- | `onChange` | `FormEventHandler<SVGSVGElement>` | No | `-` | - |
1418
- | `onPointClick` | `((event: MouseEvent<Element, MouseEvent>, point: { x: number; y: number; dataX: number; dataY: number; }) => void)` | No | `-` | Handler for when a dot is clicked. Automatically makes dots appear pressable when provided. |
2017
+ | `onChange` | `FormEventHandler<HTMLDivElement>` | No | `-` | - |
2018
+ | `onPointClick` | `((event: MouseEvent<Element, MouseEvent>, point: { x: number; y: number; dataX: number; dataY: number; }) => void)` | No | `-` | Handler for when a point is clicked. Passed through to Point components rendered via points. |
1419
2019
  | `onScrubberPositionChange` | `((index: number) => void) \| undefined` | No | `-` | Callback fired when the scrubber position changes. Receives the dataIndex of the scrubber or undefined when not scrubbing. |
1420
- | `opacity` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset` | No | `-` | - |
1421
- | `operator` | `string \| number` | No | `-` | - |
1422
- | `order` | `string \| number` | No | `-` | - |
1423
- | `orient` | `string \| number` | No | `-` | - |
1424
- | `orientation` | `string \| number` | No | `-` | - |
1425
- | `origin` | `string \| number` | No | `-` | - |
1426
- | `overflow` | `ResponsiveProp<clip \| auto \| visible \| hidden \| scroll>` | No | `-` | - |
1427
- | `overlinePosition` | `string \| number` | No | `-` | - |
1428
- | `overlineThickness` | `string \| number` | No | `-` | - |
1429
- | `padding` | `0 \| 5 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 6 \| 7 \| 8 \| 9` | No | `-` | - |
1430
- | `paddingBottom` | `0 \| 5 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 6 \| 7 \| 8 \| 9` | No | `-` | - |
1431
- | `paddingEnd` | `0 \| 5 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 6 \| 7 \| 8 \| 9` | No | `-` | - |
1432
- | `paddingStart` | `0 \| 5 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 6 \| 7 \| 8 \| 9` | No | `-` | - |
1433
- | `paddingTop` | `0 \| 5 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 6 \| 7 \| 8 \| 9` | No | `-` | - |
1434
- | `paddingX` | `0 \| 5 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 6 \| 7 \| 8 \| 9` | No | `-` | - |
1435
- | `paddingY` | `0 \| 5 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 6 \| 7 \| 8 \| 9` | No | `-` | - |
1436
- | `paintOrder` | `string \| number` | No | `-` | - |
1437
- | `panose1` | `string \| number` | No | `-` | - |
1438
- | `path` | `string` | No | `-` | - |
1439
- | `pathLength` | `string \| number` | No | `-` | - |
1440
- | `patternContentUnits` | `string` | No | `-` | - |
1441
- | `patternTransform` | `string \| number` | No | `-` | - |
1442
- | `patternUnits` | `string` | No | `-` | - |
2020
+ | `opacity` | `number \| ({ base?: Opacity; phone?: Opacity \| undefined; tablet?: Opacity \| undefined; desktop?: Opacity \| undefined; } & number) \| undefined` | No | `1` | Opacity of the lines stroke. Will also be applied to points and area fill. |
2021
+ | `overflow` | `ResponsiveProp<hidden \| auto \| visible \| clip \| scroll>` | No | `-` | - |
2022
+ | `padding` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
2023
+ | `paddingBottom` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
2024
+ | `paddingEnd` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
2025
+ | `paddingStart` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
2026
+ | `paddingTop` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
2027
+ | `paddingX` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
2028
+ | `paddingY` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
1443
2029
  | `pin` | `top \| bottom \| left \| right \| all` | No | `-` | Direction in which to absolutely pin the box. |
1444
- | `pointerEvents` | `string \| number` | No | `-` | - |
1445
- | `points` | `string` | No | `-` | - |
1446
- | `pointsAtX` | `string \| number` | No | `-` | - |
1447
- | `pointsAtY` | `string \| number` | No | `-` | - |
1448
- | `pointsAtZ` | `string \| number` | No | `-` | - |
2030
+ | `points` | `boolean \| ((defaults: PointBaseProps) => boolean \| Partial<PointProps> \| null) \| undefined` | No | `-` | Controls whether and how to render points at each data point in the series. - true: Show all points with default styling - false or undefined: Hide all points - Function: Called for every entry in the data array to customize individual points |
1449
2031
  | `position` | `ResponsiveProp<static \| relative \| absolute \| fixed \| sticky>` | No | `-` | - |
1450
- | `preserveAlpha` | `false \| true \| true \| false` | No | `-` | - |
1451
- | `preserveAspectRatio` | `string` | No | `-` | - |
1452
- | `primitiveUnits` | `string \| number` | No | `-` | - |
1453
- | `r` | `string \| number` | No | `-` | - |
1454
- | `radius` | `string \| number` | No | `-` | - |
1455
2032
  | `ref` | `((instance: SVGSVGElement \| null) => void) \| RefObject<SVGSVGElement> \| null` | No | `-` | - |
1456
- | `refX` | `string \| number` | No | `-` | - |
1457
- | `refY` | `string \| number` | No | `-` | - |
1458
- | `renderPoints` | `((params: RenderPointsParams) => boolean \| PointConfig \| null) \| undefined` | No | `-` | Callback function to determine how to render points at each data point in the series. Called for every entry in the data array. |
1459
- | `renderingIntent` | `string \| number` | No | `-` | - |
1460
- | `repeatCount` | `string \| number` | No | `-` | - |
1461
- | `repeatDur` | `string \| number` | No | `-` | - |
1462
- | `requiredExtensions` | `string \| number` | No | `-` | - |
1463
- | `requiredFeatures` | `string \| number` | No | `-` | - |
1464
- | `restart` | `string \| number` | No | `-` | - |
1465
- | `result` | `string` | No | `-` | - |
1466
2033
  | `right` | `ResponsiveProp<Right<string \| number>>` | No | `-` | - |
1467
- | `role` | `grid \| article \| button \| dialog \| figure \| form \| img \| link \| main \| menu \| menuitem \| option \| search \| table \| switch \| string & {} \| none \| row \| alert \| alertdialog \| application \| banner \| cell \| checkbox \| columnheader \| combobox \| complementary \| contentinfo \| definition \| directory \| document \| feed \| gridcell \| group \| heading \| list \| listbox \| listitem \| log \| marquee \| math \| menubar \| menuitemcheckbox \| menuitemradio \| navigation \| note \| presentation \| progressbar \| radio \| radiogroup \| region \| rowgroup \| rowheader \| scrollbar \| searchbox \| separator \| slider \| spinbutton \| status \| tab \| tablist \| tabpanel \| term \| textbox \| timer \| toolbar \| tooltip \| tree \| treegrid \| treeitem` | No | `-` | - |
1468
- | `rotate` | `string \| number` | No | `-` | - |
1469
- | `rowGap` | `0 \| 5 \| 10 \| 0.25 \| 0.5 \| 0.75 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 6 \| 7 \| 8 \| 9` | No | `-` | - |
1470
- | `rx` | `string \| number` | No | `-` | - |
1471
- | `ry` | `string \| number` | No | `-` | - |
1472
- | `scale` | `string \| number` | No | `-` | - |
1473
- | `seed` | `string \| number` | No | `-` | - |
2034
+ | `rowGap` | `0 \| 1 \| 2 \| 0.25 \| 0.5 \| 0.75 \| 1.5 \| 3 \| 4 \| 5 \| 6 \| 7 \| 8 \| 9 \| 10` | No | `-` | - |
1474
2035
  | `series` | `LineSeries[]` | No | `-` | Configuration objects that define how to visualize the data. Each series supports Line component props for individual customization. |
1475
- | `shapeRendering` | `string \| number` | No | `-` | - |
1476
- | `showArea` | `boolean` | No | `-` | Show area fill under the line. |
2036
+ | `showArea` | `boolean` | No | `-` | Whether to show area fill under the line. |
1477
2037
  | `showXAxis` | `boolean` | No | `-` | Whether to show the X axis. |
1478
2038
  | `showYAxis` | `boolean` | No | `-` | Whether to show the Y axis. |
1479
- | `slope` | `string \| number` | No | `-` | - |
1480
- | `spacing` | `string \| number` | No | `-` | - |
1481
- | `specularConstant` | `string \| number` | No | `-` | - |
1482
- | `specularExponent` | `string \| number` | No | `-` | - |
1483
- | `speed` | `string \| number` | No | `-` | - |
1484
- | `spreadMethod` | `string` | No | `-` | - |
1485
- | `startOffset` | `string \| number` | No | `-` | - |
1486
- | `stdDeviation` | `string \| number` | No | `-` | - |
1487
- | `stemh` | `string \| number` | No | `-` | - |
1488
- | `stemv` | `string \| number` | No | `-` | - |
1489
- | `stitchTiles` | `string \| number` | No | `-` | - |
1490
- | `stopColor` | `string` | No | `-` | - |
1491
- | `stopOpacity` | `string \| number` | No | `-` | - |
1492
- | `strikethroughPosition` | `string \| number` | No | `-` | - |
1493
- | `strikethroughThickness` | `string \| number` | No | `-` | - |
1494
- | `string` | `string \| number` | No | `-` | - |
1495
- | `stroke` | `string` | No | `-` | - |
1496
- | `strokeDasharray` | `string \| number` | No | `-` | - |
1497
- | `strokeDashoffset` | `string \| number` | No | `-` | - |
1498
- | `strokeLinecap` | `inherit \| butt \| round \| square` | No | `-` | - |
1499
- | `strokeLinejoin` | `inherit \| round \| miter \| bevel` | No | `-` | - |
1500
- | `strokeMiterlimit` | `string \| number` | No | `-` | - |
1501
- | `strokeOpacity` | `string \| number` | No | `-` | - |
1502
- | `strokeWidth` | `number` | No | `-` | - |
1503
- | `style` | `CSSProperties` | No | `-` | - |
1504
- | `suppressHydrationWarning` | `boolean` | No | `-` | - |
1505
- | `surfaceScale` | `string \| number` | No | `-` | - |
1506
- | `systemLanguage` | `string \| number` | No | `-` | - |
1507
- | `tabIndex` | `number` | No | `-` | - |
1508
- | `tableValues` | `string \| number` | No | `-` | - |
1509
- | `target` | `string` | No | `-` | - |
1510
- | `targetX` | `string \| number` | No | `-` | - |
1511
- | `targetY` | `string \| number` | No | `-` | - |
2039
+ | `strokeOpacity` | `number` | No | `1` | Opacity of the line |
2040
+ | `strokeWidth` | `number` | No | `2` | Width of the line |
2041
+ | `style` | `CSSProperties` | No | `-` | Custom styles for the root element. |
2042
+ | `styles` | `{ root?: CSSProperties; chart?: CSSProperties \| undefined; } \| undefined` | No | `-` | Custom styles for the component. |
1512
2043
  | `testID` | `string` | No | `-` | Used to locate this element in unit and end-to-end tests. Under the hood, testID translates to data-testid on Web. On Mobile, testID stays the same - testID |
1513
- | `textAlign` | `ResponsiveProp<center \| end \| start \| justify>` | No | `-` | - |
1514
- | `textAnchor` | `string` | No | `-` | - |
2044
+ | `textAlign` | `ResponsiveProp<center \| start \| end \| justify>` | No | `-` | - |
1515
2045
  | `textDecoration` | `ResponsiveProp<none \| underline \| overline \| line-through \| underline overline \| underline double>` | No | `-` | - |
1516
- | `textLength` | `string \| number` | No | `-` | - |
1517
- | `textRendering` | `string \| number` | No | `-` | - |
1518
- | `textTransform` | `ResponsiveProp<capitalize \| lowercase \| none \| uppercase>` | No | `-` | - |
1519
- | `to` | `string \| number` | No | `-` | - |
2046
+ | `textTransform` | `ResponsiveProp<none \| uppercase \| lowercase \| capitalize>` | No | `-` | - |
1520
2047
  | `top` | `ResponsiveProp<Top<string \| number>>` | No | `-` | - |
1521
- | `transform` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| none` | No | `-` | - |
1522
- | `type` | `dotted \| solid \| gradient` | No | `'solid'` | The type of line to render. |
1523
- | `u1` | `string \| number` | No | `-` | - |
1524
- | `u2` | `string \| number` | No | `-` | - |
1525
- | `underlinePosition` | `string \| number` | No | `-` | - |
1526
- | `underlineThickness` | `string \| number` | No | `-` | - |
1527
- | `unicode` | `string \| number` | No | `-` | - |
1528
- | `unicodeBidi` | `string \| number` | No | `-` | - |
1529
- | `unicodeRange` | `string \| number` | No | `-` | - |
1530
- | `unitsPerEm` | `string \| number` | No | `-` | - |
1531
- | `userSelect` | `ResponsiveProp<text \| none \| auto \| all>` | No | `-` | - |
1532
- | `vAlphabetic` | `string \| number` | No | `-` | - |
1533
- | `vHanging` | `string \| number` | No | `-` | - |
1534
- | `vIdeographic` | `string \| number` | No | `-` | - |
1535
- | `vMathematical` | `string \| number` | No | `-` | - |
1536
- | `values` | `string` | No | `-` | - |
1537
- | `vectorEffect` | `string \| number` | No | `-` | - |
1538
- | `version` | `string` | No | `-` | - |
1539
- | `vertAdvY` | `string \| number` | No | `-` | - |
1540
- | `vertOriginX` | `string \| number` | No | `-` | - |
1541
- | `vertOriginY` | `string \| number` | No | `-` | - |
1542
- | `viewBox` | `string` | No | `-` | - |
1543
- | `viewTarget` | `string \| number` | No | `-` | - |
1544
- | `visibility` | `ResponsiveProp<visible \| hidden>` | No | `-` | - |
2048
+ | `transform` | `inherit \| none \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
2049
+ | `transition` | `Orchestration & Repeat & Tween \| Orchestration & Repeat & Spring \| Orchestration & Repeat & Keyframes \| Orchestration & Repeat & Inertia \| Orchestration & Repeat & Just \| Orchestration & Repeat & None \| Orchestration & Repeat & PermissiveTransitionDefinition \| Orchestration & Repeat & Tween & { [key: string]: TransitionDefinition; } \| Orchestration & Repeat & Spring & { [key: string]: TransitionDefinition; } \| Orchestration & Repeat & Keyframes & { [key: string]: TransitionDefinition; } \| Orchestration & Repeat & Inertia & { [key: string]: TransitionDefinition; } \| Orchestration & Repeat & Just & { [key: string]: TransitionDefinition; } \| Orchestration & Repeat & None & { [key: string]: TransitionDefinition; } \| Orchestration & Repeat & PermissiveTransitionDefinition & { [key: string]: TransitionDefinition; }` | No | `-` | Transition configuration for line animations. |
2050
+ | `type` | `dotted \| solid` | No | `'solid'` | The type of line to render. |
2051
+ | `userSelect` | `ResponsiveProp<text \| none \| all \| auto>` | No | `-` | - |
2052
+ | `visibility` | `ResponsiveProp<hidden \| visible>` | No | `-` | - |
1545
2053
  | `width` | `ResponsiveProp<Width<string \| number>>` | No | `-` | - |
1546
- | `widths` | `string \| number` | No | `-` | - |
1547
- | `wordSpacing` | `string \| number` | No | `-` | - |
1548
- | `writingMode` | `string \| number` | No | `-` | - |
1549
- | `x` | `string \| number` | No | `-` | - |
1550
- | `x1` | `string \| number` | No | `-` | - |
1551
- | `x2` | `string \| number` | No | `-` | - |
1552
- | `xAxis` | `(Partial<AxisConfigProps> & AxisBaseProps & { className?: string; classNames?: { root?: string \| undefined; tickLabel?: string \| undefined; gridLine?: string \| undefined; line?: string \| undefined; tickMark?: string \| undefined; } \| undefined; style?: CSSProperties \| undefined; styles?: { root?: CSSProperties \| undefined; tickLabel?: CSSProperties \| undefined; gridLine?: CSSProperties \| undefined; line?: CSSProperties \| undefined; tickMark?: CSSProperties \| undefined; } \| undefined; } & { position?: top \| bottom \| undefined; height?: number \| undefined; }) \| undefined` | No | `-` | - |
1553
- | `xChannelSelector` | `string` | No | `-` | - |
1554
- | `xHeight` | `string \| number` | No | `-` | - |
1555
- | `xlinkActuate` | `string` | No | `-` | - |
1556
- | `xlinkArcrole` | `string` | No | `-` | - |
1557
- | `xlinkHref` | `string` | No | `-` | - |
1558
- | `xlinkRole` | `string` | No | `-` | - |
1559
- | `xlinkShow` | `string` | No | `-` | - |
1560
- | `xlinkTitle` | `string` | No | `-` | - |
1561
- | `xlinkType` | `string` | No | `-` | - |
1562
- | `xmlBase` | `string` | No | `-` | - |
1563
- | `xmlLang` | `string` | No | `-` | - |
1564
- | `xmlSpace` | `string` | No | `-` | - |
1565
- | `xmlns` | `string` | No | `-` | - |
1566
- | `xmlnsXlink` | `string` | No | `-` | - |
1567
- | `y` | `string \| number` | No | `-` | - |
1568
- | `y1` | `string \| number` | No | `-` | - |
1569
- | `y2` | `string \| number` | No | `-` | - |
1570
- | `yAxis` | `(Partial<AxisConfigProps> & AxisBaseProps & { className?: string; classNames?: { root?: string \| undefined; tickLabel?: string \| undefined; gridLine?: string \| undefined; line?: string \| undefined; tickMark?: string \| undefined; } \| undefined; style?: CSSProperties \| undefined; styles?: { root?: CSSProperties \| undefined; tickLabel?: CSSProperties \| undefined; gridLine?: CSSProperties \| undefined; line?: CSSProperties \| undefined; tickMark?: CSSProperties \| undefined; } \| undefined; } & { axisId?: string \| undefined; position?: left \| right \| undefined; width?: number \| undefined; }) \| undefined` | No | `-` | - |
1571
- | `yChannelSelector` | `string` | No | `-` | - |
1572
- | `z` | `string \| number` | No | `-` | - |
1573
- | `zIndex` | `-moz-initial \| inherit \| initial \| revert \| revert-layer \| unset \| auto` | No | `-` | - |
1574
- | `zoomAndPan` | `string` | No | `-` | - |
2054
+ | `xAxis` | `(Partial<AxisConfigProps> & AxisBaseProps & { className?: string; classNames?: { root?: string \| undefined; label?: string \| undefined; tickLabel?: string \| undefined; gridLine?: string \| undefined; line?: string \| undefined; tickMark?: string \| undefined; } \| undefined; style?: CSSProperties \| undefined; styles?: { root?: CSSProperties \| undefined; label?: CSSProperties \| undefined; tickLabel?: CSSProperties \| undefined; gridLine?: CSSProperties \| undefined; line?: CSSProperties \| undefined; tickMark?: CSSProperties \| undefined; } \| undefined; GridLineComponent?: LineComponent \| undefined; LineComponent?: LineComponent \| undefined; TickMarkLineComponent?: LineComponent \| undefined; tickLabelFormatter?: ((value: number) => ChartTextChildren) \| undefined; TickLabelComponent?: AxisTickLabelComponent \| undefined; } & { position?: top \| bottom \| undefined; height?: number \| undefined; }) \| undefined` | No | `-` | Configuration for x-axis. Accepts axis config and axis props. To show the axis, set showXAxis to true. |
2055
+ | `yAxis` | `(Partial<AxisConfigProps> & AxisBaseProps & { className?: string; classNames?: { root?: string \| undefined; label?: string \| undefined; tickLabel?: string \| undefined; gridLine?: string \| undefined; line?: string \| undefined; tickMark?: string \| undefined; } \| undefined; style?: CSSProperties \| undefined; styles?: { root?: CSSProperties \| undefined; label?: CSSProperties \| undefined; tickLabel?: CSSProperties \| undefined; gridLine?: CSSProperties \| undefined; line?: CSSProperties \| undefined; tickMark?: CSSProperties \| undefined; } \| undefined; GridLineComponent?: LineComponent \| undefined; LineComponent?: LineComponent \| undefined; TickMarkLineComponent?: LineComponent \| undefined; tickLabelFormatter?: ((value: number) => ChartTextChildren) \| undefined; TickLabelComponent?: AxisTickLabelComponent \| undefined; } & { axisId?: string \| undefined; position?: left \| right \| undefined; width?: number \| undefined; }) \| undefined` | No | `-` | Configuration for y-axis. Accepts axis config and axis props. To show the axis, set showYAxis to true. |
2056
+ | `zIndex` | `inherit \| auto \| revert \| -moz-initial \| initial \| revert-layer \| unset` | No | `-` | - |
1575
2057
 
1576
2058