import React, { useMemo, useState } from 'react' import { Edge as EdgeProps } from '@typebot.io/schemas' import { Portal, useColorMode, useDisclosure } from '@chakra-ui/react' import { useTypebot } from '@/features/editor/providers/TypebotProvider' import { colors } from '@/lib/theme' import { useEndpoints } from '../../providers/EndpointsProvider' import { computeEdgePath } from '../../helpers/computeEdgePath' import { getAnchorsPosition } from '../../helpers/getAnchorsPosition' import { useGraph } from '../../providers/GraphProvider' import { EdgeMenu } from './EdgeMenu' import { useEventsCoordinates } from '../../providers/EventsCoordinateProvider' import { eventWidth, groupWidth } from '../../constants' import { useGroupsStore } from '../../hooks/useGroupsStore' import { useShallow } from 'zustand/react/shallow' type Props = { edge: EdgeProps fromGroupId: string | undefined } export const Edge = ({ edge, fromGroupId }: Props) => { const isDark = useColorMode().colorMode === 'dark' const { deleteEdge } = useTypebot() const { previewingEdge, graphPosition, isReadOnly, setPreviewingEdge } = useGraph() const { sourceEndpointYOffsets, targetEndpointYOffsets } = useEndpoints() const fromGroupCoordinates = useGroupsStore( useShallow((state) => fromGroupId && state.groupsCoordinates ? state.groupsCoordinates[fromGroupId] : undefined ) ) const toGroupCoordinates = useGroupsStore( useShallow((state) => state.groupsCoordinates ? state.groupsCoordinates[edge.to.groupId] : undefined ) ) const { eventsCoordinates } = useEventsCoordinates() const [isMouseOver, setIsMouseOver] = useState(false) const { isOpen, onOpen, onClose } = useDisclosure() const [edgeMenuPosition, setEdgeMenuPosition] = useState({ x: 0, y: 0 }) const isPreviewing = isMouseOver || previewingEdge?.id === edge.id const sourceElementCoordinates = 'eventId' in edge.from ? eventsCoordinates[edge.from.eventId] : fromGroupCoordinates const sourceTop = useMemo(() => { const endpointId = 'eventId' in edge.from ? edge.from.eventId : edge?.from.itemId ?? edge?.from.blockId if (!endpointId) return return sourceEndpointYOffsets.get(endpointId)?.y }, [edge.from, sourceEndpointYOffsets]) const targetTop = useMemo( () => edge?.to.blockId ? targetEndpointYOffsets.get(edge?.to.blockId)?.y : undefined, [edge?.to.blockId, targetEndpointYOffsets] ) const path = useMemo(() => { if (!sourceElementCoordinates || !toGroupCoordinates || !sourceTop) return `` const anchorsPosition = getAnchorsPosition({ sourceGroupCoordinates: sourceElementCoordinates, targetGroupCoordinates: toGroupCoordinates, elementWidth: 'eventId' in edge.from ? eventWidth : groupWidth, sourceTop, targetTop, graphScale: graphPosition.scale, }) return computeEdgePath(anchorsPosition) }, [ sourceElementCoordinates, toGroupCoordinates, sourceTop, edge.from, targetTop, graphPosition.scale, ]) const handleMouseEnter = () => setIsMouseOver(true) const handleMouseLeave = () => setIsMouseOver(false) const handleEdgeClick = () => { setPreviewingEdge(edge) } const handleContextMenuTrigger = (e: React.MouseEvent) => { if (isReadOnly) return e.preventDefault() setEdgeMenuPosition({ x: e.clientX, y: e.clientY }) onOpen() } const handleDeleteEdge = () => deleteEdge(edge.id) return ( <> ) }