@coinbase/cds-mcp-server 8.47.1 → 8.47.3

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 (39) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/mcp-docs/mobile/components/BarChart.txt +8 -8
  3. package/mcp-docs/mobile/components/CartesianChart.txt +85 -30
  4. package/mcp-docs/mobile/components/LineChart.txt +16 -16
  5. package/mcp-docs/mobile/components/MessagingCard.txt +18 -11
  6. package/mcp-docs/mobile/components/Numpad.txt +2 -2
  7. package/mcp-docs/mobile/components/Point.txt +2 -2
  8. package/mcp-docs/mobile/components/ReferenceLine.txt +151 -65
  9. package/mcp-docs/mobile/components/Scrubber.txt +12 -19
  10. package/mcp-docs/mobile/components/Select.txt +1 -1
  11. package/mcp-docs/mobile/components/SelectAlpha.txt +1 -1
  12. package/mcp-docs/mobile/components/SelectOption.txt +1 -1
  13. package/mcp-docs/mobile/components/SlideButton.txt +1 -1
  14. package/mcp-docs/mobile/components/SparklineInteractive.txt +239 -46
  15. package/mcp-docs/mobile/components/SparklineInteractiveHeader.txt +55 -13
  16. package/mcp-docs/mobile/components/XAxis.txt +4 -5
  17. package/mcp-docs/mobile/components/YAxis.txt +2 -2
  18. package/mcp-docs/mobile/getting-started/theming.txt +1 -1
  19. package/mcp-docs/web/components/BarChart.txt +40 -48
  20. package/mcp-docs/web/components/Carousel.txt +2 -2
  21. package/mcp-docs/web/components/CartesianChart.txt +82 -45
  22. package/mcp-docs/web/components/Combobox.txt +61 -61
  23. package/mcp-docs/web/components/LineChart.txt +87 -110
  24. package/mcp-docs/web/components/MediaQueryProvider.txt +10 -2
  25. package/mcp-docs/web/components/MessagingCard.txt +21 -12
  26. package/mcp-docs/web/components/PeriodSelector.txt +57 -39
  27. package/mcp-docs/web/components/Point.txt +3 -3
  28. package/mcp-docs/web/components/ReferenceLine.txt +341 -279
  29. package/mcp-docs/web/components/Scrubber.txt +48 -52
  30. package/mcp-docs/web/components/SelectChipAlpha.txt +1 -1
  31. package/mcp-docs/web/components/SparklineInteractive.txt +399 -54
  32. package/mcp-docs/web/components/SparklineInteractiveHeader.txt +368 -28
  33. package/mcp-docs/web/components/TabbedChipsAlpha.txt +1 -1
  34. package/mcp-docs/web/components/XAxis.txt +5 -6
  35. package/mcp-docs/web/components/YAxis.txt +2 -2
  36. package/mcp-docs/web/getting-started/theming.txt +1 -1
  37. package/mcp-docs/web/hooks/useBreakpoints.txt +5 -4
  38. package/mcp-docs/web/hooks/useMediaQuery.txt +10 -2
  39. package/package.json +1 -1
@@ -14,10 +14,6 @@ import { ReferenceLine } from '@coinbase/cds-web-visualization'
14
14
 
15
15
  ReferenceLine can be used to add important details to a chart, such as a reference price or date. You can create horizontal lines using `dataY` or vertical lines using `dataX`.
16
16
 
17
- #### Simple Reference Line
18
-
19
- A minimal reference line without labels, useful for marking key thresholds:
20
-
21
17
  ```jsx live
22
18
  <LineChart
23
19
  showArea
@@ -178,73 +174,159 @@ Use `labelBoundsInset` to prevent labels from getting too close to chart edges.
178
174
  </Box>
179
175
  ```
180
176
 
181
- #### Custom Component
177
+ #### Custom Components
182
178
 
183
179
  You can adjust the style of the label using a custom `LabelComponent`.
184
180
 
