feat: copy cross page

pull/21256/head
G.Wood-Sun 11 months ago
parent c05e47ebc0
commit c490e774b1

@ -17,3 +17,5 @@ export * from './use-workflow-interactions'
export * from './use-workflow-mode' export * from './use-workflow-mode'
export * from './use-format-time-from-now' export * from './use-format-time-from-now'
export * from './use-workflow-refresh-draft' export * from './use-workflow-refresh-draft'
export * from './use-selection-graph-menu'
export * from './use-selection-paste'

@ -1356,6 +1356,8 @@ export const useNodesInteractions = () => {
} }
}) })
console.log(nodesToPaste, edgesToPaste)
setNodes([...nodes, ...nodesToPaste]) setNodes([...nodes, ...nodesToPaste])
setEdges([...edges, ...edgesToPaste]) setEdges([...edges, ...edgesToPaste])
saveStateToHistory(WorkflowHistoryEvent.NodePaste) saveStateToHistory(WorkflowHistoryEvent.NodePaste)

@ -26,6 +26,7 @@ export const usePanelInteractions = () => {
const handleNodeContextmenuCancel = useCallback(() => { const handleNodeContextmenuCancel = useCallback(() => {
workflowStore.setState({ workflowStore.setState({
nodeMenu: undefined, nodeMenu: undefined,
selectPanelMenu: undefined,
}) })
}, [workflowStore]) }, [workflowStore])

@ -0,0 +1,36 @@
import type { MouseEvent } from 'react'
import { useCallback } from 'react'
import { useWorkflowStore } from '../store'
export const useSelectionGraphMenu = () => {
const workflowStore = useWorkflowStore()
const { setSelectPanelMenu, setNodeMenu, setPanelMenu } = workflowStore.getState()
const handleSelectPanelContextMenu = useCallback((e: MouseEvent) => {
e.preventDefault()
console.log('handleSelectPanelContextMenu', e.clientX, e.clientY)
const container = document.querySelector('#workflow-container')
const { x, y } = container!.getBoundingClientRect()
setSelectPanelMenu({
top: e.clientY - y,
left: e.clientX - x,
})
}, [setSelectPanelMenu])
const handleSelectPanelContextmenuCancel = useCallback(() => {
setSelectPanelMenu(undefined)
}, [setSelectPanelMenu])
const handleOtherContextmenuCancel = useCallback(() => {
setNodeMenu(undefined)
setPanelMenu(undefined)
}, [setNodeMenu, setPanelMenu])
return {
handleSelectPanelContextMenu,
handleSelectPanelContextmenuCancel,
handleOtherContextmenuCancel,
}
}

@ -0,0 +1,25 @@
import {
useCallback,
} from 'react'
import type {
OnSelectionChangeParams,
} from 'reactflow'
import { useOnSelectionChange } from 'reactflow'
import { useWorkflowStore } from '../store'
export const useSelectionGraph = () => {
const workflowStore = useWorkflowStore()
const { setSelectGraph } = workflowStore.getState()
const onChange = useCallback((params: OnSelectionChangeParams) => {
const { nodes, edges } = params
setSelectGraph({
nodes,
edges,
})
}, [])
useOnSelectionChange({
onChange,
})
}

@ -0,0 +1,28 @@
import type { RefObject } from 'react'
import { useEffect } from'react'
import {
useStore,
} from 'reactflow'
type UseSelectionPasteProps = {
workflowContainerRef: RefObject<HTMLDivElement>
}
export const useSelectionPaste = ({ workflowContainerRef }: UseSelectionPasteProps) => {
const domNode = useStore(s => s.domNode)
const handlePaste = (e: ClipboardEvent) => {
e.preventDefault()
console.log(e.clipboardData?.getData('text'))
}
useEffect(() => {
if (domNode) {
console.log('workflowContainerRef.current', domNode)
domNode.addEventListener('paste', handlePaste)
}
return () => {
if (domNode)
domNode.removeEventListener('paste', handlePaste)
}
}, [domNode])
}

@ -41,6 +41,7 @@ import {
useNodesSyncDraft, useNodesSyncDraft,
usePanelInteractions, usePanelInteractions,
useSelectionInteractions, useSelectionInteractions,
useSelectionPaste,
useShortcuts, useShortcuts,
useWorkflow, useWorkflow,
useWorkflowReadOnly, useWorkflowReadOnly,
@ -80,6 +81,9 @@ import Confirm from '@/app/components/base/confirm'
import DatasetsDetailProvider from './datasets-detail-store/provider' import DatasetsDetailProvider from './datasets-detail-store/provider'
import { HooksStoreContextProvider } from './hooks-store' import { HooksStoreContextProvider } from './hooks-store'
import type { Shape as HooksStoreShape } from './hooks-store' import type { Shape as HooksStoreShape } from './hooks-store'
import { useSelectionGraph } from './hooks/use-selection-graph'
import { useSelectionGraphMenu } from './hooks/use-selection-graph-menu'
import SelectPanelContextmenu from './select-panel-contextmenu'
const nodeTypes = { const nodeTypes = {
[CUSTOM_NODE]: CustomNode, [CUSTOM_NODE]: CustomNode,
@ -115,6 +119,14 @@ export const Workflow: FC<WorkflowProps> = memo(({
const nodeAnimation = useStore(s => s.nodeAnimation) const nodeAnimation = useStore(s => s.nodeAnimation)
const showConfirm = useStore(s => s.showConfirm) const showConfirm = useStore(s => s.showConfirm)
// add selection hook
useSelectionGraph()
// add paste hook
useSelectionPaste({
workflowContainerRef,
})
const { handleSelectPanelContextMenu } = useSelectionGraphMenu()
const { const {
setShowConfirm, setShowConfirm,
setControlPromptEditorRerenderKey, setControlPromptEditorRerenderKey,
@ -270,6 +282,7 @@ export const Workflow: FC<WorkflowProps> = memo(({
<Operator handleRedo={handleHistoryForward} handleUndo={handleHistoryBack} /> <Operator handleRedo={handleHistoryForward} handleUndo={handleHistoryBack} />
<PanelContextmenu /> <PanelContextmenu />
<NodeContextmenu /> <NodeContextmenu />
<SelectPanelContextmenu />
<HelpLine /> <HelpLine />
{ {
!!showConfirm && ( !!showConfirm && (
@ -307,6 +320,7 @@ export const Workflow: FC<WorkflowProps> = memo(({
onSelectionDrag={handleSelectionDrag} onSelectionDrag={handleSelectionDrag}
onPaneContextMenu={handlePaneContextMenu} onPaneContextMenu={handlePaneContextMenu}
connectionLineComponent={CustomConnectionLine} connectionLineComponent={CustomConnectionLine}
onSelectionContextMenu={handleSelectPanelContextMenu}
// TODO: For LOOP node, how to distinguish between ITERATION and LOOP here? Maybe both are the same? // TODO: For LOOP node, how to distinguish between ITERATION and LOOP here? Maybe both are the same?
connectionLineContainerStyle={{ zIndex: ITERATION_CHILDREN_Z_INDEX }} connectionLineContainerStyle={{ zIndex: ITERATION_CHILDREN_Z_INDEX }}
defaultViewport={viewport} defaultViewport={viewport}
@ -345,6 +359,16 @@ export const WorkflowWithInnerContext = memo(({
hooksStore, hooksStore,
...restProps ...restProps
}: WorkflowWithInnerContextProps) => { }: WorkflowWithInnerContextProps) => {
const handlePaste = (e: ClipboardEvent) => {
console.log(e, 123)
}
useEffect(() => {
document.body.addEventListener('paste', handlePaste, true)
return () => {
document.body.removeEventListener('paste', handlePaste, true)
}
}, [])
return ( return (
<HooksStoreContextProvider {...hooksStore}> <HooksStoreContextProvider {...hooksStore}>
<Workflow {...restProps} /> <Workflow {...restProps} />

@ -30,6 +30,8 @@ const PanelContextmenu = () => {
const { handleAddNote } = useOperator() const { handleAddNote } = useOperator()
const { exportCheck } = useDSL() const { exportCheck } = useDSL()
console.log(clipboardElements, 'clipboardElements')
useEffect(() => { useEffect(() => {
if (panelMenu) if (panelMenu)
handleNodeContextmenuCancel() handleNodeContextmenuCancel()

@ -0,0 +1,68 @@
import {
memo,
useEffect,
useRef,
} from 'react'
import { useTranslation } from 'react-i18next'
import { useClickAway } from 'ahooks'
import ShortcutsName from './shortcuts-name'
import { useStore } from './store'
import {
useNodesInteractions,
useSelectionGraphMenu,
useWorkflowStartRun,
} from './hooks'
import { useStore as useAppStore } from '@/app/components/app/store'
const PanelContextmenu = () => {
const { t } = useTranslation()
const ref = useRef(null)
const selectPanelMenu = useStore(s => s.selectPanelMenu)
const selectGraph = useStore(s => s.selectGraph)
const clipboardElements = useStore(s => s.clipboardElements)
const { handleNodesPaste } = useNodesInteractions()
const { handleSelectPanelContextmenuCancel, handleOtherContextmenuCancel } = useSelectionGraphMenu()
const { handleStartWorkflowRun } = useWorkflowStartRun()
const appDetail = useAppStore(state => state.appDetail)
console.log(appDetail?.mode, selectGraph)
useEffect(() => {
if (selectPanelMenu)
handleOtherContextmenuCancel()
}, [selectPanelMenu, handleOtherContextmenuCancel])
useClickAway(() => {
handleSelectPanelContextmenuCancel()
}, ref)
if (!selectPanelMenu)
return null
return (
<div
className='absolute z-[9] w-[200px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg'
style={{
left: selectPanelMenu.left,
top: selectPanelMenu.top,
}}
ref={ref}
>
<div className='p-1'>
<div
className='flex h-8 cursor-pointer items-center justify-between rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover'
onClick={() => {
handleStartWorkflowRun()
handleSelectPanelContextmenuCancel()
}}
>
{t('workflow.common.run')}
<ShortcutsName keys={['alt', 'r']} />
</div>
</div>
</div>
)
}
export default memo(PanelContextmenu)

@ -30,6 +30,7 @@ import type { WorkflowSliceShape } from './workflow-slice'
import { createWorkflowSlice } from './workflow-slice' import { createWorkflowSlice } from './workflow-slice'
import { WorkflowContext } from '@/app/components/workflow/context' import { WorkflowContext } from '@/app/components/workflow/context'
import type { WorkflowSliceShape as WorkflowAppSliceShape } from '@/app/components/workflow-app/store/workflow/workflow-slice' import type { WorkflowSliceShape as WorkflowAppSliceShape } from '@/app/components/workflow-app/store/workflow/workflow-slice'
import { type SelectPanelSliceShape, createSelectPanelSlice } from './select-node-panel'
export type Shape = export type Shape =
ChatVariableSliceShape & ChatVariableSliceShape &
@ -43,7 +44,8 @@ export type Shape =
VersionSliceShape & VersionSliceShape &
WorkflowDraftSliceShape & WorkflowDraftSliceShape &
WorkflowSliceShape & WorkflowSliceShape &
WorkflowAppSliceShape WorkflowAppSliceShape &
SelectPanelSliceShape
type CreateWorkflowStoreParams = { type CreateWorkflowStoreParams = {
injectWorkflowStoreSliceFn?: StateCreator<WorkflowAppSliceShape> injectWorkflowStoreSliceFn?: StateCreator<WorkflowAppSliceShape>
@ -65,6 +67,7 @@ export const createWorkflowStore = (params: CreateWorkflowStoreParams) => {
...createWorkflowDraftSlice(...args), ...createWorkflowDraftSlice(...args),
...createWorkflowSlice(...args), ...createWorkflowSlice(...args),
...(injectWorkflowStoreSliceFn?.(...args) || {} as WorkflowAppSliceShape), ...(injectWorkflowStoreSliceFn?.(...args) || {} as WorkflowAppSliceShape),
...createSelectPanelSlice(...args),
})) }))
} }

@ -0,0 +1,25 @@
import type { Edge, Node } from 'reactflow'
import type { StateCreator } from 'zustand'
export type SelectPanelSliceShape = {
selectGraph: {
nodes: Node[],
edges: Edge[],
},
selectPanelMenu?: {
top: number
left: number
},
setSelectPanelMenu: (panelMenu: SelectPanelSliceShape['selectPanelMenu']) => void,
setSelectGraph: (selectGraph: SelectPanelSliceShape['selectGraph']) => void,
}
export const createSelectPanelSlice: StateCreator<SelectPanelSliceShape> = set => ({
selectPanelMenu: undefined,
setSelectPanelMenu: selectPanelMenu => set(() => ({ selectPanelMenu })),
selectGraph: {
nodes: [],
edges: [],
},
setSelectGraph: selectGraph => set(() => ({ selectGraph })),
})
Loading…
Cancel
Save