feat(workflow): enhance group node availability checks

- Updated `checkMakeGroupAvailability` to include a check for existing group nodes, preventing group creation if a group node is already selected.
- Modified `useMakeGroupAvailability` and `useNodesInteractions` hooks to incorporate the new group node check, ensuring accurate group creation logic.
- Adjusted UI rendering logic in the workflow panel to conditionally display elements based on node type, specifically for group nodes.
This commit is contained in:
zhsama 2026-01-06 02:07:13 +08:00
parent 9012dced6a
commit d92c476388
3 changed files with 21 additions and 17 deletions

View File

@ -2,6 +2,7 @@ import type { PredecessorHandle } from '../utils'
import { useMemo } from 'react'
import { useStore as useReactFlowStore } from 'reactflow'
import { shallow } from 'zustand/shallow'
import { BlockEnum } from '../types'
import { getCommonPredecessorHandles } from '../utils'
export type MakeGroupAvailability = {
@ -24,9 +25,9 @@ type MinimalEdge = {
export const checkMakeGroupAvailability = (
selectedNodeIds: string[],
edges: MinimalEdge[],
hasGroupNode = false,
): MakeGroupAvailability => {
// Make group requires selecting at least 2 nodes.
if (selectedNodeIds.length <= 1) {
if (selectedNodeIds.length <= 1 || hasGroupNode) {
return {
canMakeGroup: false,
branchEntryNodeIds: [],
@ -109,8 +110,6 @@ export const checkMakeGroupAvailability = (
}
export const useMakeGroupAvailability = (selectedNodeIds: string[]): MakeGroupAvailability => {
// Subscribe to the minimal edge state we need (source/sourceHandle/target) to avoid
// snowball rerenders caused by subscribing to the entire `edges` objects.
const edgeKeys = useReactFlowStore((state) => {
const delimiter = '\u0000'
const keys = state.edges.map(edge => `${edge.source}${delimiter}${edge.sourceHandle || 'source'}${delimiter}${edge.target}`)
@ -118,8 +117,11 @@ export const useMakeGroupAvailability = (selectedNodeIds: string[]): MakeGroupAv
return keys
}, shallow)
const hasGroupNode = useReactFlowStore((state) => {
return state.getNodes().some(node => node.selected && node.data.type === BlockEnum.Group)
})
return useMemo(() => {
// Reconstruct a minimal edge list from `edgeKeys` for downstream graph checks.
const delimiter = '\u0000'
const edges = edgeKeys.map((key) => {
const [source, handleId, target] = key.split(delimiter)
@ -131,6 +133,6 @@ export const useMakeGroupAvailability = (selectedNodeIds: string[]): MakeGroupAv
}
})
return checkMakeGroupAvailability(selectedNodeIds, edges)
}, [edgeKeys, selectedNodeIds])
return checkMakeGroupAvailability(selectedNodeIds, edges, hasGroupNode)
}, [edgeKeys, selectedNodeIds, hasGroupNode])
}

View File

@ -2550,23 +2550,24 @@ export const useNodesInteractions = () => {
return nodes.some(node => node.data._isBundled)
}, [store])
// Check if the current box selection can be grouped
const getCanMakeGroup = useCallback(() => {
const { getNodes, edges } = store.getState()
const nodes = getNodes()
const bundledNodeIds = nodes.filter(node => node.data._isBundled).map(node => node.id)
const bundledNodes = nodes.filter(node => node.data._isBundled)
if (bundledNodeIds.length <= 1)
if (bundledNodes.length <= 1)
return false
const bundledNodeIds = bundledNodes.map(node => node.id)
const minimalEdges = edges.map(edge => ({
id: edge.id,
source: edge.source,
sourceHandle: edge.sourceHandle || 'source',
target: edge.target,
}))
const hasGroupNode = bundledNodes.some(node => node.data.type === BlockEnum.Group)
const { canMakeGroup } = checkMakeGroupAvailability(bundledNodeIds, minimalEdges)
const { canMakeGroup } = checkMakeGroupAvailability(bundledNodeIds, minimalEdges, hasGroupNode)
return canMakeGroup
}, [store])
@ -2574,19 +2575,20 @@ export const useNodesInteractions = () => {
const { getNodes, setNodes, edges, setEdges } = store.getState()
const nodes = getNodes()
const bundledNodes = nodes.filter(node => node.data._isBundled)
const bundledNodeIds = bundledNodes.map(node => node.id)
if (bundledNodeIds.length <= 1)
if (bundledNodes.length <= 1)
return
const bundledNodeIds = bundledNodes.map(node => node.id)
const minimalEdges = edges.map(edge => ({
id: edge.id,
source: edge.source,
sourceHandle: edge.sourceHandle || 'source',
target: edge.target,
}))
const hasGroupNode = bundledNodes.some(node => node.data.type === BlockEnum.Group)
const { canMakeGroup } = checkMakeGroupAvailability(bundledNodeIds, minimalEdges)
const { canMakeGroup } = checkMakeGroupAvailability(bundledNodeIds, minimalEdges, hasGroupNode)
if (!canMakeGroup)
return

View File

@ -594,7 +594,7 @@ const BasePanel: FC<BasePanelProps> = ({
)
}
{
!needsToolAuth && !currentDataSource && !currentTriggerPlugin && (
!needsToolAuth && !currentDataSource && !currentTriggerPlugin && data.type !== BlockEnum.Group && (
<div className="flex items-center justify-between pl-4 pr-3">
<Tab
value={tabType}
@ -603,9 +603,9 @@ const BasePanel: FC<BasePanelProps> = ({
</div>
)
}
<Split />
{data.type !== BlockEnum.Group && <Split />}
</div>
{tabType === TabType.settings && (
{(tabType === TabType.settings || data.type === BlockEnum.Group) && (
<div className="flex flex-1 flex-col overflow-y-auto">
<div>
{cloneElement(children as any, {