2
0
Files
bot/apps/builder/components/shared/Graph/Nodes/BlockNode/BlockNode.tsx

163 lines
4.9 KiB
TypeScript
Raw Normal View History

2021-12-16 10:43:49 +01:00
import {
Editable,
EditableInput,
EditablePreview,
Stack,
useEventListener,
} from '@chakra-ui/react'
import React, { useEffect, useRef, useState } from 'react'
2022-01-06 09:40:56 +01:00
import { Block } from 'models'
2021-12-16 10:43:49 +01:00
import { useGraph } from 'contexts/GraphContext'
import { useStepDnd } from 'contexts/GraphDndContext'
import { StepNodesList } from '../StepNode/StepNodesList'
import { isNotDefined } from 'utils'
2022-01-06 09:40:56 +01:00
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
import { ContextMenu } from 'components/shared/ContextMenu'
import { BlockNodeContextMenu } from './BlockNodeContextMenu'
import { useDebounce } from 'use-debounce'
import { setMultipleRefs } from 'services/utils'
2021-12-16 10:43:49 +01:00
2022-01-03 17:39:59 +01:00
type Props = {
block: Block
blockIndex: number
2022-01-03 17:39:59 +01:00
}
export const BlockNode = ({ block, blockIndex }: Props) => {
const {
connectingIds,
setConnectingIds,
previewingEdge,
blocksCoordinates,
updateBlockCoordinates,
isReadOnly,
} = useGraph()
2022-01-12 09:10:59 +01:00
const { typebot, updateBlock } = useTypebot()
const { setMouseOverBlock, mouseOverBlock } = useStepDnd()
2021-12-16 10:43:49 +01:00
const [isMouseDown, setIsMouseDown] = useState(false)
const [isConnecting, setIsConnecting] = useState(false)
const isPreviewing =
previewingEdge?.to.blockId === block.id ||
previewingEdge?.from.blockId === block.id
const isStartBlock =
block.steps.length === 1 && block.steps[0].type === 'start'
2021-12-16 10:43:49 +01:00
const blockCoordinates = blocksCoordinates[block.id]
const blockRef = useRef<HTMLDivElement | null>(null)
const [debouncedBlockPosition] = useDebounce(blockCoordinates, 100)
useEffect(() => {
if (!debouncedBlockPosition || isReadOnly) return
if (
debouncedBlockPosition?.x === block.graphCoordinates.x &&
debouncedBlockPosition.y === block.graphCoordinates.y
)
return
updateBlock(blockIndex, { graphCoordinates: debouncedBlockPosition })
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [debouncedBlockPosition])
2021-12-16 10:43:49 +01:00
useEffect(() => {
setIsConnecting(
connectingIds?.target?.blockId === block.id &&
isNotDefined(connectingIds.target?.stepId)
2021-12-16 10:43:49 +01:00
)
}, [block.id, connectingIds])
const handleTitleSubmit = (title: string) =>
updateBlock(blockIndex, { title })
2021-12-16 10:43:49 +01:00
const handleMouseDown = (e: React.MouseEvent) => {
e.stopPropagation()
2021-12-16 10:43:49 +01:00
setIsMouseDown(true)
}
const handleMouseUp = () => {
setIsMouseDown(false)
}
2022-01-19 09:44:21 +01:00
useEventListener('mouseup', handleMouseUp)
2021-12-16 10:43:49 +01:00
const handleMouseMove = (event: MouseEvent) => {
if (!isMouseDown) return
const { movementX, movementY } = event
if (!blockCoordinates) return
updateBlockCoordinates(block.id, {
x: blockCoordinates.x + movementX,
y: blockCoordinates.y + movementY,
2021-12-16 10:43:49 +01:00
})
}
useEventListener('mousemove', handleMouseMove)
const handleMouseEnter = () => {
2022-02-11 18:06:59 +01:00
if (isReadOnly) return
if (mouseOverBlock?.id !== block.id && !isStartBlock)
setMouseOverBlock({ id: block.id, ref: blockRef })
2021-12-16 10:43:49 +01:00
if (connectingIds)
setConnectingIds({ ...connectingIds, target: { blockId: block.id } })
}
const handleMouseLeave = () => {
2022-02-11 18:06:59 +01:00
if (isReadOnly) return
setMouseOverBlock(undefined)
2021-12-16 10:43:49 +01:00
if (connectingIds) setConnectingIds({ ...connectingIds, target: undefined })
}
return (
<ContextMenu<HTMLDivElement>
renderMenu={() => <BlockNodeContextMenu blockIndex={blockIndex} />}
isDisabled={isReadOnly}
2021-12-16 10:43:49 +01:00
>
{(ref, isOpened) => (
<Stack
ref={setMultipleRefs([ref, blockRef])}
data-testid="block"
p="4"
rounded="lg"
bgColor="blue.50"
2022-01-19 09:44:21 +01:00
backgroundImage="linear-gradient(rgb(235, 239, 244), rgb(231, 234, 241))"
borderWidth="2px"
borderColor={
2022-01-19 09:44:21 +01:00
isConnecting || isOpened || isPreviewing ? 'blue.400' : 'white'
}
2022-01-22 18:24:57 +01:00
w="300px"
2022-01-19 09:44:21 +01:00
transition="border 300ms, box-shadow 200ms"
pos="absolute"
style={{
transform: `translate(${blockCoordinates?.x ?? 0}px, ${
blockCoordinates?.y ?? 0
}px)`,
}}
onMouseDown={handleMouseDown}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
2022-01-19 09:44:21 +01:00
cursor={isMouseDown ? 'grabbing' : 'pointer'}
boxShadow="0px 0px 0px 1px #e9edf3;"
_hover={{ shadow: 'lg' }}
>
2022-01-19 09:44:21 +01:00
<Editable
defaultValue={block.title}
onSubmit={handleTitleSubmit}
fontWeight="semibold"
2022-02-11 18:06:59 +01:00
pointerEvents={isReadOnly ? 'none' : 'auto'}
2022-01-19 09:44:21 +01:00
>
<EditablePreview
2022-01-19 09:44:21 +01:00
_hover={{ bgColor: 'gray.300' }}
px="1"
userSelect={'none'}
/>
<EditableInput minW="0" px="1" />
</Editable>
{typebot && (
<StepNodesList
blockId={block.id}
steps={block.steps}
blockIndex={blockIndex}
blockRef={ref}
isStartBlock={isStartBlock}
/>
)}
</Stack>
)}
</ContextMenu>
2021-12-16 10:43:49 +01:00
)
}