185
181
  ```jsx live
186
- function LabelStyleExample() {
187
- const LiquidationLabel = useMemo(
188
- () =>
189
- memo((props) => (
190
- <DefaultReferenceLineLabel
191
- {...props}
192
- background="var(--color-accentSubtleYellow)"
193
- borderRadius={4}
194
- color="rgb(var(--yellow70))"
195
- dx={12}
196
- font="label1"
197
- horizontalAlignment="left"
198
- inset={{ top: 4, bottom: 4, left: 8, right: 8 }}
199
- />
200
- )),
201
- [],
202
- );
182
+ function CustomLabelExample() {
183
+ const PriceLabel = memo((props) => (
184
+ <DefaultReferenceLineLabel
185
+ {...props}
186
+ background="var(--color-bgSecondary)"
187
+ borderRadius={12.5}
188
+ color="var(--color-fg)"
189
+ inset={{ top: 4, bottom: 4, left: 8, right: 8 }}
190
+ font="label1"
191
+ />
192
+ ));
193
+
194
+ function Example() {
195
+ const hourData = useMemo(() => sparklineInteractiveData.hour, []);
196
+ const startPrice = hourData[0].value;
197
+ const endPrice = hourData[hourData.length - 1].value;
198
+ const isPositive = endPrice >= startPrice;
199
+ const seriesColor = isPositive ? 'var(--color-fgPositive)' : 'var(--color-fgNegative)';
200
+
201
+ const formattedStartPrice = useMemo(
202
+ () =>
203
+ startPrice.toLocaleString('en-US', {
204
+ minimumFractionDigits: 2,
205
+ maximumFractionDigits: 2,
206
+ }),
207
+ [startPrice],
208
+ );
203
209
 
204
- const PriceLabel = useMemo(
205
- () =>
206
- memo((props) => (
207
- <DefaultReferenceLineLabel
208
- {...props}
209
- background="var(--color-bg)"
210
- borderRadius={4}
211
- color="rgb(var(--yellow70))"
212
- dx={-12}
213
- font="label1"
214
- horizontalAlignment="right"
215
- inset={{ top: 2, bottom: 2, left: 4, right: 4 }}
210
+ return (
211
+ <LineChart
212
+ enableScrubbing
213
+ showArea
214
+ areaType="dotted"
215
+ height={{ base: 200, tablet: 250, desktop: 300 }}
216
+ series={[
217
+ {
218
+ id: 'hourly-prices',
219
+ data: hourData.map((d) => d.value),
220
+ color: seriesColor,
221
+ },
222
+ ]}
223
+ xAxis={{
224
+ range: ({ min, max }) => ({ min, max: max - 24 }),
225
+ }}
226
+ >
227
+ <Scrubber />
228
+ <ReferenceLine
229
+ LabelComponent={PriceLabel}
230
+ LineComponent={(props) => (
231
+ <DottedLine {...props} strokeDasharray="0 16" strokeWidth={3} />
232
+ )}
233
+ dataY={startPrice}
234
+ label={formattedStartPrice}
235
+ stroke="var(--color-fgMuted)"
236
+ labelDx={-12}
237
+ labelHorizontalAlignment="right"
216
238
  />
217
- )),
218
- [],
219
- );
239
+ </LineChart>
240
+ );
241
+ }
220
242
 
221
- return (
222
- <LineChart
223
- height={{ base: 150, tablet: 200, desktop: 250 }}
224
- inset={{ right: 4 }}
225
- series={[
226
- {
227
- id: 'prices',
228
- data: [10, 22, 29, 45, 98, 45, 22, 52, 21, 4, 68, 20, 21, 58],
229
- },
230
- ]}
231
- >
232
- <ReferenceLine
233
- LabelComponent={LiquidationLabel}
234
- dataY={25}
235
- label="Liquidation"
236
- labelPosition="left"
237
- stroke="var(--color-bgWarning)"
238
- />
239
- <ReferenceLine
240
- LabelComponent={PriceLabel}
241
- dataY={25}
242
- label="$25"
243
- labelPosition="right"
244
- stroke="transparent"
243
+ return <Example />;
244
+ }
245
+ ```
246
+
247
+ You can also optionally hide the label based on user scrubbing.
248
+
249
+ ```jsx live
250
+ function StartPriceReferenceLine() {
251
+ const PriceLabel = memo((props) => {
252
+ const { scrubberPosition } = useScrubberContext();
253
+ const { getXScale, drawingArea } = useCartesianChartContext();
254
+ const isScrubbing = scrubberPosition !== undefined;
255
+
256
+ const fadeZone = 128;
257
+
258
+ const opacity = useMemo(() => {
259
+ if (!isScrubbing) return 0;
260
+ const xScale = getXScale();
261
+ if (!xScale) return 1;
262
+ const scrubX = xScale(scrubberPosition) ?? 0;
263
+ const rightEdge = drawingArea.x + drawingArea.width;
264
+ return rightEdge - scrubX >= fadeZone ? 1 : 0;
265
+ }, [isScrubbing, scrubberPosition, getXScale, drawingArea]);
266
+
267
+ return (
268
+ <DefaultReferenceLineLabel
269
+ {...props}
270
+ background="var(--color-bgSecondary)"
271
+ borderRadius={12.5}
272
+ color="var(--color-fg)"
273
+ inset={{ top: 4, bottom: 4, left: 8, right: 8 }}
274
+ font="label1"
275
+ styles={{ root: { opacity: opacity, transition: 'opacity 0.25s ease' } }}
245
276
  />
246
- </LineChart>
247
- );
277
+ );
278
+ });
279
+
280
+ function Example() {
281
+ const hourData = useMemo(() => sparklineInteractiveData.hour, []);
282
+ const startPrice = hourData[0].value;
283
+ const endPrice = hourData[hourData.length - 1].value;
284
+ const isPositive = endPrice >= startPrice;
285
+ const seriesColor = isPositive ? 'var(--color-fgPositive)' : 'var(--color-fgNegative)';
286
+
287
+ const formattedStartPrice = useMemo(
288
+ () =>
289
+ startPrice.toLocaleString('en-US', {
290
+ minimumFractionDigits: 2,
291
+ maximumFractionDigits: 2,
292
+ }),
293
+ [startPrice],
294
+ );
295
+
296
+ return (
297
+ <LineChart
298
+ enableScrubbing
299
+ showArea
300
+ areaType="dotted"
301
+ height={{ base: 200, tablet: 250, desktop: 300 }}
302
+ series={[
303
+ {
304
+ id: 'hourly-prices',
305
+ data: hourData.map((d) => d.value),
306
+ color: seriesColor,
307
+ },
308
+ ]}
309
+ xAxis={{
310
+ range: ({ min, max }) => ({ min, max: max - 24 }),
311
+ }}
312
+ >
313
+ <Scrubber />
314
+ <ReferenceLine
315
+ LabelComponent={PriceLabel}
316
+ LineComponent={(props) => (
317
+ <DottedLine {...props} strokeDasharray="0 16" strokeWidth={3} />
318
+ )}
319
+ dataY={startPrice}
320
+ label={formattedStartPrice}
321
+ stroke="var(--color-fgMuted)"
322
+ labelDx={-12}
323
+ labelHorizontalAlignment="right"
324
+ />
325
+ </LineChart>
326
+ );
327
+ }
328
+
329
+ return <Example />;
248
330
  }
249
331
  ```
