@coinbase/cds-mcp-server 8.66.0 → 8.66.1

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.
@@ -12,9 +12,11 @@ import { AreaChart } from '@coinbase/cds-web-visualization'
12
12
 
13
13
  ## Examples
14
14
 
15
- AreaChart is a cartesian chart variant that allows for easy visualization of stacked data.
15
+ AreaChart is a wrapper for [CartesianChart](/components/charts/CartesianChart) that has some unique features over [LineChart](/components/charts/LineChart), such as the ability to stack areas on top of each other and a default value-axis minimum that follows the baseline (`0` when baseline is not set). Charts are built using SVGs.
16
16
 
17
- ### Basic Example
17
+ ### Basics
18
+
19
+ 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.
18
20
 
19
21
  ```jsx live
20
22
  <AreaChart
@@ -36,7 +38,11 @@ AreaChart is a cartesian chart variant that allows for easy visualization of sta
36
38
  />
37
39
  ```
38
40
 
39
- ### Simple
41
+ ### Data
42
+
43
+ #### Positive and Negative
44
+
45
+ Area grows from the baseline to each value, allowing for both positive and negative data to be shown. Also, you can set `baseline` to any value you'd like.
40
46
 
41
47
  ```jsx live
42
48
  <AreaChart
@@ -44,26 +50,86 @@ AreaChart is a cartesian chart variant that allows for easy visualization of sta
44
50
  inset={0}
45
51
  series={[
46
52
  {
47
- id: 'prices',
48
- data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 2, 68, 20, 21, 58],
53
+ id: 'area',
54
+ data: [-40, -28, -21, -5, 48, -5, -28, 2, -29, -48, 18, -30, -29, 8],
55
+ gradient: {
56
+ stops: [
57
+ { offset: 0, color: 'var(--color-fgNegative)' },
58
+ { offset: 0, color: 'var(--color-fgPositive)' },
59
+ ],
60
+ },
49
61
  },
50
62
  ]}
51
- type="gradient"
63
+ type="dotted"
52
64
  showLines
65
+ showYAxis
66
+ yAxis={{
67
+ showGrid: true,
68
+ }}
53
69
  />
54
70
  ```
55
71
 
72
+ #### Range
73
+
74
+ You can pass in `[min, max]` tuples as data points to render an area that span a range of values.
75
+
76
+ ```jsx live
77
+ <AreaChart
78
+ legend={<Legend seriesIds={['marketCap']} />}
79
+ showXAxis
80
+ showYAxis
81
+ height={250}
82
+ series={[
83
+ {
84
+ id: 'marketCap',
85
+ label: 'Market Cap',
86
+ data: [5.4, 5.8, 6.1, 5.9, 6.0, 6.3],
87
+ showLines: true,
88
+ fillOpacity: 0,
89
+ },
90
+ {
91
+ id: 'confidenceInterval',
92
+ label: 'Confidence Interval',
93
+ data: [
94
+ [5.3, 5.5],
95
+ [5.6, 6.0],
96
+ [5.8, 6.2],
97
+ [5.8, 6.1],
98
+ [5.9, 6.3],
99
+ [6.2, 6.5],
100
+ ],
101
+ fillOpacity: 0.3,
102
+ },
103
+ ]}
104
+ xAxis={{
105
+ showLine: true,
106
+ showTickMarks: true,
107
+ data: ['January', 'February', 'March', 'April', 'May', 'June'],
108
+ }}
109
+ yAxis={{
110
+ showGrid: true,
111
+ showLine: true,
112
+ showTickMarks: true,
113
+ domain: { min: 5.0, max: 7.0 },
114
+ tickLabelFormatter: (val) => `$${val}B`,
115
+ }}
116
+ >
117
+ <Line seriesId="marketCap" />
118
+ </AreaChart>
119
+ ```
120
+
56
121
  ### Stacking
57
122
 
