dify
This commit is contained in:
156
dify/web/app/components/workflow/panel/index.tsx
Normal file
156
dify/web/app/components/workflow/panel/index.tsx
Normal file
@@ -0,0 +1,156 @@
|
||||
import type { FC } from 'react'
|
||||
import { memo, useCallback, useEffect, useRef } from 'react'
|
||||
import type { VersionHistoryPanelProps } from '@/app/components/workflow/panel/version-history-panel'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
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,
|
||||
})
|
||||
|
||||
export type PanelProps = {
|
||||
components?: {
|
||||
left?: React.ReactNode
|
||||
right?: React.ReactNode
|
||||
}
|
||||
versionHistoryPanelProps?: VersionHistoryPanelProps
|
||||
}
|
||||
|
||||
/**
|
||||
* Reference MDN standard implementation:https://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
|
||||
}
|
||||
|
||||
const Panel: FC<PanelProps> = ({
|
||||
components,
|
||||
versionHistoryPanelProps,
|
||||
}) => {
|
||||
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)
|
||||
const isRestoring = useStore(s => s.isRestoring)
|
||||
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 (
|
||||
<div
|
||||
ref={rightPanelRef}
|
||||
tabIndex={-1}
|
||||
className={cn('absolute bottom-1 right-0 top-14 z-10 flex outline-none')}
|
||||
key={`${isRestoring}`}
|
||||
>
|
||||
{components?.left}
|
||||
{!!selectedNode && <NodePanel {...selectedNode} />}
|
||||
<div
|
||||
className="relative"
|
||||
ref={otherPanelRef}
|
||||
>
|
||||
{
|
||||
components?.right
|
||||
}
|
||||
{
|
||||
showWorkflowVersionHistoryPanel && (
|
||||
<VersionHistoryPanel {...versionHistoryPanelProps} />
|
||||
)
|
||||
}
|
||||
{
|
||||
showEnvPanel && (
|
||||
<EnvPanel />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(Panel)
|
||||
Reference in New Issue
Block a user