import { useCallback } from 'react' import produce from 'immer' import { useTranslation } from 'react-i18next' import { useStoreApi } from 'reactflow' import type { BlockEnum, Node, } from '../../types' import { generateNewNode } from '../../utils' import { ITERATION_PADDING, NODES_INITIAL_DATA, } from '../../constants' import { CUSTOM_ITERATION_START_NODE } from '../iteration-start/constants' export const useNodeIterationInteractions = () => { const { t } = useTranslation() const store = useStoreApi() const handleNodeIterationRerender = useCallback((nodeId: string) => { const { getNodes, setNodes, } = store.getState() const nodes = getNodes() const currentNode = nodes.find(n => n.id === nodeId)! const childrenNodes = nodes.filter(n => n.parentId === nodeId) let rightNode: Node let bottomNode: Node childrenNodes.forEach((n) => { if (rightNode) { if (n.position.x + n.width! > rightNode.position.x + rightNode.width!) rightNode = n } else { rightNode = n } if (bottomNode) { if (n.position.y + n.height! > bottomNode.position.y + bottomNode.height!) bottomNode = n } else { bottomNode = n } }) const widthShouldExtend = rightNode! && currentNode.width! < rightNode.position.x + rightNode.width! const heightShouldExtend = bottomNode! && currentNode.height! < bottomNode.position.y + bottomNode.height! if (widthShouldExtend || heightShouldExtend) { const newNodes = produce(nodes, (draft) => { draft.forEach((n) => { if (n.id === nodeId) { if (widthShouldExtend) { n.data.width = rightNode.position.x + rightNode.width! + ITERATION_PADDING.right n.width = rightNode.position.x + rightNode.width! + ITERATION_PADDING.right } if (heightShouldExtend) { n.data.height = bottomNode.position.y + bottomNode.height! + ITERATION_PADDING.bottom n.height = bottomNode.position.y + bottomNode.height! + ITERATION_PADDING.bottom } } }) }) setNodes(newNodes) } }, [store]) const handleNodeIterationChildDrag = useCallback((node: Node) => { const { getNodes } = store.getState() const nodes = getNodes() const restrictPosition: { x?: number; y?: number } = { x: undefined, y: undefined } if (node.data.isInIteration) { const parentNode = nodes.find(n => n.id === node.parentId) if (parentNode) { if (node.position.y < ITERATION_PADDING.top) restrictPosition.y = ITERATION_PADDING.top if (node.position.x < ITERATION_PADDING.left) restrictPosition.x = ITERATION_PADDING.left if (node.position.x + node.width! > parentNode!.width! - ITERATION_PADDING.right) restrictPosition.x = parentNode!.width! - ITERATION_PADDING.right - node.width! if (node.position.y + node.height! > parentNode!.height! - ITERATION_PADDING.bottom) restrictPosition.y = parentNode!.height! - ITERATION_PADDING.bottom - node.height! } } return { restrictPosition, } }, [store]) const handleNodeIterationChildSizeChange = useCallback((nodeId: string) => { const { getNodes } = store.getState() const nodes = getNodes() const currentNode = nodes.find(n => n.id === nodeId)! const parentId = currentNode.parentId if (parentId) handleNodeIterationRerender(parentId) }, [store, handleNodeIterationRerender]) const handleNodeIterationChildrenCopy = useCallback((nodeId: string, newNodeId: string) => { const { getNodes } = store.getState() const nodes = getNodes() const childrenNodes = nodes.filter(n => n.parentId === nodeId && n.type !== CUSTOM_ITERATION_START_NODE) return childrenNodes.map((child, index) => { const childNodeType = child.data.type as BlockEnum const nodesWithSameType = nodes.filter(node => node.data.type === childNodeType) const { newNode } = generateNewNode({ data: { ...NODES_INITIAL_DATA[childNodeType], ...child.data, selected: false, _isBundled: false, _connectedSourceHandleIds: [], _connectedTargetHandleIds: [], title: nodesWithSameType.length > 0 ? `${t(`workflow.blocks.${childNodeType}`)} ${nodesWithSameType.length + 1}` : t(`workflow.blocks.${childNodeType}`), iteration_id: newNodeId, }, position: child.position, positionAbsolute: child.positionAbsolute, parentId: newNodeId, extent: child.extent, zIndex: child.zIndex, }) newNode.id = `${newNodeId}${newNode.id + index}` return newNode }) }, [store, t]) return { handleNodeIterationRerender, handleNodeIterationChildDrag, handleNodeIterationChildSizeChange, handleNodeIterationChildrenCopy, } }