250
332
 
@@ -252,12 +334,10 @@ function LabelStyleExample() {
252
334
 
253
335
  You can pair a ReferenceLine with a custom drag component to create a draggable price target.
254
336
 
255
- ```jsx live
337
+ ```tsx live
256
338
  function DraggablePriceTarget() {
257
- const DragIcon = ({ x, y }: { x: number; y: number }) => {
258
- const DragCircle = (props: React.SVGProps<SVGCircleElement>) => (
259
- <circle {...props} fill="var(--color-fg)" r="1.5" />
260
- );
339
+ const DragIcon = ({ x, y }) => {
340
+ const DragCircle = (props) => <circle {...props} fill="var(--color-fg)" r="1.5" />;
261
341
 
262
342
  return (
263
343
  <g transform={`translate(${x}, ${y})`}>
@@ -273,17 +353,7 @@ function DraggablePriceTarget() {
273
353
  );
274
354
  };
275
355
 
276
- const TrendArrowIcon = ({
277
- x,
278
- y,
279
- isPositive,
280
- color,
281
- }: {
282
- x: number;
283
- y: number;
284
- isPositive: boolean;
285
- color: string;
286
- }) => {
356
+ const TrendArrowIcon = ({ x, y, isPositive, color }) => {
287
357
  return (
288
358
  <g transform={`translate(${x - 8}, ${y - 8})`}>
289
359
  <g
@@ -302,221 +372,209 @@ function DraggablePriceTarget() {
302
372
  );
303
373
  };
304
374
 
305
- const DynamicPriceLabel = memo(
306
- ({ color, ...props }: React.ComponentProps<typeof DefaultReferenceLineLabel> & { color: string }) => (
307
- <DefaultReferenceLineLabel
308
- {...props}
309
- background={color}
310
- borderRadius={4}
311
- color="white"
312
- dx={-12}
313
- font="label1"
314
- horizontalAlignment="right"
315
- inset={{ top: 5, bottom: 5, left: 10, right: 10 }}
316
- />
317
- ),
318
- );
375
+ const DynamicPriceLabel = memo(({ color, ...props }) => (
376
+ <DefaultReferenceLineLabel
377
+ {...props}
378
+ background={color}
379
+ borderRadius={4}
380
+ color="white"
381
+ dx={-12}
382
+ font="label1"
383
+ horizontalAlignment="right"
384
+ inset={{ top: 5, bottom: 5, left: 10, right: 10 }}
385
+ />
386
+ ));
319
387
 
320
- const DraggableReferenceLine = memo(
321
- ({
322
- baselineAmount,
323
- startAmount,
324
- chartRef,
325
- }: {
326
- baselineAmount: number;
327
- startAmount: number;
328
- chartRef: RefObject<SVGSVGElement>;
329
- }) => {
330
- const theme = useTheme();
331
- const { isPhone } = useBreakpoints();
332
-
333
- const formatPrice = useCallback((value: number) => {
334
- return `$${value.toLocaleString('en-US', {
335
- minimumFractionDigits: 2,
336
- maximumFractionDigits: 2,
337
- })}`;
338
- }, []);
388
+ const DraggableReferenceLine = memo(({ baselineAmount, startAmount, chartRef }) => {
389
+ const theme = useTheme();
390
+ const { isPhone } = useBreakpoints();
391
+
392
+ const formatPrice = useCallback((value) => {
393
+ return `$${value.toLocaleString('en-US', {
394
+ minimumFractionDigits: 2,
395
+ maximumFractionDigits: 2,
396
+ })}`;
397
+ }, []);
398
+
399
+ const { getYScale, drawingArea } = useCartesianChartContext();
400
+ const [amount, setAmount] = useState(startAmount);
401
+ const [isDragging, setIsDragging] = useState(false);
402
+ const [textDimensions, setTextDimensions] = useState({ width: 0, height: 0 });
403
+ const color = amount >= baselineAmount ? 'var(--color-bgPositive)' : 'var(--color-bgNegative)';
404
+
405
+ const yScale = getYScale();
339
406
 
340
- const { getYScale, drawingArea } = useCartesianChartContext();
341
- const [amount, setAmount] = useState(startAmount);
342
- const [isDragging, setIsDragging] = useState(false);
343
- const [textDimensions, setTextDimensions] = useState({ width: 0, height: 0 });
344
- const color = amount >= baselineAmount ? 'var(--color-bgPositive)' : 'var(--color-bgNegative)';
407
+ const labelComponent = useCallback(
408
+ (props) => <DynamicPriceLabel {...props} color={color} />,
409
+ [color],
410
+ );
411
+
412
+ // Set up persistent event listeners on the chart SVG element
413
+ useEffect(() => {
414
+ const element = chartRef.current;
415
+
416
+ if (!element || !yScale || !('invert' in yScale && typeof yScale.invert === 'function')) {
417
+ return;
418
+ }
419
+
420
+ const updatePosition = (clientX, clientY) => {
421
+ const point = element.createSVGPoint();
422
+ point.x = clientX;
423
+ point.y = clientY;
424
+
425
+ const svgPoint = point.matrixTransform(element.getScreenCTM()?.inverse());
426
+
427
+ // Clamp the Y position to the chart area
428
+ const clampedY = Math.max(
429
+ drawingArea.y,
430
+ Math.min(drawingArea.y + drawingArea.height, svgPoint.y),
431
+ );
432
+
433
+ const rawAmount = yScale.invert(clampedY);
345
434
 
346
- const yScale = getYScale();
435
+ const rawPercentage = ((rawAmount - baselineAmount) / baselineAmount) * 100;
347
436
 
348
- const labelComponent = useCallback(
349
- (props: React.ComponentProps<typeof DefaultReferenceLineLabel>) => (
350
- <DynamicPriceLabel {...props} color={color} />
351
- ),
352
- [color],
353
- );
437
+ let targetPercentage = Math.round(rawPercentage);
354
438
 
355
- // Set up persistent event listeners on the chart SVG element
356
- useEffect(() => {
357
- const element = chartRef.current;
439
+ if (targetPercentage === 0) {
440
+ targetPercentage = rawPercentage >= 0 ? 1 : -1;
441
+ }
442
+
443
+ const newAmount = baselineAmount * (1 + targetPercentage / 100);
444
+ setAmount(newAmount);
445
+ };
446
+
447
+ const handleMouseMove = (event: MouseEvent) => {
448
+ if (!isDragging) {
449
+ return;
450
+ }
451
+ updatePosition(event.clientX, event.clientY);
452
+ };
358
453
 
359
- if (!element || !yScale || !('invert' in yScale && typeof yScale.invert === 'function')) {
454
+ const handleTouchMove = (event: TouchEvent) => {
455
+ if (!isDragging || event.touches.length === 0) {
360
456
  return;
361
457
  }
458
+ const touch = event.touches[0];
459
+ updatePosition(touch.clientX, touch.clientY);
460
+ };
362
461
 
363
- const updatePosition = (clientX: number, clientY: number) => {
364
- const point = element.createSVGPoint();
365
- point.x = clientX;
366
- point.y = clientY;
367
-
368
- const svgPoint = point.matrixTransform(element.getScreenCTM()?.inverse());
369
-
370
- // Clamp the Y position to the chart area
371
- const clampedY = Math.max(
372
- drawingArea.y,
373
- Math.min(drawingArea.y + drawingArea.height, svgPoint.y),
374
- );
375
-
376
- const rawAmount = yScale.invert(clampedY);
377
-
378
- const rawPercentage = ((rawAmount - baselineAmount) / baselineAmount) * 100;
379
-
380
- let targetPercentage = Math.round(rawPercentage);
381
-
382
- if (targetPercentage === 0) {
383
- targetPercentage = rawPercentage >= 0 ? 1 : -1;
384
- }
385
-
386
- const newAmount = baselineAmount * (1 + targetPercentage / 100);
387
- setAmount(newAmount);
388
- };
389
-
390
- const handleMouseMove = (event: MouseEvent) => {
391
- if (!isDragging) {
392
- return;
393
- }
394
- updatePosition(event.clientX, event.clientY);
395
- };
396
-
397
- const handleTouchMove = (event: TouchEvent) => {
398
- if (!isDragging || event.touches.length === 0) {
399
- return;
400
- }
401
- const touch = event.touches[0];
402
- updatePosition(touch.clientX, touch.clientY);
403
- };
404
-
405
- const handleMouseUp = () => {
406
- setIsDragging(false);
407
- };
408
-
409
- const handleTouchEnd = () => {
410
- setIsDragging(false);
411
- };
412
-
413
- const handleMouseLeave = () => {
414
- setIsDragging(false);
415
- };
416
-
417
- element.addEventListener('mousemove', handleMouseMove);
418
- element.addEventListener('mouseup', handleMouseUp);
419
- element.addEventListener('mouseleave', handleMouseLeave);
420
- element.addEventListener('touchmove', handleTouchMove);
421
- element.addEventListener('touchend', handleTouchEnd);
422
- element.addEventListener('touchcancel', handleTouchEnd);
423
-
424
- return () => {
425
- element.removeEventListener('mousemove', handleMouseMove);
426
- element.removeEventListener('mouseup', handleMouseUp);
427
- element.removeEventListener('mouseleave', handleMouseLeave);
428
- element.removeEventListener('touchmove', handleTouchMove);
429
- element.removeEventListener('touchend', handleTouchEnd);
430
- element.removeEventListener('touchcancel', handleTouchEnd);
431
- };
432
- }, [isDragging, yScale, chartRef, baselineAmount, drawingArea.y, drawingArea.height]);
433
-
434
- if (!yScale) return null;
435
-
436
- const yPixel = yScale(amount);
437
-
438
- if (yPixel === undefined || yPixel === null) return null;
439
-
440
- const difference = amount - baselineAmount;
441
- const percentageChange = Math.round((difference / baselineAmount) * 100);
442
- const isPositive = difference > 0;
443
-
444
- const percentageLabel = isPhone
445
- ? `${Math.abs(percentageChange)}%`
446
- : `${Math.abs(percentageChange)}% (${formatPrice(Math.abs(difference))})`;
447
- const dollarLabel = formatPrice(amount);
448
-
449
- const handleMouseDown = (e: React.MouseEvent) => {
450
- e.preventDefault();
451
- setIsDragging(true);
462
+ const handleMouseUp = () => {
463
+ setIsDragging(false);
452
464
  };
453
465
 
454
- const handleTouchStart = (e: React.TouchEvent) => {
455
- e.preventDefault();
456
- setIsDragging(true);
466
+ const handleTouchEnd = () => {
467
+ setIsDragging(false);
457
468
  };
458
469
 
459
- const padding = 16;
460
- const dragIconSize = 16;
461
- const trendArrowIconSize = 16;
462
- const iconGap = 8;
463
- const totalPadding = padding * 2 + iconGap;
470
+ const handleMouseLeave = () => {
471
+ setIsDragging(false);
472
+ };
464
473
 
465
- const rectWidth = textDimensions.width + totalPadding + dragIconSize + trendArrowIconSize;
474
+ element.addEventListener('mousemove', handleMouseMove);
475
+ element.addEventListener('mouseup', handleMouseUp);
476
+ element.addEventListener('mouseleave', handleMouseLeave);
477
+ element.addEventListener('touchmove', handleTouchMove);
478
+ element.addEventListener('touchend', handleTouchEnd);
479
+ element.addEventListener('touchcancel', handleTouchEnd);
480
+
481
+ return () => {
482
+ element.removeEventListener('mousemove', handleMouseMove);
483
+ element.removeEventListener('mouseup', handleMouseUp);
484
+ element.removeEventListener('mouseleave', handleMouseLeave);
485
+ element.removeEventListener('touchmove', handleTouchMove);
486
+ element.removeEventListener('touchend', handleTouchEnd);
487
+ element.removeEventListener('touchcancel', handleTouchEnd);
488
+ };
489
+ }, [isDragging, yScale, chartRef, baselineAmount, drawingArea.y, drawingArea.height]);
466
490
 
467
- return (
468
- <>
469
- <ReferenceLine
470
- LabelComponent={labelComponent}
471
- dataY={amount}
472
- label={dollarLabel}
473
- labelPosition="right"
491
+ if (!yScale) return null;
492
+
493
+ const yPixel = yScale(amount);
494
+
495
+ if (yPixel === undefined || yPixel === null) return null;
496
+
497
+ const difference = amount - baselineAmount;
498
+ const percentageChange = Math.round((difference / baselineAmount) * 100);
499
+ const isPositive = difference > 0;
500
+
501
+ const percentageLabel = isPhone
502
+ ? `${Math.abs(percentageChange)}%`
503
+ : `${Math.abs(percentageChange)}% (${formatPrice(Math.abs(difference))})`;
504
+ const dollarLabel = formatPrice(amount);
505
+
506
+ const handleMouseDown = (e) => {
507
+ e.preventDefault();
508
+ setIsDragging(true);
509
+ };
510
+
511
+ const handleTouchStart = (e) => {
512
+ e.preventDefault();
513
+ setIsDragging(true);
514
+ };
515
+
516
+ const padding = 16;
517
+ const dragIconSize = 16;
518
+ const trendArrowIconSize = 16;
519
+ const iconGap = 8;
520
+ const totalPadding = padding * 2 + iconGap;
521
+
522
+ const rectWidth = textDimensions.width + totalPadding + dragIconSize + trendArrowIconSize;
523
+
524
+ return (
525
+ <>
526
+ <ReferenceLine
527
+ LabelComponent={labelComponent}
528
+ dataY={amount}
529
+ label={dollarLabel}
530
+ labelPosition="right"
531
+ />
532
+ <g
533
+ onMouseDown={handleMouseDown}
534
+ onTouchStart={handleTouchStart}
535
+ style={{
536
+ cursor: isDragging ? 'grabbing' : 'grab',
537
+ opacity: textDimensions.width === 0 ? 0 : 1,
538
+ }}
539
+ >
540
+ <rect
541
+ fill="var(--color-bgSecondary)"
542
+ height={32}
543
+ rx={theme.borderRadius['400']}
544
+ ry={theme.borderRadius['400']}
545
+ width={rectWidth}
546
+ x={drawingArea.x}
547
+ y={yPixel - 16}
548
+ />
549
+ <DragIcon x={drawingArea.x + padding} y={yPixel} />
550
+ <TrendArrowIcon
551
+ color={color}
552
+ isPositive={isPositive}
553
+ x={drawingArea.x + padding + dragIconSize + iconGap}
554
+ y={yPixel}
474
555
  />
475
- <g
476
- onMouseDown={handleMouseDown}
477
- onTouchStart={handleTouchStart}
478
- style={{
479
- cursor: isDragging ? 'grabbing' : 'grab',
480
- opacity: textDimensions.width === 0 ? 0 : 1,
481
- }}
556
+ <ChartText
557
+ disableRepositioning
558
+ color={color}
559
+ font="label1"
560
+ horizontalAlignment="left"
561
+ onDimensionsChange={(dimensions) => setTextDimensions(dimensions)}
562
+ verticalAlignment="middle"
563
+ x={drawingArea.x + padding + dragIconSize + iconGap + trendArrowIconSize}
564
+ y={yPixel + 1}
482
565
  >
483
- <rect
484
- fill="var(--color-bgSecondary)"
485
- height={32}
486
- rx={theme.borderRadius['400']}
487
- ry={theme.borderRadius['400']}
488
- width={rectWidth}
489
- x={drawingArea.x}
490
- y={yPixel - 16}
491
- />
492
- <DragIcon x={drawingArea.x + padding} y={yPixel} />
493
- <TrendArrowIcon
494
- color={color}
495
- isPositive={isPositive}
496
- x={drawingArea.x + padding + dragIconSize + iconGap}
497
- y={yPixel}
498
- />
499
- <ChartText
500
- disableRepositioning
501
- color={color}
502
- font="label1"
503
- horizontalAlignment="left"
504
- onDimensionsChange={(dimensions) => setTextDimensions(dimensions)}
505
- verticalAlignment="middle"
506
- x={drawingArea.x + padding + dragIconSize + iconGap + trendArrowIconSize}
507
- y={yPixel + 1}
508
- >
509
- {percentageLabel}
510
- </ChartText>
511
- </g>
512
- </>
513
- );
514
- },
515
- );
566
+ {percentageLabel}
567
+ </ChartText>
568
+ </g>
569
+ </>
570
+ );
571
+ });
516
572
 
517
- const BaselinePriceLabel = useMemo(() => memo((props) => (
518
- <DefaultReferenceLineLabel {...props} dx={8} horizontalAlignment="left" />
519
- )), []);
573
+ const BaselinePriceLabel = useMemo(
574
+ () =>
575
+ memo((props) => <DefaultReferenceLineLabel {...props} dx={8} horizontalAlignment="left" />),
576
+ [],
577
+ );
520
578
 
521
579
  const PriceTargetChart = () => {
522
580
  const priceData = useMemo(() => sparklineInteractiveData.year.map((d) => d.value), []);
@@ -524,7 +582,7 @@ function DraggablePriceTarget() {
524
582
 
525
583
  const chartRef = useRef<SVGSVGElement>(null);
526
584
 
527
- const formatPrice = useCallback((value: number) => {
585
+ const formatPrice = useCallback((value) => {
528
586
  return `$${value.toLocaleString('en-US', {
529
587
  minimumFractionDigits: 2,
530
588
  maximumFractionDigits: 2,
@@ -537,7 +595,11 @@ function DraggablePriceTarget() {
537
595
  showArea
538
596
  animate={false}
539
597
  height={250}
540
- inset={isPhone ? { top: 16, bottom: 16, left: 0, right: 0 } : { top: 16, bottom: 16, left: 8, right: 80 }}
598
+ inset={
599
+ isPhone
600
+ ? { top: 16, bottom: 16, left: 0, right: 0 }
601
+ : { top: 16, bottom: 16, left: 8, right: 80 }
602
+ }
541
603
  series={[
542
604
  {
543
605
  id: 'prices',
@@ -563,7 +625,7 @@ function DraggablePriceTarget() {
563
625
  </LineChart>
564
626
  );
565
627
  };
566
- return <PriceTargetChart />
628
+ return <PriceTargetChart />;
567
629
  }
568
630
  ```
569
631