|
|
|
|
@ -1,23 +1,25 @@
|
|
|
|
|
import { memo } from 'react'
|
|
|
|
|
import { useTranslation } from 'react-i18next'
|
|
|
|
|
import { RiCrosshairLine } from '@remixicon/react'
|
|
|
|
|
import type { XYPosition } from 'reactflow'
|
|
|
|
|
import { useReactFlow, useStoreApi } from 'reactflow'
|
|
|
|
|
import TooltipPlus from '@/app/components/base/tooltip'
|
|
|
|
|
|
|
|
|
|
type NodePositionProps = {
|
|
|
|
|
nodeId: string
|
|
|
|
|
position: XYPosition,
|
|
|
|
|
width?: number | null,
|
|
|
|
|
height?: number | null,
|
|
|
|
|
}
|
|
|
|
|
const NodePosition = ({
|
|
|
|
|
nodeId,
|
|
|
|
|
position,
|
|
|
|
|
width,
|
|
|
|
|
height,
|
|
|
|
|
}: NodePositionProps) => {
|
|
|
|
|
const { t } = useTranslation()
|
|
|
|
|
const reactflow = useReactFlow()
|
|
|
|
|
const store = useStoreApi()
|
|
|
|
|
|
|
|
|
|
if (!nodeId) return null
|
|
|
|
|
|
|
|
|
|
const node = reactflow.getNode(nodeId)
|
|
|
|
|
if (node == null) return null
|
|
|
|
|
if (!position || !width || !height) return null
|
|
|
|
|
|
|
|
|
|
const workflowContainer = document.getElementById('workflow-container')
|
|
|
|
|
const { transform } = store.getState()
|
|
|
|
|
@ -25,7 +27,6 @@ const NodePosition = ({
|
|
|
|
|
|
|
|
|
|
const { clientWidth, clientHeight } = workflowContainer!
|
|
|
|
|
const { setViewport } = reactflow
|
|
|
|
|
const position = node.position
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<TooltipPlus
|
|
|
|
|
@ -35,8 +36,8 @@ const NodePosition = ({
|
|
|
|
|
className='mr-1 flex h-6 w-6 cursor-pointer items-center justify-center rounded-md hover:bg-state-base-hover'
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setViewport({
|
|
|
|
|
x: (clientWidth - 400 - node.width! * zoom) / 2 - position.x * zoom,
|
|
|
|
|
y: (clientHeight - node.height! * zoom) / 2 - position.y * zoom,
|
|
|
|
|
x: (clientWidth - 400 - width * zoom) / 2 - position.x * zoom,
|
|
|
|
|
y: (clientHeight - height * zoom) / 2 - position.y * zoom,
|
|
|
|
|
zoom: transform[2],
|
|
|
|
|
})
|
|
|
|
|
}}
|
|
|
|
|
|