Source: components/LinkHoverChip.tsx

import Actor from "@/types/Actor";
import Intention from "@/types/Intention";
import { ActorType } from "@/types/actorType";
import { IntentionType } from "@/types/intentionType";
import { Chip, Paper, Popper } from "@mui/material";
import React from "react";
import DetailsScreen from "./DetailsScreen";
import { AddHistoryItemContext } from "./context/HistoryContext";
import {
  DecrementHoverDepthContext,
  HoverDepthContext,
  IncrementHoverDepthContext,
} from "./context/HoverDepthContext";
import { SelectedActorDispatchContext } from "./context/SelectedActorContext";
import { SelectedIntentionDispatchContext } from "./context/SelectedIntentionContext";

/**
 * A component that displays a clickable chip with a label and an optional color.
 * When hovered over, it displays an overlay with additional details about the element.
 * When clicked, it selects the element and displays its details in a separate screen.
 *
 * @param label - The label to display on the chip.
 * @param color - The color of the chip. Can be one of "default", "warning", "primary", "secondary", "error", "info", or "success".
 * @param element - The element to display details for when the chip is clicked or hovered over.
 */
export default function LinkHoverChip({
  label,
  color,
  element,
}: {
  label: string | undefined;
  color?:
    | "default"
    | "warning"
    | "primary"
    | "secondary"
    | "error"
    | "info"
    | "success";
  element?: Intention | Actor;
}) {
  const setSelectedActor = React.useContext(SelectedActorDispatchContext);
  const setSelectedIntention = React.useContext(
    SelectedIntentionDispatchContext
  );
  const addHistoryItem = React.useContext(AddHistoryItemContext);
  const hoverDepth = React.useContext(HoverDepthContext);
  const incrementHoverDepth = React.useContext(IncrementHoverDepthContext);
  const decrementHoverDepth = React.useContext(DecrementHoverDepthContext);
  const [showOverlay, setShowOverlay] = React.useState(false);
  const [delayHandler, setDelayHandler] = React.useState<null | NodeJS.Timeout>(
    null
  );
  return (
    <>
      <Chip
        component="span"
        label={label}
        color={color}
        size="small"
        onMouseEnter={(event) => {
          clearTimeout(delayHandler as NodeJS.Timeout);
          setDelayHandler(setTimeout(() => setShowOverlay(true), 500));
        }}
        onMouseLeave={(event) => {
          clearTimeout(delayHandler as NodeJS.Timeout);
          // setDelayHandler(setTimeout(() => setShowOverlay(false), 500));
        }}
        onClick={() => {
          console.log(element);
          if (elementIsActor(element)) {
            setSelectedActor(element as Actor);
            addHistoryItem(element as Actor);
          }
          if (elementIsIntention(element)) {
            setSelectedIntention(element as Intention);
            addHistoryItem(element as Intention);
          }
        }}
      />
      <Popper
        sx={{ mt: (hoverDepth?.current || 1) * 10, width: "50vw" }}
        open={showOverlay}
        onMouseEnter={(event) => {
          clearTimeout(delayHandler as NodeJS.Timeout);
          incrementHoverDepth();
          console.log(hoverDepth);
          setShowOverlay(true);
        }}
        onMouseLeave={(event) => {
          clearTimeout(delayHandler as NodeJS.Timeout);
          decrementHoverDepth();
          setShowOverlay(false);
        }}
      >
        <Paper elevation={10}>
          {elementIsActor(element) && (
            <DetailsScreen actor={element as Actor} />
          )}
          {elementIsIntention(element) && (
            <DetailsScreen intention={element as Intention} />
          )}
        </Paper>
      </Popper>
    </>
  );
}

/**
 * Returns true if the element is an Actor or Intention, false otherwise.
 *
 * @param element The element to check.
 * @returns True if the element is an Actor or Intention, false otherwise.
 */
function elementIsActor(element: Intention | Actor | undefined): boolean {
  return Object.values(ActorType).includes(element?.type as ActorType);
}

/**
 * Checks if an element is an instance of the Intention class.
 * @param element The element to check.
 * @returns True if the element is an instance of the Intention class, false otherwise.
 */
function elementIsIntention(element: Intention | Actor | undefined): boolean {
  return Object.values(IntentionType).includes(element?.type as IntentionType);
}