export function Arrow()

in frontend/libs/canvasSpreadsheet/src/lib/components/ScrollBar/Arrow/Arrow.tsx [14:186]


export function Arrow({ place }: Props) {
  const { gridHeight, gridWidth, gridSizes, theme } =
    useContext(GridStateContext);
  const { moveViewport } = useContext(GridViewportContext);

  const graphicsRef = useRef<PIXI.Graphics>(null);
  const isArrowHovered = useRef(false);
  const mouseDownInterval = useRef<ReturnType<typeof setInterval>>();

  const isHorizontal = place === 'left' || place === 'right';

  const arrowCoords = useMemo(() => {
    const { scrollBar } = gridSizes;
    const { arrowSize, arrowWrapperSize, trackSize } = scrollBar;

    const shiftPosition = (arrowWrapperSize - arrowSize) / 2;
    const arrowRect: Rectangle = {
      x: isHorizontal
        ? place === 'left'
          ? shiftPosition
          : gridWidth - trackSize - arrowWrapperSize + shiftPosition
        : gridWidth - arrowWrapperSize + shiftPosition,
      y: isHorizontal
        ? gridHeight - arrowWrapperSize + shiftPosition
        : place === 'up'
        ? shiftPosition
        : gridHeight - trackSize - arrowWrapperSize + shiftPosition,
      width: arrowSize,
      height: arrowSize,
    };

    let coords: Coordinates[] = [];
    if (place === 'up') {
      coords = [
        { x: arrowRect.x + arrowRect.width / 2, y: arrowRect.y },
        { x: arrowRect.x + arrowRect.width, y: arrowRect.y + arrowRect.height },
        { x: arrowRect.x, y: arrowRect.y + arrowRect.height },
      ];
    } else if (place === 'down') {
      coords = [
        { x: arrowRect.x, y: arrowRect.y },
        {
          x: arrowRect.x + arrowRect.width / 2,
          y: arrowRect.y + arrowRect.height,
        },
        { x: arrowRect.x + arrowRect.width, y: arrowRect.y },
      ];
    } else if (place === 'left') {
      coords = [
        { x: arrowRect.x, y: arrowRect.y + arrowRect.height / 2 },
        { x: arrowRect.x + arrowRect.width, y: arrowRect.y },
        { x: arrowRect.x + arrowRect.width, y: arrowRect.y + arrowRect.height },
      ];
    } else if (place === 'right') {
      coords = [
        { x: arrowRect.x, y: arrowRect.y },
        { x: arrowRect.x, y: arrowRect.y + arrowRect.height },
        {
          x: arrowRect.x + arrowRect.width,
          y: arrowRect.y + arrowRect.height / 2,
        },
      ];
    }

    return coords;
  }, [gridHeight, gridSizes, gridWidth, isHorizontal, place]);

  const arrowWrapperRect = useMemo(() => {
    const { scrollBar } = gridSizes;
    const { arrowWrapperSize, trackSize } = scrollBar;

    const arrowWrapperRect: Rectangle = {
      x: isHorizontal
        ? place === 'left'
          ? 0
          : gridWidth - trackSize - arrowWrapperSize
        : gridWidth - arrowWrapperSize,
      y: isHorizontal
        ? gridHeight - arrowWrapperSize
        : place === 'up'
        ? 0
        : gridHeight - trackSize - arrowWrapperSize,
      width: arrowWrapperSize,
      height: arrowWrapperSize,
    };

    return arrowWrapperRect;
  }, [gridHeight, gridSizes, gridWidth, isHorizontal, place]);

  const onMouseOver = useCallback((e: PIXI.FederatedPointerEvent) => {
    isArrowHovered.current = true;
  }, []);

  const onMouseOut = useCallback((e: PIXI.FederatedPointerEvent) => {
    isArrowHovered.current = false;
  }, []);

  const onMouseDown = useCallback(
    (e: PIXI.FederatedPointerEvent) => {
      const moveX = isHorizontal
        ? place === 'left'
          ? -gridSizes.cell.width
          : gridSizes.cell.width
        : 0;
      const moveY = isHorizontal
        ? 0
        : place === 'up'
        ? -gridSizes.cell.height
        : gridSizes.cell.height;

      moveViewport(moveX, moveY);
      mouseDownInterval.current = setInterval(() => {
        moveViewport(moveX, moveY);
      }, 200);
    },
    [
      gridSizes.cell.height,
      gridSizes.cell.width,
      isHorizontal,
      moveViewport,
      place,
    ]
  );

  const onMouseUp = useCallback((e: PIXI.FederatedPointerEvent) => {
    clearInterval(mouseDownInterval.current);
  }, []);

  const drawArrow = useCallback(
    (
      arrowCoords: Coordinates[],
      arrowWrapperRect: Rectangle,
      g: PIXI.Graphics
    ) => {
      // We need to draw wrapper to have events not only on arrow but in bigger sizes
      g.beginFill(theme.scrollBar.trackColor)
        .drawRect(
          arrowWrapperRect.x,
          arrowWrapperRect.y,
          arrowWrapperRect.width,
          arrowWrapperRect.height
        )
        .beginFill(
          isArrowHovered.current
            ? theme.scrollBar.thumbColorHovered
            : theme.scrollBar.thumbColor,
          1
        )
        .drawPolygon(arrowCoords);
    },
    [theme]
  );

  useTick(() => {
    if (!graphicsRef.current) return;

    graphicsRef.current.clear();

    drawArrow(arrowCoords, arrowWrapperRect, graphicsRef.current);
  }, true);

  return (
    <Graphics
      cursor="pointer"
      eventMode="static"
      onmousedown={onMouseDown}
      onmouseout={onMouseOut}
      onmouseover={onMouseOver}
      onmouseup={onMouseUp}
      ref={graphicsRef}
    />
  );
}