58
- You can use the `stacked` prop to stack all areas on top of each other. You can also use the `stackId` prop on a series to create different stack groups. See [CartesianChart](/components/charts/CartesianChart/#series-stacks) for more details.
123
+ You can use the `stacked` prop to stack all areas on top of each other. You can also use the `stackId` prop on a series to create different stack groups. See [CartesianChart](/components/charts/CartesianChart/#series-stacks) for more info on stacking.
59
124
 
60
125
  ```jsx live
61
126
  <AreaChart
62
127
  showLines
63
128
  stacked
64
129
  curve="natural"
130
+ legend
65
131
  height={{ base: 150, tablet: 200, desktop: 250 }}
66
- inset={0}
132
+ inset={{ top: 0, bottom: 8, left: 0, right: 0 }}
67
133
  series={[
68
134
  {
69
135
  id: 'currentRewards',
@@ -72,6 +138,8 @@ You can use the `stacked` prop to stack all areas on top of each other. You can
72
138
  3700, 4170,
73
139
  ],
74
140
  color: 'var(--color-fg)',
141
+ legendShape: 'squircle',
142
+ label: 'Current Rewards',
75
143
  },
76
144
  {
77
145
  id: 'potentialRewards',
@@ -80,7 +148,9 @@ You can use the `stacked` prop to stack all areas on top of each other. You can
80
148
  3520, 3900,
81
149
  ],
82
150
  color: 'var(--color-fgPositive)',
83
- LineComponent: DottedLine,
151
+ LineComponent: (props) => <DottedLine {...props} strokeDasharray="6 6" />,
152
+ legendShape: 'squircle',
153
+ label: 'Potential Rewards',
84
154
  },
85
155
  ]}
86
156
  AreaComponent={(props) => <DottedArea {...props} peakOpacity={0.4} baselineOpacity={0.4} />}
@@ -88,30 +158,9 @@ You can use the `stacked` prop to stack all areas on top of each other. You can
88
158
  />
89
159
  ```
90
160
 
91
- ### Negative Values
92
-
93
- When an area chart contains negative values, the baseline automatically adjusts to zero instead of the bottom of the chart. The area fills from the data line to the zero baseline, properly showing both positive and negative regions.
94
-
95
- ```jsx live
96
- <AreaChart
97
- height={{ base: 150, tablet: 200, desktop: 250 }}
98
- inset={0}
99
- series={[
100
- {
101
- id: 'pageViews',
102
- data: [24, 13, -98, 39, 48, 38, 43],
103
- },
104
- ]}
105
- AreaComponent={(props) => <SolidArea {...props} opacity={0.4} />}
106
- showLines
107
- showYAxis
108
- yAxis={{
109
- showGrid: true,
110
- }}
111
- />
112
- ```
161
+ ### Styling
113
162
 
114
- ### Area Styles
163
+ #### Areas
115
164
 
116
165
  You can have different area styles for each series.
117
166
 
@@ -144,374 +193,173 @@ You can have different area styles for each series.
144
193
  />
145
194
  ```
146
195
 
147
- ### Animations
196
+ #### Axes
148
197
 
149
- You can configure chart transitions using the `transitions` prop. Also, you can toggle animations by setting `animate` to `true` or `false`.
198
+ Using `showXAxis` and `showYAxis` allows you to display the axes. For more information, such as adjusting domain and range, see [XAxis](/components/charts/XAxis) and [YAxis](/components/charts/YAxis).
150
199
 
151
200
  ```jsx live
152
- function AnimatedStackedAreas() {
153
- const dataCount = 20;
154
- const minYValue = 5000;
155
- const maxDataOffset = 15000;
156
- const minStepOffset = 2500;
157
- const maxStepOffset = 10000;
158
- const updateInterval = 500;
159
- const seriesSpacing = 2000;
160
-
161
- const seriesConfig = [
162
- { id: 'red', label: 'Red', color: 'rgb(var(--red40))' },
163
- { id: 'orange', label: 'Orange', color: 'rgb(var(--orange40))' },
164
- { id: 'yellow', label: 'Yellow', color: 'rgb(var(--yellow40))' },
165
- { id: 'green', label: 'Green', color: 'rgb(var(--green40))' },
166
- { id: 'blue', label: 'Blue', color: 'rgb(var(--blue40))' },
167
- { id: 'indigo', label: 'Indigo', color: 'rgb(var(--indigo40))' },
168
- { id: 'purple', label: 'Purple', color: 'rgb(var(--purple40))' },
169
- ];
170
-
171
- const domainLimit = maxDataOffset + seriesConfig.length * seriesSpacing;
172
-
173
- function generateNextValue(previousValue) {
174
- const range = maxStepOffset - minStepOffset;
175
- const offset = Math.random() * range + minStepOffset;
176
-
177
- let direction;
178
- if (previousValue >= maxDataOffset) {
179
- direction = -1;
180
- } else if (previousValue <= minYValue) {
181
- direction = 1;
182
- } else {
183
- direction = Math.random() < 0.5 ? -1 : 1;
184
- }
185
-
186
- let newValue = previousValue + offset * direction;
187
- newValue = Math.max(minYValue, Math.min(maxDataOffset, newValue));
188
- return newValue;
189
- }
190
-
191
- function generateInitialData() {
192
- const data = [];
193
-
194
- let previousValue = minYValue + Math.random() * (maxDataOffset - minYValue);
195
- data.push(previousValue);
196
-
197
- for (let i = 1; i < dataCount; i++) {
198
- const newValue = generateNextValue(previousValue);
199
- data.push(newValue);
200
- previousValue = newValue;
201
- }
202
-
203
- return data;
204
- }
205
-
206
- const MemoizedDottedArea = memo((props) => (
207
- <DottedArea {...props} baselineOpacity={1} peakOpacity={1} />
208
- ));
209
-
210
- function AnimatedChart(props) {
211
- const [data, setData] = useState(generateInitialData);
212
-
213
- useEffect(() => {
214
- const intervalId = setInterval(() => {
215
- setData((currentData) => {
216
- const lastValue = currentData[currentData.length - 1] ?? 0;
217
- const newValue = generateNextValue(lastValue);
218
-
219
- return [...currentData.slice(1), newValue];
220
- });
221
- }, updateInterval);
222
-
223
- return () => clearInterval(intervalId);
224
- }, []);
225
-
226
- const series = seriesConfig.map((config, index) => ({
227
- id: config.id,
228
- label: config.label,
229
- color: config.color,
230
- data: index === 0 ? data : Array(dataCount).fill(seriesSpacing),
231
- }));
232
-
233
- return (
234
- <AreaChart
235
- overflow="visible"
236
- stacked
237
- height={{ base: 200, tablet: 250, desktop: 300 }}
238
- series={series}
239
- type="dotted"
240
- showLines
241
- AreaComponent={MemoizedDottedArea}
242
- transitions={{
243
- enter: { type: 'spring', stiffness: 700, damping: 80 },
244
- update: { type: 'spring', stiffness: 700, damping: 20 },
245
- }}
246
- inset={0}
247
- showYAxis
248
- yAxis={{
249
- showGrid: true,
250
- width: 0,
251
- tickLabelFormatter: () => '',
252
- domain: { min: 0, max: domainLimit },
253
- }}
254
- {...props}
255
- />
256
- );
257
- }
258
-
259
- function AnimatedChartExample() {
260
- const animatedStates = [
261
- { id: 'on', label: 'On' },
262
- { id: 'off', label: 'Off' },
263
- ];
264
- const [animatedState, setAnimatedState] = useState(animatedStates[0]);
265
-
266
- return (
267
- <VStack gap={2}>
268
- <HStack justifyContent="flex-end" gap={2} alignItems="center">
269
- <Text as="h3" font="headline">
270
- Animations
271
- </Text>
272
- <SegmentedTabs
273
- activeTab={animatedState}
274
- onChange={setAnimatedState}
275
- tabs={animatedStates}
276
- />
277
- </HStack>
278
- <AnimatedChart animate={animatedState.id === 'on'} />
279
- </VStack>
280
- );
281
- }
282
-
283
- return <AnimatedChartExample />;
284
- }
201
+ <AreaChart
202
+ showLines
203
+ showXAxis
204
+ showYAxis
205
+ height={{ base: 200, tablet: 225, desktop: 250 }}
206
+ series={[
207
+ {
208
+ id: 'prices',
209
+ data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58],
210
+ },
211
+ ]}
212
+ xAxis={{
213
+ showGrid: true,
214
+ showLine: true,
215
+ showTickMarks: true,
216
+ tickLabelFormatter: (dataX) => `Day ${dataX}`,
217
+ }}
218
+ yAxis={{
219
+ showGrid: true,
220
+ showLine: true,
221
+ showTickMarks: true,
222
+ }}
223
+ fillOpacity={0.5}
224
+ />
285
225
  ```
286
226
 
287
- ### Gradients
288
-
289
- You can use the `gradient` prop on `series` or `Area` components to enable gradients.
227
+ #### Gradient
290
228
 
291
- 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).
292
-
293
- Values in between stops will be interpolated smoothly using [srgb color space](https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperty).
229
+ You can build threshold-based gradients with hard transitions by reusing stop offsets.
294
230
 
295
231
  ```jsx live
296
- function ContinuousGradient() {
297
- const spectrumColors = [
298
- 'blue',
299
- 'green',
300
- 'orange',
301
- 'yellow',
302
- 'gray',
303
- 'indigo',
304
- 'pink',
305
- 'purple',
306
- 'red',
307
- 'teal',
308
- 'chartreuse',
309
- ];
310
- const data = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];
311
-
312
- const [currentSpectrumColor, setCurrentSpectrumColor] = useState('pink');
313
-
314
- return (
315
- <VStack gap={2}>
316
- <HStack gap={1} justifyContent="flex-end" flexWrap="wrap">
317
- {spectrumColors.map((color) => (
318
- <Pressable
319
- key={color}
320
- onClick={() => setCurrentSpectrumColor(color)}
321
- accessibilityLabel={`Select ${color}`}
322
- style={{
323
- backgroundColor: `rgb(var(--${color}20))`,
324
- border: `2px solid rgb(var(--${color}50))`,
325
- outlineColor: `rgb(var(--${color}80))`,
326
- outline:
327
- currentSpectrumColor === color ? `2px solid rgb(var(--${color}80))` : undefined,
328
- }}
329
- width={{ base: 16, tablet: 24, desktop: 24 }}
330
- height={{ base: 16, tablet: 24, desktop: 24 }}
331
- borderRadius={1000}
332
- />
333
- ))}
334
- </HStack>
335
- <AreaChart
336
- enableScrubbing
337
- height={{ base: 150, tablet: 200, desktop: 250 }}
338
- series={[
339
- {
340
- id: 'prices',
341
- data: data,
342
- gradient: {
343
- stops: ({ min, max }) => [
344
- // Allows a function which accepts min/max or direct array
345
- { offset: min, color: `rgb(var(--${currentSpectrumColor}80))` },
346
- { offset: max, color: `rgb(var(--${currentSpectrumColor}20))` },
347
- ],
348
- },
349
- },
350
- ]}
351
- showYAxis
352
- yAxis={{
353
- showGrid: true,
354
- }}
355
- >
356
- <Scrubber />
357
- </AreaChart>
358
- </VStack>
359
- );
360
- }
232
+ <AreaChart
233
+ showLines
234
+ showYAxis
235
+ height={{ base: 180, tablet: 220, desktop: 260 }}
236
+ series={[
237
+ {
238
+ id: 'netFlow',
239
+ label: 'Net Flow',
240
+ data: [-9, -7, -3, -1, 2, 5, 3, -2, 1, 6, 8, 4],
241
+ gradient: {
242
+ stops: ({ min, max }) => [
243
+ { offset: min, color: 'var(--color-fgNegative)', opacity: 0.45 },
244
+ { offset: -2, color: 'var(--color-fgNegative)', opacity: 0.45 },
245
+ { offset: -2, color: 'var(--color-fgWarning)', opacity: 0.45 },
246
+ { offset: 2, color: 'var(--color-fgWarning)', opacity: 0.45 },
247
+ { offset: 2, color: 'var(--color-fgPositive)', opacity: 0.45 },
248
+ { offset: max, color: 'var(--color-fgPositive)', opacity: 0.45 },
249
+ ],
250
+ },
251
+ type: 'gradient',
252
+ },
253
+ ]}
254
+ yAxis={{
255
+ showGrid: true,
256
+ domain: { min: -10, max: 10 },
257
+ tickLabelFormatter: (value) => `${value}%`,
258
+ }}
259
+ />
361
260
  ```
362
261
 
363
- #### Discrete
262
+ ### Composed Examples
364
263
 
365
- You can set multiple stops at the same offset to create a discrete gradient.
264
+ #### Custom Baseline
366
265
 
367
- ```jsx live
368
- function DiscreteGradient() {
369
- const spectrumColors = [
370
- 'blue',
371
- 'green',
372
- 'orange',
373
- 'yellow',
374
- 'gray',
375
- 'indigo',
376
- 'pink',
377
- 'purple',
378
- 'red',
379
- 'teal',
380
- 'chartreuse',
381
- ];
382
- const data = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];
383
-
384
- const [currentSpectrumColor, setCurrentSpectrumColor] = useState('pink');
266
+ You can combine a custom baseline with a scrubber label that shows both price and date.
385
267
 
