157 lines
4.7 KiB
TypeScript
Raw Normal View History

import type { FC } from 'react'
import { memo, useCallback, useEffect, useRef } from 'react'
2025-05-08 18:27:15 +08:00
import type { VersionHistoryPanelProps } from '@/app/components/workflow/panel/version-history-panel'
2025-07-02 18:07:09 +08:00
import { useShallow } from 'zustand/react/shallow'
2025-07-02 17:48:23 +08:00
import { useStore as useReactflow } from 'reactflow'
import { Panel as NodePanel } from '../nodes'
import { useStore } from '../store'
import EnvPanel from './env-panel'
import cn from '@/utils/classnames'
import dynamic from 'next/dynamic'
const VersionHistoryPanel = dynamic(() => import('@/app/components/workflow/panel/version-history-panel'), {
ssr: false,
})
2025-04-18 13:59:12 +08:00
export type PanelProps = {
components?: {
left?: React.ReactNode
right?: React.ReactNode
}
2025-05-08 18:27:15 +08:00
versionHistoryPanelProps?: VersionHistoryPanelProps
2025-04-18 13:59:12 +08:00
}
/**
* Reference MDN standard implementationhttps://developer.mozilla.org/zh-CN/docs/Web/API/ResizeObserverEntry/borderBoxSize
*/
const getEntryWidth = (entry: ResizeObserverEntry, element: HTMLElement): number => {
if (entry.borderBoxSize?.length > 0)
return entry.borderBoxSize[0].inlineSize
if (entry.contentRect.width > 0)
return entry.contentRect.width
return element.getBoundingClientRect().width
}
const useResizeObserver = (
callback: (width: number) => void,
dependencies: React.DependencyList = [],
) => {
const elementRef = useRef<HTMLDivElement>(null)
const stableCallback = useCallback(callback, [callback])
useEffect(() => {
const element = elementRef.current
if (!element) return
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
const width = getEntryWidth(entry, element)
stableCallback(width)
}
})
resizeObserver.observe(element)
const initialWidth = element.getBoundingClientRect().width
stableCallback(initialWidth)
return () => {
resizeObserver.disconnect()
}
}, [stableCallback, ...dependencies])
return elementRef
}
2025-04-18 13:59:12 +08:00
const Panel: FC<PanelProps> = ({
components,
2025-05-08 18:27:15 +08:00
versionHistoryPanelProps,
2025-04-18 13:59:12 +08:00
}) => {
2025-07-02 17:48:23 +08:00
const selectedNode = useReactflow(useShallow((s) => {
const nodes = s.getNodes()
const currentNode = nodes.find(node => node.data.selected)
if (currentNode) {
return {
id: currentNode.id,
type: currentNode.type,
data: currentNode.data,
}
}
}))
const showEnvPanel = useStore(s => s.showEnvPanel)
2024-04-24 13:05:33 +08:00
const isRestoring = useStore(s => s.isRestoring)
2025-05-08 18:27:15 +08:00
const showWorkflowVersionHistoryPanel = useStore(s => s.showWorkflowVersionHistoryPanel)
// widths used for adaptive layout
const workflowCanvasWidth = useStore(s => s.workflowCanvasWidth)
const previewPanelWidth = useStore(s => s.previewPanelWidth)
const setPreviewPanelWidth = useStore(s => s.setPreviewPanelWidth)
// When a node is selected and the NodePanel appears, if the current width
// of preview/otherPanel is too large, it may result in the total width of
// the two panels exceeding the workflowCanvasWidth, causing the NodePanel
// to be pushed out. Here we check and, if necessary, reduce the previewPanelWidth
// to "workflowCanvasWidth - 400 (minimum NodePanel width) - 400 (minimum canvas space)",
// while still ensuring that previewPanelWidth ≥ 400.
useEffect(() => {
if (!selectedNode || !workflowCanvasWidth)
return
const reservedCanvasWidth = 400 // Reserve the minimum visible width for the canvas
const minNodePanelWidth = 400
const maxAllowed = Math.max(workflowCanvasWidth - reservedCanvasWidth - minNodePanelWidth, 400)
if (previewPanelWidth > maxAllowed)
setPreviewPanelWidth(maxAllowed)
}, [selectedNode, workflowCanvasWidth, previewPanelWidth, setPreviewPanelWidth])
const setRightPanelWidth = useStore(s => s.setRightPanelWidth)
const setOtherPanelWidth = useStore(s => s.setOtherPanelWidth)
const rightPanelRef = useResizeObserver(
setRightPanelWidth,
[setRightPanelWidth, selectedNode, showEnvPanel, showWorkflowVersionHistoryPanel],
)
const otherPanelRef = useResizeObserver(
setOtherPanelWidth,
[setOtherPanelWidth, showEnvPanel, showWorkflowVersionHistoryPanel],
)
return (
2024-04-25 14:02:06 +08:00
<div
ref={rightPanelRef}
2024-04-25 14:02:06 +08:00
tabIndex={-1}
className={cn('absolute bottom-1 right-0 top-14 z-10 flex outline-none')}
2024-04-25 14:02:06 +08:00
key={`${isRestoring}`}
>
{components?.left}
{!!selectedNode && <NodePanel {...selectedNode} />}
<div
className="relative"
ref={otherPanelRef}
>
{
components?.right
}
2025-06-26 15:21:24 +08:00
{
showWorkflowVersionHistoryPanel && (
<VersionHistoryPanel {...versionHistoryPanelProps} />
)
}
{
showEnvPanel && (
<EnvPanel />
)
}
</div>
</div>
)
}
export default memo(Panel)