Files
dify/web/app/components/workflow/utils/node-navigation.ts
twwu eed3ebdd0f Merge branch 'main' into feat/dify-builder
# Conflicts:
#	api/services/app_generate_service.py
#	api/services/workflow_service.py
#	web/app/components/app/create-app-dialog-shell.tsx
#	web/app/components/app/create-app-dialog/__tests__/index.spec.tsx
#	web/app/components/app/create-app-dialog/app-list/__tests__/sidebar.spec.tsx
#	web/app/components/app/create-app-modal/__tests__/index.spec.tsx
#	web/app/components/app/create-app-modal/index.tsx
#	web/app/components/apps/__tests__/list.spec.tsx
#	web/app/components/header/account-setting/model-provider-page/__tests__/hooks.spec.ts
#	web/app/components/workflow-app/components/__tests__/workflow-main.spec.tsx
#	web/app/components/workflow-app/components/__tests__/workflow-panel.spec.tsx
#	web/app/components/workflow-app/hooks/__tests__/use-DSL.spec.ts
#	web/app/components/workflow-app/hooks/__tests__/use-nodes-sync-draft.spec.ts
#	web/app/components/workflow-app/hooks/__tests__/use-workflow-init.spec.ts
#	web/app/components/workflow-app/hooks/__tests__/use-workflow-refresh-draft.spec.ts
#	web/app/components/workflow-app/hooks/__tests__/use-workflow-run.spec.ts
#	web/app/components/workflow-app/hooks/use-DSL.ts
#	web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts
#	web/app/components/workflow-app/hooks/use-workflow-init.ts
#	web/app/components/workflow-app/hooks/use-workflow-refresh-draft.ts
#	web/app/components/workflow/__tests__/workflow-edge-events.spec.tsx
#	web/app/components/workflow/collaboration/core/__tests__/crdt-provider.test.ts
#	web/app/components/workflow/collaboration/hooks/__tests__/use-collaboration.spec.ts
#	web/app/components/workflow/header/__tests__/header-layouts.spec.tsx
#	web/app/components/workflow/header/__tests__/run-and-history.spec.tsx
#	web/app/components/workflow/header/__tests__/scroll-to-selected-node-button.spec.tsx
#	web/app/components/workflow/header/checklist/__tests__/index.spec.tsx
#	web/app/components/workflow/hooks/__tests__/use-workflow-panel-interactions.spec.tsx
#	web/app/components/workflow/hooks/__tests__/use-workflow-search.spec.tsx
#	web/app/components/workflow/hooks/__tests__/use-workflow-update.spec.tsx
#	web/app/components/workflow/hooks/__tests__/use-workflow.spec.ts
#	web/app/components/workflow/nodes/__tests__/index.spec.tsx
#	web/app/components/workflow/nodes/question-classifier/__tests__/integration.spec.tsx
#	web/app/components/workflow/operator/__tests__/control.spec.tsx
#	web/app/components/workflow/operator/__tests__/zoom-in-out.spec.tsx
#	web/app/components/workflow/panel/__tests__/workflow-preview.spec.tsx
#	web/app/components/workflow/run/__tests__/result-panel.spec.tsx
#	web/app/components/workflow/store/__tests__/workflow-draft-slice.spec.ts
#	web/app/components/workflow/utils/__tests__/node-navigation.spec.ts
#	web/app/components/workflow/utils/node-navigation.ts
#	web/i18n/locales/ar-TN/app.json
#	web/i18n/locales/az-AZ/app.json
#	web/i18n/locales/de-DE/app.json
#	web/i18n/locales/en-US/app.json
#	web/i18n/locales/es-ES/app.json
#	web/i18n/locales/fa-IR/app.json
#	web/i18n/locales/fr-FR/app.json
#	web/i18n/locales/hi-IN/app.json
#	web/i18n/locales/id-ID/app.json
#	web/i18n/locales/it-IT/app.json
#	web/i18n/locales/ja-JP/app.json
#	web/i18n/locales/ko-KR/app.json
#	web/i18n/locales/lo-LA/app.json
#	web/i18n/locales/nl-NL/app.json
#	web/i18n/locales/pl-PL/app.json
#	web/i18n/locales/pt-BR/app.json
#	web/i18n/locales/ro-RO/app.json
#	web/i18n/locales/ru-RU/app.json
#	web/i18n/locales/sl-SI/app.json
#	web/i18n/locales/th-TH/app.json
#	web/i18n/locales/tr-TR/app.json
#	web/i18n/locales/uk-UA/app.json
#	web/i18n/locales/vi-VN/app.json
#	web/i18n/locales/zh-Hans/app.json
#	web/i18n/locales/zh-Hant/app.json
#	web/i18n/resources.generated.d.ts
#	web/service/console/client.spec.ts
2026-10-19 11:39:20 +08:00