386
- return (
387
- <VStack gap={2}>
388
- <HStack gap={1} justifyContent="flex-end" flexWrap="wrap">
389
- {spectrumColors.map((color) => (
390
- <Pressable
391
- key={color}
392
- onClick={() => setCurrentSpectrumColor(color)}
393
- accessibilityLabel={`Select ${color}`}
394
- style={{
395
- backgroundColor: `rgb(var(--${color}20))`,
396
- border: `2px solid rgb(var(--${color}50))`,
397
- outlineColor: `rgb(var(--${color}80))`,
398
- outline:
399
- currentSpectrumColor === color ? `2px solid rgb(var(--${color}80))` : undefined,
400
- }}
401
- width={{ base: 16, tablet: 24, desktop: 24 }}
402
- height={{ base: 16, tablet: 24, desktop: 24 }}
403
- borderRadius={1000}
404
- />
405
- ))}
406
- </HStack>
407
- <AreaChart
408
- enableScrubbing
409
- height={{ base: 150, tablet: 200, desktop: 250 }}
410
- series={[
411
- {
412
- id: 'prices',
413
- data: data,
414
- gradient: {
415
- stops: ({ min, max }) => [
416
- { offset: min, color: `rgb(var(--${currentSpectrumColor}80))` },
417
- { offset: min + (max - min) / 3, color: `rgb(var(--${currentSpectrumColor}80))` },
418
- { offset: min + (max - min) / 3, color: `rgb(var(--${currentSpectrumColor}50))` },
419
- {
420
- offset: min + ((max - min) / 3) * 2,
421
- color: `rgb(var(--${currentSpectrumColor}50))`,
422
- },
423
- {
424
- offset: min + ((max - min) / 3) * 2,
425
- color: `rgb(var(--${currentSpectrumColor}20))`,
426
- },
427
- { offset: max, color: `rgb(var(--${currentSpectrumColor}20))` },
428
- ],
429
- },
430
- },
431
- ]}
432
- showLines
433
- strokeWidth={4}
434
- showYAxis
435
- yAxis={{
436
- showGrid: true,
437
- }}
438
- fillOpacity={0.5}
439
- >
440
- <Scrubber />
441
- </AreaChart>
442
- </VStack>
268
+ ```jsx live
269
+ function CustomBaseline() {
270
+ const candles = [...btcCandles].reverse().slice(0, 180);
271
+ const prices = candles.map((candle) => parseFloat(candle.close));
272
+ const dates = candles.map((candle) => new Date(parseInt(candle.start, 10) * 1000));
273
+
274
+ const startingPrice = prices[0];
275
+
276
+ const formatPrice = useCallback((price) => {
277
+ return `$${price.toLocaleString('en-US', {
278
+ minimumFractionDigits: 2,
279
+ maximumFractionDigits: 2,
280
+ })}`;
281
+ }, []);
282
+
283
+ const formatDate = useCallback((date) => {
284
+ return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
285
+ }, []);
286
+
287
+ const formatLabel = useCallback(
288
+ (dataIndex) => {
289
+ const price = prices[dataIndex];
290
+ const date = dates[dataIndex];
291
+
292
+ return (
293
+ <>
294
+ <tspan style={{ fontWeight: 'bold' }}>{formatPrice(price)}</tspan> {formatDate(date)}
295
+ </>
296
+ );
297
+ },
298
+ [dates, formatDate, formatPrice, prices],
443
299
  );
444
- }
445
- ```
446
300
 
447
- #### Axes
301
+ const PriceLabel = memo((props) => (
302
+ <DefaultReferenceLineLabel
303
+ {...props}
304
+ background="var(--color-bgSecondary)"
305
+ borderRadius={12.5}
306
+ color="var(--color-fg)"
307
+ dx={12}
308
+ font="label1"
309
+ horizontalAlignment="left"
310
+ inset={{ top: 4, bottom: 4, left: 8, right: 8 }}
311
+ />
312
+ ));
448
313
 
449
- By default, gradients will be applied to the y-axis. You can apply a gradient to the x-axis by setting `axis` to `x` in the gradient definition.
314
+ const chartAccessibilityLabel = `Bitcoin area chart with custom baseline. Current price: ${formatPrice(
315
+ prices[prices.length - 1],
316
+ )}. Use arrow keys to navigate.`;
450
317
 
451
- ```jsx live
452
- function XAxisGradient() {
453
- const spectrumColors = [
454
- 'blue',
455
- 'green',
456
- 'orange',
457
- 'yellow',
458
- 'gray',
459
- 'indigo',
460
- 'pink',
461
- 'purple',
462
- 'red',
463
- 'teal',
464
- 'chartreuse',
465
- ];
466
- const data = [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58];
467
-
468
- const [currentSpectrumColor, setCurrentSpectrumColor] = useState('pink');
318
+ const getScrubberAccessibilityLabel = useCallback(
319
+ (index) => `${formatPrice(prices[index])} ${formatDate(dates[index])}`,
320
+ [dates, formatDate, formatPrice, prices],
321
+ );
469
322
 
470
323
  return (
471
- <VStack gap={2}>
472
- <HStack gap={1} justifyContent="flex-end" flexWrap="wrap">
473
- {spectrumColors.map((color) => (
474
- <Pressable
475
- key={color}
476
- onClick={() => setCurrentSpectrumColor(color)}
477
- accessibilityLabel={`Select ${color}`}
478
- style={{
479
- backgroundColor: `rgb(var(--${color}20))`,
480
- border: `2px solid rgb(var(--${color}50))`,
481
- outlineColor: `rgb(var(--${color}80))`,
482
- outline:
483
- currentSpectrumColor === color ? `2px solid rgb(var(--${color}80))` : undefined,
484
- }}
485
- width={{ base: 16, tablet: 24, desktop: 24 }}
486
- height={{ base: 16, tablet: 24, desktop: 24 }}
487
- borderRadius={1000}
488
- />
489
- ))}
490
- </HStack>
491
- <AreaChart
492
- enableScrubbing
493
- height={{ base: 150, tablet: 200, desktop: 250 }}
494
- series={[
495
- {
496
- id: 'prices',
497
- data: data,
498
- gradient: {
499
- axis: 'x',
500
- stops: ({ min, max }) => [
501
- { offset: min, color: `rgb(var(--${currentSpectrumColor}80))`, opacity: 0 },
502
- { offset: max, color: `rgb(var(--${currentSpectrumColor}20))`, opacity: 1 },
503
- ],
504
- },
324
+ <AreaChart
325
+ enableScrubbing
326
+ showLines
327
+ showYAxis
328
+ accessibilityLabel={chartAccessibilityLabel}
329
+ getScrubberAccessibilityLabel={getScrubberAccessibilityLabel}
330
+ fillOpacity={0.5}
331
+ height={300}
332
+ series={[
333
+ {
334
+ id: 'prices',
335
+ data: prices,
336
+ gradient: {
337
+ stops: [
338
+ { offset: startingPrice, color: 'var(--color-fgNegative)' },
339
+ { offset: startingPrice, color: 'var(--color-fgPositive)' },
340
+ ],
505
341
  },
506
- ]}
507
- showYAxis
508
- yAxis={{
509
- showGrid: true,
510
- }}
511
- >
512
- <Scrubber />
513
- </AreaChart>
514
- </VStack>
342
+ },
343
+ ]}
344
+ yAxis={{
345
+ baseline: startingPrice,
346
+ showGrid: true,
347
+ tickLabelFormatter: formatPrice,
348
+ domain: { min: 70000, max: 120000 },
349
+ width: 80,
350
+ ticks: [80000, 100000, 120000],
351
+ }}
352
+ >
353
+ <Scrubber label={formatLabel} labelElevated />
354
+ <ReferenceLine
355
+ LabelComponent={PriceLabel}
356
+ LineComponent={(props) => (
357
+ <DottedLine {...props} stroke="var(--color-fg)" strokeDasharray="0 16" strokeWidth={3} />
358
+ )}
359
+ dataY={startingPrice}
360
+ label={formatPrice(startingPrice)}
361
+ />
362
+ </AreaChart>
515
363
  );
516
364
  }
517
365
  ```