feat: refactor workflow branch selection logic and introduce context provider
This commit is contained in:
@@ -20,25 +20,16 @@ import {
|
||||
syncConditionBranchTargetsFromEdges,
|
||||
} from "./workflow-utils"
|
||||
|
||||
export type SelectedWorkflowBranch = {
|
||||
nodeId: string
|
||||
branchId: string
|
||||
}
|
||||
|
||||
export function useFlowgramEditorProps({
|
||||
definition,
|
||||
nodeSpecs,
|
||||
selectedBranch,
|
||||
readonly = false,
|
||||
onDefinitionChange,
|
||||
onSelectBranch,
|
||||
}: {
|
||||
definition: AIWorkflowDefinition
|
||||
nodeSpecs: AIWorkflowNodeSpec[]
|
||||
selectedBranch?: SelectedWorkflowBranch | null
|
||||
readonly?: boolean
|
||||
onDefinitionChange?: (definition: AIWorkflowDefinition) => void
|
||||
onSelectBranch?: (branch: SelectedWorkflowBranch | null) => void
|
||||
}) {
|
||||
return useMemo<FreeLayoutProps>(
|
||||
() => {
|
||||
@@ -59,7 +50,7 @@ export function useFlowgramEditorProps({
|
||||
materials: {
|
||||
renderDefaultNode: FlowgramNodeRenderer,
|
||||
},
|
||||
nodeRegistries: buildFlowgramNodeRegistries(nodeSpecs, selectedBranch, onSelectBranch),
|
||||
nodeRegistries: buildFlowgramNodeRegistries(nodeSpecs),
|
||||
nodeEngine: {
|
||||
enable: true,
|
||||
},
|
||||
@@ -123,7 +114,7 @@ export function useFlowgramEditorProps({
|
||||
],
|
||||
}
|
||||
},
|
||||
[definition, nodeSpecs, onDefinitionChange, onSelectBranch, readonly, selectedBranch]
|
||||
[definition, nodeSpecs, onDefinitionChange, readonly]
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,13 +8,9 @@ import {
|
||||
normalizeNodeConfig,
|
||||
type WorkflowConditionBranch,
|
||||
} from "./workflow-utils"
|
||||
import type { SelectedWorkflowBranch } from "./flowgram-editor-provider"
|
||||
import { useWorkflowBranchSelection } from "./workflow-branch-selection"
|
||||
|
||||
export function buildFlowgramNodeRegistries(
|
||||
nodeSpecs: AIWorkflowNodeSpec[],
|
||||
selectedBranch?: SelectedWorkflowBranch | null,
|
||||
onSelectBranch?: (branch: SelectedWorkflowBranch | null) => void
|
||||
): WorkflowNodeRegistry[] {
|
||||
export function buildFlowgramNodeRegistries(nodeSpecs: AIWorkflowNodeSpec[]): WorkflowNodeRegistry[] {
|
||||
const seen = new Set<string>()
|
||||
const specs = nodeSpecs.length > 0
|
||||
? nodeSpecs
|
||||
@@ -45,8 +41,6 @@ export function buildFlowgramNodeRegistries(
|
||||
<FlowgramNodeForm
|
||||
nodeType={spec.type}
|
||||
fallbackTitle={spec.title || spec.type}
|
||||
selectedBranch={selectedBranch}
|
||||
onSelectBranch={onSelectBranch}
|
||||
/>
|
||||
),
|
||||
},
|
||||
@@ -56,13 +50,9 @@ export function buildFlowgramNodeRegistries(
|
||||
function FlowgramNodeForm({
|
||||
nodeType,
|
||||
fallbackTitle,
|
||||
selectedBranch,
|
||||
onSelectBranch,
|
||||
}: {
|
||||
nodeType: string
|
||||
fallbackTitle: string
|
||||
selectedBranch?: SelectedWorkflowBranch | null
|
||||
onSelectBranch?: (branch: SelectedWorkflowBranch | null) => void
|
||||
}) {
|
||||
const { node } = useNodeRender()
|
||||
const nodeId = String(node.id ?? "")
|
||||
@@ -72,8 +62,6 @@ function FlowgramNodeForm({
|
||||
<ConditionNodeForm
|
||||
fallbackTitle={fallbackTitle}
|
||||
nodeId={nodeId}
|
||||
selectedBranch={selectedBranch}
|
||||
onSelectBranch={onSelectBranch}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -108,14 +96,12 @@ function defaultPortsForNodeType(type: string) {
|
||||
function ConditionNodeForm({
|
||||
fallbackTitle,
|
||||
nodeId,
|
||||
selectedBranch,
|
||||
onSelectBranch,
|
||||
}: {
|
||||
fallbackTitle: string
|
||||
nodeId: string
|
||||
selectedBranch?: SelectedWorkflowBranch | null
|
||||
onSelectBranch?: (branch: SelectedWorkflowBranch | null) => void
|
||||
}) {
|
||||
const { selectedBranch, onSelectBranch } = useWorkflowBranchSelection()
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col">
|
||||
<Field<string> name="title">
|
||||
@@ -154,10 +140,16 @@ function ConditionNodeForm({
|
||||
? "bg-[#dfe4ff] text-[#3327b9] ring-1 ring-inset ring-[#4e40e5]/45 before:w-1"
|
||||
: "bg-[#eef1f7] hover:bg-[#e6eaff]",
|
||||
].join(" ")}
|
||||
onClick={(event) => {
|
||||
onPointerDownCapture={(event) => {
|
||||
event.stopPropagation()
|
||||
onSelectBranch?.({ nodeId, branchId: branch.id })
|
||||
}}
|
||||
onMouseDownCapture={(event) => {
|
||||
event.stopPropagation()
|
||||
}}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
}}
|
||||
>
|
||||
<span className="min-w-0 flex-1 truncate font-medium text-foreground/90">
|
||||
{branch.name || (branch.default ? "默认分支" : branch.id)}
|
||||
@@ -170,6 +162,12 @@ function ConditionNodeForm({
|
||||
type="button"
|
||||
className="flex size-5 shrink-0 items-center justify-center rounded-sm text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive"
|
||||
aria-label={`删除条件 ${branch.name || branch.id}`}
|
||||
onPointerDownCapture={(event) => {
|
||||
event.stopPropagation()
|
||||
}}
|
||||
onMouseDownCapture={(event) => {
|
||||
event.stopPropagation()
|
||||
}}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
deleteBranch(branch.id)
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
"use client"
|
||||
|
||||
import { createContext, useContext, useMemo, type ReactNode } from "react"
|
||||
|
||||
export type SelectedWorkflowBranch = {
|
||||
nodeId: string
|
||||
branchId: string
|
||||
}
|
||||
|
||||
type WorkflowBranchSelectionContextValue = {
|
||||
selectedBranch: SelectedWorkflowBranch | null
|
||||
onSelectBranch: (branch: SelectedWorkflowBranch | null) => void
|
||||
}
|
||||
|
||||
const WorkflowBranchSelectionContext = createContext<WorkflowBranchSelectionContextValue>({
|
||||
selectedBranch: null,
|
||||
onSelectBranch: () => {},
|
||||
})
|
||||
|
||||
export function WorkflowBranchSelectionProvider({
|
||||
selectedBranch,
|
||||
onSelectBranch,
|
||||
children,
|
||||
}: WorkflowBranchSelectionContextValue & {
|
||||
children: ReactNode
|
||||
}) {
|
||||
const value = useMemo(
|
||||
() => ({ selectedBranch, onSelectBranch }),
|
||||
[onSelectBranch, selectedBranch]
|
||||
)
|
||||
|
||||
return (
|
||||
<WorkflowBranchSelectionContext.Provider value={value}>
|
||||
{children}
|
||||
</WorkflowBranchSelectionContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useWorkflowBranchSelection() {
|
||||
return useContext(WorkflowBranchSelectionContext)
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { XIcon } from "lucide-react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import type { AIWorkflowDefinition, AIWorkflowNodeSpec } from "@/lib/api/admin"
|
||||
|
||||
import type { SelectedWorkflowBranch } from "./flowgram-editor-provider"
|
||||
import type { SelectedWorkflowBranch } from "./workflow-branch-selection"
|
||||
import { ConditionBranchConfigPanel, NodeConfigPanel } from "./node-config-panel"
|
||||
import {
|
||||
getAvailableVariables,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { useEffect, useMemo, useState, type ReactNode } from "react"
|
||||
import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react"
|
||||
|
||||
import {
|
||||
EditorRenderer,
|
||||
@@ -16,7 +16,11 @@ import {
|
||||
|
||||
import type { AIWorkflowDefinition, AIWorkflowNodeSpec } from "@/lib/api/admin"
|
||||
|
||||
import { useFlowgramEditorProps, type SelectedWorkflowBranch } from "./flowgram-editor-provider"
|
||||
import { useFlowgramEditorProps } from "./flowgram-editor-provider"
|
||||
import {
|
||||
WorkflowBranchSelectionProvider,
|
||||
type SelectedWorkflowBranch,
|
||||
} from "./workflow-branch-selection"
|
||||
import { WorkflowConfigPanel } from "./workflow-config-sidebar"
|
||||
import { WorkflowEditorToolbar } from "./workflow-editor-toolbar"
|
||||
import { WorkflowNodePalette } from "./workflow-node-palette"
|
||||
@@ -66,6 +70,7 @@ export function WorkflowEditor({
|
||||
const [localDefinition, setLocalDefinition] = useState(definition)
|
||||
const [selectedNodeId, setSelectedNodeId] = useState("")
|
||||
const [selectedBranch, setSelectedBranch] = useState<SelectedWorkflowBranch | null>(null)
|
||||
const branchSelectAtRef = useRef(0)
|
||||
|
||||
const validation = useMemo(
|
||||
() => validateWorkflowDefinition(localDefinition, nodeSpecs),
|
||||
@@ -75,53 +80,64 @@ export function WorkflowEditor({
|
||||
const editorProps = useFlowgramEditorProps({
|
||||
definition: localDefinition,
|
||||
nodeSpecs,
|
||||
selectedBranch,
|
||||
onDefinitionChange: (next) => {
|
||||
setLocalDefinition(next)
|
||||
onDefinitionChange(next)
|
||||
},
|
||||
onSelectBranch: (branch) => {
|
||||
})
|
||||
|
||||
const handleSelectBranch = useCallback(
|
||||
(branch: SelectedWorkflowBranch | null) => {
|
||||
if (!branch) {
|
||||
setSelectedBranch(null)
|
||||
return
|
||||
}
|
||||
branchSelectAtRef.current = Date.now()
|
||||
setSelectedNodeId(branch.nodeId)
|
||||
setSelectedBranch(branch)
|
||||
},
|
||||
})
|
||||
[]
|
||||
)
|
||||
|
||||
return (
|
||||
<FreeLayoutEditorProvider {...editorProps}>
|
||||
<WorkflowEditorInner
|
||||
definition={localDefinition}
|
||||
nodeSpecs={nodeSpecs}
|
||||
selectedNodeId={selectedNodeId}
|
||||
selectedBranch={selectedBranch}
|
||||
validation={validation}
|
||||
toolbarExtra={toolbarExtra}
|
||||
onDefinitionChange={(next) => {
|
||||
setLocalDefinition(next)
|
||||
onDefinitionChange(next)
|
||||
}}
|
||||
onSelectNode={(nodeId) => {
|
||||
setSelectedNodeId(nodeId)
|
||||
setSelectedBranch(null)
|
||||
}}
|
||||
onSelectBranch={setSelectedBranch}
|
||||
onUndo={onUndo}
|
||||
undoDisabled={undoDisabled}
|
||||
onRedo={onRedo}
|
||||
redoDisabled={redoDisabled}
|
||||
onRestoreDefault={onRestoreDefault}
|
||||
restoreDefaultDisabled={restoreDefaultDisabled}
|
||||
onValidate={onValidate}
|
||||
validateDisabled={validateDisabled}
|
||||
onSaveDraft={onSaveDraft}
|
||||
saveDraftDisabled={saveDraftDisabled}
|
||||
onPublish={onPublish}
|
||||
publishDisabled={publishDisabled}
|
||||
/>
|
||||
</FreeLayoutEditorProvider>
|
||||
<WorkflowBranchSelectionProvider
|
||||
selectedBranch={selectedBranch}
|
||||
onSelectBranch={handleSelectBranch}
|
||||
>
|
||||
<FreeLayoutEditorProvider {...editorProps}>
|
||||
<WorkflowEditorInner
|
||||
definition={localDefinition}
|
||||
nodeSpecs={nodeSpecs}
|
||||
selectedNodeId={selectedNodeId}
|
||||
selectedBranch={selectedBranch}
|
||||
validation={validation}
|
||||
toolbarExtra={toolbarExtra}
|
||||
onDefinitionChange={(next) => {
|
||||
setLocalDefinition(next)
|
||||
onDefinitionChange(next)
|
||||
}}
|
||||
onSelectNode={(nodeId) => {
|
||||
setSelectedNodeId(nodeId)
|
||||
if (!nodeId || Date.now() - branchSelectAtRef.current > 160) {
|
||||
setSelectedBranch(null)
|
||||
}
|
||||
}}
|
||||
onSelectBranch={handleSelectBranch}
|
||||
onUndo={onUndo}
|
||||
undoDisabled={undoDisabled}
|
||||
onRedo={onRedo}
|
||||
redoDisabled={redoDisabled}
|
||||
onRestoreDefault={onRestoreDefault}
|
||||
restoreDefaultDisabled={restoreDefaultDisabled}
|
||||
onValidate={onValidate}
|
||||
validateDisabled={validateDisabled}
|
||||
onSaveDraft={onSaveDraft}
|
||||
saveDraftDisabled={saveDraftDisabled}
|
||||
onPublish={onPublish}
|
||||
publishDisabled={publishDisabled}
|
||||
/>
|
||||
</FreeLayoutEditorProvider>
|
||||
</WorkflowBranchSelectionProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user