import { Flex, HStack, Popover, PopoverTrigger, useDisclosure, } from '@chakra-ui/react' import React, { useEffect, useRef, useState } from 'react' import { BubbleStep, BubbleStepContent, DraggableStep, Step, TextBubbleContent, TextBubbleStep, } from 'models' import { useGraph } from 'contexts/GraphContext' import { StepIcon } from 'components/editor/StepsSideBar/StepIcon' import { isBubbleStep, isTextBubbleStep } from 'utils' import { StepNodeContent } from './StepNodeContent/StepNodeContent' import { useTypebot } from 'contexts/TypebotContext' import { ContextMenu } from 'components/shared/ContextMenu' import { SettingsPopoverContent } from './SettingsPopoverContent' import { StepNodeContextMenu } from './StepNodeContextMenu' import { SourceEndpoint } from '../../Endpoints/SourceEndpoint' import { hasDefaultConnector } from 'services/typebots' import { useRouter } from 'next/router' import { SettingsModal } from './SettingsPopoverContent/SettingsModal' import { StepSettings } from './SettingsPopoverContent/SettingsPopoverContent' import { TextBubbleEditor } from './TextBubbleEditor' import { TargetEndpoint } from '../../Endpoints' import { MediaBubblePopoverContent } from './MediaBubblePopoverContent' import { NodePosition, useDragDistance } from 'contexts/GraphDndContext' import { setMultipleRefs } from 'services/utils' export const StepNode = ({ step, isConnectable, indices, onMouseDown, }: { step: Step isConnectable: boolean indices: { stepIndex: number; blockIndex: number } onMouseDown?: (stepNodePosition: NodePosition, step: DraggableStep) => void }) => { const { query } = useRouter() const { setConnectingIds, connectingIds, openedStepId, setOpenedStepId } = useGraph() const { updateStep } = useTypebot() const [isConnecting, setIsConnecting] = useState(false) const [isPopoverOpened, setIsPopoverOpened] = useState( openedStepId === step.id ) const [isEditing, setIsEditing] = useState( isTextBubbleStep(step) && step.content.plainText === '' ) const stepRef = useRef(null) const onDrag = (position: NodePosition) => { if (step.type === 'start' || !onMouseDown) return onMouseDown(position, step) } useDragDistance({ ref: stepRef, onDrag, isDisabled: !onMouseDown || step.type === 'start', }) const { isOpen: isModalOpen, onOpen: onModalOpen, onClose: onModalClose, } = useDisclosure() useEffect(() => { if (query.stepId?.toString() === step.id) setOpenedStepId(step.id) // eslint-disable-next-line react-hooks/exhaustive-deps }, [query]) useEffect(() => { setIsConnecting( connectingIds?.target?.blockId === step.blockId && connectingIds?.target?.stepId === step.id ) }, [connectingIds, step.blockId, step.id]) const handleModalClose = () => { updateStep(indices, { ...step }) onModalClose() } const handleMouseEnter = () => { if (connectingIds) setConnectingIds({ ...connectingIds, target: { blockId: step.blockId, stepId: step.id }, }) } const handleMouseLeave = () => { if (connectingIds?.target) setConnectingIds({ ...connectingIds, target: { ...connectingIds.target, stepId: undefined }, }) } const handleCloseEditor = (content: TextBubbleContent) => { const updatedStep = { ...step, content } as Step updateStep(indices, updatedStep) setIsEditing(false) } const handleClick = (e: React.MouseEvent) => { e.stopPropagation() if (isTextBubbleStep(step)) setIsEditing(true) setOpenedStepId(step.id) } const handleExpandClick = () => { setOpenedStepId(undefined) onModalOpen() } const handleStepUpdate = (updates: Partial) => updateStep(indices, { ...step, ...updates }) const handleContentChange = (content: BubbleStepContent) => updateStep(indices, { ...step, content } as Step) useEffect(() => { setIsPopoverOpened(openedStepId === step.id) // eslint-disable-next-line react-hooks/exhaustive-deps }, [openedStepId]) return isEditing && isTextBubbleStep(step) ? ( ) : ( renderMenu={() => } > {(ref, isOpened) => ( {isConnectable && hasDefaultConnector(step) && ( )} {hasSettingsPopover(step) && ( )} {isMediaBubbleStep(step) && ( )} )} ) } const hasSettingsPopover = (step: Step): step is Exclude => !isBubbleStep(step) const isMediaBubbleStep = ( step: Step ): step is Exclude => isBubbleStep(step) && !isTextBubbleStep(step)