HabitatMap/AirCasting

View on GitHub
app/javascript/react/components/Graph/chartEvents.ts

Summary

Maintainability
D
2 days
Test Coverage
import Highcharts from "highcharts/highstock";
import chevronLeft from "../../assets/icons/chevronLeftCircle.svg";
import chevronRight from "../../assets/icons/chevronRightCircle.svg";
import graphChevronLeft from "../../assets/icons/graphChevronLeft.svg";
import graphChevronRight from "../../assets/icons/graphChevronRight.svg";
import mobileChevronLeft from "../../assets/icons/mobileChevronLeft.svg";
import mobileChevronRight from "../../assets/icons/mobileChevronRight.svg";

const DIRECTION_LEFT = "left";
const DIRECTION_RIGHT = "right";

const addNavigationArrows = (
  chart: Highcharts.Chart,
  isCalendarPage: boolean,
  isMobile: boolean
) => {
  if (isMobile && !isCalendarPage) {
    return;
  }

  if (
    chart.renderer.boxWrapper.element.querySelectorAll(".custom-arrow").length >
    0
  ) {
    return;
  }
  let leftArrow: Highcharts.SVGElement;
  let rightArrow: Highcharts.SVGElement;

  const updateArrowStates = () => {
    const axis = chart.xAxis[0];
    const { min, max, dataMin, dataMax } = axis.getExtremes();

    leftArrow.css({
      cursor: min <= dataMin ? "not-allowed" : "pointer",
      opacity: min <= dataMin ? 0.5 : 1,
    });
    rightArrow.css({
      cursor: max >= dataMax ? "not-allowed" : "pointer",
      opacity: max >= dataMax ? 0.5 : 1,
    });
  };

  const move = (direction: typeof DIRECTION_LEFT | typeof DIRECTION_RIGHT) => {
    const axis = chart.xAxis[0];
    const { min, max, dataMin, dataMax } = axis.getExtremes();
    const range = max - min;
    const moveAmount = range * 0.1;

    const newMin =
      direction === DIRECTION_LEFT
        ? Math.max(dataMin, min - moveAmount)
        : Math.min(dataMax - range, min + moveAmount);
    const newMax =
      direction === DIRECTION_LEFT
        ? Math.max(dataMin + range, max - moveAmount)
        : Math.min(dataMax, max + moveAmount);

    axis.setExtremes(newMin, newMax, true, false, { trigger: "syncExtremes" });
    updateArrowStates();
  };

  const toggleElements = (display: "none" | "block") => {
    const elements = [
      ".highcharts-tooltip",
      ".highcharts-point",
      ".highcharts-crosshair",
      ".highcharts-halo",
    ];
    elements.forEach((selector) => {
      const element = chart.container.querySelector(selector) as HTMLElement;
      if (element) element.style.display = display;
    });
  };

  const createArrows = () => {
    const chartWidth = chart.chartWidth;
    const chartHeight = chart.chartHeight;

    // Remove existing arrows if any
    chart.renderer.boxWrapper.element
      .querySelectorAll(".custom-arrow")
      .forEach((el) => el.remove());

    const leftIcon = isMobile
      ? mobileChevronLeft
      : isCalendarPage
      ? chevronLeft
      : graphChevronLeft;
    const rightIcon = isMobile
      ? mobileChevronRight
      : isCalendarPage
      ? chevronRight
      : graphChevronRight;

    let iconSize = 48;
    let leftArrowX = 15;
    let rightArrowX = chartWidth - 118;
    let arrowY = chartHeight / 2;

    if (isMobile && isCalendarPage) {
      iconSize = 40;
      leftArrowX = 0;
      rightArrowX = chartWidth - 50;
      arrowY = -29;
    } else if (!isMobile && isCalendarPage) {
      iconSize = 46;
      leftArrowX = -70;
      rightArrowX = chartWidth + 25;
      arrowY = chartHeight / 2 - 24;
    } else if (!isMobile && !isCalendarPage) {
      iconSize = 48;
      leftArrowX = 15;
      rightArrowX = chartWidth - 118;
      arrowY = chartHeight / 2 - 24;
    }

    leftArrow = chart.renderer
      .image(leftIcon, leftArrowX, arrowY, iconSize, iconSize)
      .attr({ zIndex: 10, class: "custom-arrow" })
      .css({ cursor: "pointer" })
      .add();

    rightArrow = chart.renderer
      .image(rightIcon, rightArrowX, arrowY, iconSize, iconSize)
      .attr({ zIndex: 10, class: "custom-arrow" })
      .css({ cursor: "pointer" })
      .add();

    leftArrow.on("click", () => {
      if (leftArrow.element.style.cursor !== "not-allowed") {
        move(DIRECTION_LEFT);
      }
    });

    rightArrow.on("click", () => {
      if (rightArrow.element.style.cursor !== "not-allowed") {
        move(DIRECTION_RIGHT);
      }
    });

    leftArrow.on("mouseover", () => toggleElements("none"));
    leftArrow.on("mouseout", () => toggleElements("block"));
    rightArrow.on("mouseover", () => toggleElements("none"));
    rightArrow.on("mouseout", () => toggleElements("block"));

    updateArrowStates();

    Highcharts.addEvent(chart, "redraw", () => {
      updateArrowStates();
      leftArrow.attr({ x: leftArrowX, y: arrowY });
      rightArrow.attr({
        x: rightArrowX,
        y: arrowY,
      });
    });
  };

  createArrows();

  Highcharts.addEvent(chart, "resize", createArrows);
};

const handleLoad = function (
  this: Highcharts.Chart,
  isCalendarPage: boolean,
  isMobile: boolean
) {
  addNavigationArrows(this, isCalendarPage, isMobile);
};

export { handleLoad };