2
0
Files
bot/apps/builder/components/board/graph/BlockNode/BlockNode.tsx
2022-01-29 12:44:56 +01:00

123 lines
3.8 KiB
TypeScript

import {
Editable,
EditableInput,
EditablePreview,
Stack,
useEventListener,
} from '@chakra-ui/react'
import React, { useEffect, useMemo, useState } from 'react'
import { Block } from 'models'
import { useGraph } from 'contexts/GraphContext'
import { useStepDnd } from 'contexts/StepDndContext'
import { StepsList } from './StepsList'
import { isDefined } from 'utils'
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
import { ContextMenu } from 'components/shared/ContextMenu'
import { BlockNodeContextMenu } from './BlockNodeContextMenu'
type Props = {
block: Block
}
export const BlockNode = ({ block }: Props) => {
const { connectingIds, setConnectingIds, previewingEdgeId } = useGraph()
const { typebot, updateBlock } = useTypebot()
const { setMouseOverBlockId } = useStepDnd()
const { draggedStep, draggedStepType } = useStepDnd()
const [isMouseDown, setIsMouseDown] = useState(false)
const [isConnecting, setIsConnecting] = useState(false)
const isPreviewing = useMemo(() => {
if (!previewingEdgeId) return
const edge = typebot?.edges.byId[previewingEdgeId]
return edge?.to.blockId === block.id || edge?.from.blockId === block.id
}, [block.id, previewingEdgeId, typebot?.edges.byId])
useEffect(() => {
setIsConnecting(
connectingIds?.target?.blockId === block.id &&
!isDefined(connectingIds.target?.stepId)
)
}, [block.id, connectingIds])
const handleTitleSubmit = (title: string) => updateBlock(block.id, { title })
const handleMouseDown = () => {
setIsMouseDown(true)
}
const handleMouseUp = () => {
setIsMouseDown(false)
}
useEventListener('mouseup', handleMouseUp)
const handleMouseMove = (event: MouseEvent) => {
if (!isMouseDown) return
const { movementX, movementY } = event
updateBlock(block.id, {
graphCoordinates: {
x: block.graphCoordinates.x + movementX,
y: block.graphCoordinates.y + movementY,
},
})
}
useEventListener('mousemove', handleMouseMove)
const handleMouseEnter = () => {
if (draggedStepType || draggedStep) setMouseOverBlockId(block.id)
if (connectingIds)
setConnectingIds({ ...connectingIds, target: { blockId: block.id } })
}
const handleMouseLeave = () => {
setMouseOverBlockId(undefined)
if (connectingIds) setConnectingIds({ ...connectingIds, target: undefined })
}
return (
<ContextMenu<HTMLDivElement>
renderMenu={() => <BlockNodeContextMenu blockId={block.id} />}
>
{(ref, isOpened) => (
<Stack
ref={ref}
p="4"
rounded="lg"
bgColor="blue.50"
backgroundImage="linear-gradient(rgb(235, 239, 244), rgb(231, 234, 241))"
borderWidth="2px"
borderColor={
isConnecting || isOpened || isPreviewing ? 'blue.400' : 'white'
}
w="300px"
transition="border 300ms, box-shadow 200ms"
pos="absolute"
style={{
transform: `translate(${block.graphCoordinates.x}px, ${block.graphCoordinates.y}px)`,
}}
onMouseDown={handleMouseDown}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
cursor={isMouseDown ? 'grabbing' : 'pointer'}
boxShadow="0px 0px 0px 1px #e9edf3;"
_hover={{ shadow: 'lg' }}
>
<Editable
defaultValue={block.title}
onSubmit={handleTitleSubmit}
fontWeight="semibold"
>
<EditablePreview
_hover={{ bgColor: 'gray.300' }}
px="1"
userSelect={'none'}
/>
<EditableInput minW="0" px="1" />
</Editable>
{typebot && <StepsList blockId={block.id} stepIds={block.stepIds} />}
</Stack>
)}
</ContextMenu>
)
}