120 lines
3.5 KiB
TypeScript

import type { Node, ReactFlowInstance } from 'reactflow'
/**
* Node navigation utilities for workflow
* This module provides functions for node selection, focusing and scrolling in workflow
*/
/**
* Interface for node selection event detail
*/
type NodeSelectionDetail = {
nodeId: string
focus?: boolean
shouldFocus?: () => boolean
}
/**
* Select a node in the workflow
* @param nodeId - The ID of the node to select
* @param focus - Whether to focus/scroll to the node
*/
export function selectWorkflowNode(
nodeId: string,
focus = false,
shouldFocus?: () => boolean,
): void {
// Create and dispatch a custom event for node selection
const event = new CustomEvent('workflow:select-node', {
detail: {
nodeId,
focus,
...(shouldFocus && { shouldFocus }),
},
})
document.dispatchEvent(event)
}
/**
* Scroll to a specific node in the workflow
* @param nodeId - The ID of the node to scroll to
*/
export function scrollToWorkflowNode(nodeId: string): void {
// Create and dispatch a custom event for scrolling to node
const event = new CustomEvent('workflow:scroll-to-node', {
detail: { nodeId },
})
document.dispatchEvent(event)
}
/**
* Setup node selection event listener
* @param handleNodeSelect - Function to handle node selection
* @returns Cleanup function
*/
export function setupNodeSelectionListener(handleNodeSelect: (nodeId: string) => void): () => void {
// Event handler for node selection
const handleNodeSelection = (event: CustomEvent<NodeSelectionDetail>) => {
const { nodeId, focus, shouldFocus } = event.detail
if (nodeId) {
// Select the node
handleNodeSelect(nodeId)
// If focus is requested, scroll to the node
if (focus) {
// Use a small timeout to ensure node selection happens first
setTimeout(() => {
if (!shouldFocus || shouldFocus()) scrollToWorkflowNode(nodeId)
}, 100)
}
}
}
// Add event listener
document.addEventListener('workflow:select-node', handleNodeSelection as EventListener)
// Return cleanup function
return () => {
document.removeEventListener('workflow:select-node', handleNodeSelection as EventListener)
}
}
/**
* Setup scroll to node event listener with ReactFlow
* @param nodes - The workflow nodes
* @param reactflow - The ReactFlow instance
* @returns Cleanup function
*/
export function setupScrollToNodeListener(
nodes: Pick<Node, 'id' | 'position'>[],
reactflow: Pick<ReactFlowInstance, 'setCenter'>,
): () => void {
// Event handler for scrolling to node
const handleScrollToNode = (event: CustomEvent<NodeSelectionDetail>) => {
const { nodeId } = event.detail
if (nodeId) {
// Find the target node
const node = nodes.find((n) => n.id === nodeId)
if (node) {
// Use ReactFlow's fitView API to scroll to the node
const nodePosition = { x: node.position.x, y: node.position.y }
// Calculate position to place node in top-left area
// Move the center point right and down to show node in top-left
const targetX = nodePosition.x + window.innerWidth * 0.25
const targetY = nodePosition.y + window.innerHeight * 0.25
reactflow.setCenter(targetX, targetY, { zoom: 1, duration: 800 })
}
}
}
// Add event listener
document.addEventListener('workflow:scroll-to-node', handleScrollToNode as EventListener)
// Return cleanup function
return () => {
document.removeEventListener('workflow:scroll-to-node', handleScrollToNode as EventListener)
}
}