Built on 21st
Community

Dependencies

Tags

Annotation

Annotation

here is annotation component

Default preview
// DemoOne.tsx
import React, { useState, useEffect, useMemo } from 'react';
import { Component, DataPoint } from '@/components/ui/annotation'; 
import { Annotation } from '@visx/annotation';
import { scaleTime, scaleLinear } from '@visx/scale';
import { extent } from 'd3-array';
import { cn } from '@/lib/utils';

const mockStockData: DataPoint[] = [
  { date: '2023-01-01', value: 100 },
  { date: '2023-01-05', value: 110 },
  { date: '2023-01-10', value: 105 },
  { date: '2023-01-15', value: 120 },
  { date: '2023-01-20', value: 115 },
  { date: '2023-01-25', value: 130 },
  { date: '2023-01-30', value: 125 },
  { date: '2023-02-05', value: 135 },
  { date: '2023-02-10', value: 140 },
  { date: '2023-02-15', value: 138 },
  { date: '2023-02-20', value: 150 },
];

const getDate = (d: DataPoint): Date => new Date(d.date);
const getStockValue = (d: DataPoint): number => d.value;

const DemoOne = () => {
  const [dimensions, setDimensions] = useState({ width: 0, height: 0 });

  useEffect(() => {
    const updateDimensions = () => {
      setDimensions({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    };

    updateDimensions();
    window.addEventListener('resize', updateDimensions);
    return () => window.removeEventListener('resize', updateDimensions);
  }, []);

  const chartWidth = dimensions.width;
  const chartHeight = dimensions.height;

  const margin = { top: 20, right: 30, bottom: 50, left: 50 };

  const innerWidth = chartWidth - margin.left - margin.right;
  const innerHeight = chartHeight - margin.top - margin.bottom;

  const xScale = useMemo(() => scaleTime<number>({
    domain: extent(mockStockData, getDate) as [Date, Date],
    range: [0, innerWidth],
  }), [innerWidth]);

  const yScale = useMemo(() => scaleLinear<number>({
    domain: [
      Math.min(...mockStockData.map(getStockValue)) * 0.9,
      Math.max(...mockStockData.map(getStockValue)) * 1.1,
    ],
    range: [innerHeight, 0],
  }), [innerHeight]);

  const defaultAnnotatedDatum = mockStockData[5]; 
  const initialAnnotationX = xScale(getDate(defaultAnnotatedDatum)) ?? 0;
  const initialAnnotationY = yScale(getStockValue(defaultAnnotatedDatum)) ?? 0;

  const [annotationPosition, setAnnotationPosition] = useState({
    x: initialAnnotationX,
    y: initialAnnotationY,
    dx: 50,
    dy: -50,
  });

  useEffect(() => {
  
    const currentX = xScale(getDate(defaultAnnotatedDatum)) ?? 0;
    const currentY = yScale(getStockValue(defaultAnnotatedDatum)) ?? 0;
    setAnnotationPosition((prev) => ({
      x: currentX,
      y: currentY,
      dx: prev.dx,
      dy: prev.dy,
    }));
  }, [xScale, yScale, defaultAnnotatedDatum]);


  if (chartWidth === 0 || chartHeight === 0) {
    return (
      <div className={cn("flex w-screen h-screen justify-center items-center bg-gray-900 text-white")}>
        Загрузка...
      </div>
    );
  }

  return (
    <div className={cn("w-screen h-screen overflow-hidden bg-gray-800 flex justify-center items-center")}>
      <svg width={chartWidth} height={chartHeight}>
        <g transform={`translate(${margin.left}, ${margin.top})`}>
          <Component
            width={innerWidth}
            height={innerHeight}
            data={mockStockData}
            xScale={xScale}
            yScale={yScale}
            getDate={getDate}
            getStockValue={getStockValue}
            AnnotationComponent={Annotation}
            annotationPosition={annotationPosition}
            onAnnotationPositionChange={setAnnotationPosition}
            connectorType="elbow"
            labelType="html" 
            subjectType="circle"
            subtitle="The stock reached its highest value today."
            labelWidth={160}
            approxTooltipHeight={70}
            editLabelPosition={true}
            editSubjectPosition={true}
            showAnchorLine={true}
            horizontalAnchor="start"
            verticalAnchor="end"
          />
        </g>
      </svg>
    </div>
  );
};

export { DemoOne };

Dependencies

@visx/scale@visx/shape@visx/annotation

Source

airbnb.io/visx/annotation

Published

5/22/2025