您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import type { FC } from 'react'
  2. import { useShallow } from 'zustand/react/shallow'
  3. import { memo, useCallback, useEffect, useRef } from 'react'
  4. import { useStore as useReactflow } from 'reactflow'
  5. import { Panel as NodePanel } from '../nodes'
  6. import { useStore } from '../store'
  7. import EnvPanel from './env-panel'
  8. import cn from '@/utils/classnames'
  9. export type PanelProps = {
  10. components?: {
  11. left?: React.ReactNode
  12. right?: React.ReactNode
  13. }
  14. }
  15. /**
  16. * Reference MDN standard implementation:https://developer.mozilla.org/zh-CN/docs/Web/API/ResizeObserverEntry/borderBoxSize
  17. */
  18. const getEntryWidth = (entry: ResizeObserverEntry, element: HTMLElement): number => {
  19. if (entry.borderBoxSize?.length > 0)
  20. return entry.borderBoxSize[0].inlineSize
  21. if (entry.contentRect.width > 0)
  22. return entry.contentRect.width
  23. return element.getBoundingClientRect().width
  24. }
  25. const useResizeObserver = (
  26. callback: (width: number) => void,
  27. dependencies: React.DependencyList = [],
  28. ) => {
  29. const elementRef = useRef<HTMLDivElement>(null)
  30. const stableCallback = useCallback(callback, [callback])
  31. useEffect(() => {
  32. const element = elementRef.current
  33. if (!element) return
  34. const resizeObserver = new ResizeObserver((entries) => {
  35. for (const entry of entries) {
  36. const width = getEntryWidth(entry, element)
  37. stableCallback(width)
  38. }
  39. })
  40. resizeObserver.observe(element)
  41. const initialWidth = element.getBoundingClientRect().width
  42. stableCallback(initialWidth)
  43. return () => {
  44. resizeObserver.disconnect()
  45. }
  46. }, [stableCallback, ...dependencies])
  47. return elementRef
  48. }
  49. const Panel: FC<PanelProps> = ({
  50. components,
  51. }) => {
  52. const selectedNode = useReactflow(useShallow((s) => {
  53. const nodes = s.getNodes()
  54. const currentNode = nodes.find(node => node.data.selected)
  55. if (currentNode) {
  56. return {
  57. id: currentNode.id,
  58. type: currentNode.type,
  59. data: currentNode.data,
  60. }
  61. }
  62. }))
  63. const showEnvPanel = useStore(s => s.showEnvPanel)
  64. const isRestoring = useStore(s => s.isRestoring)
  65. const showWorkflowVersionHistoryPanel = useStore(s => s.showWorkflowVersionHistoryPanel)
  66. // widths used for adaptive layout
  67. const workflowCanvasWidth = useStore(s => s.workflowCanvasWidth)
  68. const previewPanelWidth = useStore(s => s.previewPanelWidth)
  69. const setPreviewPanelWidth = useStore(s => s.setPreviewPanelWidth)
  70. // When a node is selected and the NodePanel appears, if the current width
  71. // of preview/otherPanel is too large, it may result in the total width of
  72. // the two panels exceeding the workflowCanvasWidth, causing the NodePanel
  73. // to be pushed out. Here we check and, if necessary, reduce the previewPanelWidth
  74. // to "workflowCanvasWidth - 400 (minimum NodePanel width) - 400 (minimum canvas space)",
  75. // while still ensuring that previewPanelWidth ≥ 400.
  76. useEffect(() => {
  77. if (!selectedNode || !workflowCanvasWidth)
  78. return
  79. const reservedCanvasWidth = 400 // Reserve the minimum visible width for the canvas
  80. const minNodePanelWidth = 400
  81. const maxAllowed = Math.max(workflowCanvasWidth - reservedCanvasWidth - minNodePanelWidth, 400)
  82. if (previewPanelWidth > maxAllowed)
  83. setPreviewPanelWidth(maxAllowed)
  84. }, [selectedNode, workflowCanvasWidth, previewPanelWidth, setPreviewPanelWidth])
  85. const setRightPanelWidth = useStore(s => s.setRightPanelWidth)
  86. const setOtherPanelWidth = useStore(s => s.setOtherPanelWidth)
  87. const rightPanelRef = useResizeObserver(
  88. setRightPanelWidth,
  89. [setRightPanelWidth, selectedNode, showEnvPanel, showWorkflowVersionHistoryPanel],
  90. )
  91. const otherPanelRef = useResizeObserver(
  92. setOtherPanelWidth,
  93. [setOtherPanelWidth, showEnvPanel, showWorkflowVersionHistoryPanel],
  94. )
  95. return (
  96. <div
  97. ref={rightPanelRef}
  98. tabIndex={-1}
  99. className={cn('absolute bottom-1 right-0 top-14 z-10 flex outline-none')}
  100. key={`${isRestoring}`}
  101. >
  102. {components?.left}
  103. {!!selectedNode && <NodePanel {...selectedNode} />}
  104. <div
  105. className="relative"
  106. ref={otherPanelRef}
  107. >
  108. {components?.right}
  109. {showEnvPanel && <EnvPanel />}
  110. </div>
  111. </div>
  112. )
  113. }
  114. export default memo(Panel)