mirror of
https://github.com/simstudioai/sim.git
synced 2026-02-01 18:25:01 -05:00
refactor(workflow): extract block deletion protection into shared utility
Extract duplicated block protection logic from workflow.tsx into a reusable filterProtectedBlocks helper in utils/block-protection-utils.ts. This ensures consistent behavior between context menu delete and keyboard delete operations. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import type { BlockState } from '@/stores/workflows/workflow/types'
|
||||
|
||||
/**
|
||||
* Result of filtering protected blocks from a deletion operation
|
||||
*/
|
||||
export interface FilterProtectedBlocksResult {
|
||||
/** Block IDs that can be deleted (not protected) */
|
||||
deletableIds: string[]
|
||||
/** Block IDs that are protected and cannot be deleted */
|
||||
protectedIds: string[]
|
||||
/** Whether all blocks are protected (deletion should be cancelled entirely) */
|
||||
allProtected: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a block is protected from deletion.
|
||||
* A block is protected if it is locked or if its parent container is locked.
|
||||
*
|
||||
* @param blockId - The ID of the block to check
|
||||
* @param blocks - Record of all blocks in the workflow
|
||||
* @returns True if the block is protected from deletion
|
||||
*/
|
||||
export function isBlockProtected(blockId: string, blocks: Record<string, BlockState>): boolean {
|
||||
const block = blocks[blockId]
|
||||
if (!block) return false
|
||||
|
||||
// Block is locked directly
|
||||
if (block.locked) return true
|
||||
|
||||
// Block is inside a locked container
|
||||
const parentId = block.data?.parentId
|
||||
if (parentId && blocks[parentId]?.locked) return true
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out protected blocks from a list of block IDs for deletion.
|
||||
* Protected blocks are those that are locked or inside a locked container.
|
||||
*
|
||||
* @param blockIds - Array of block IDs to filter
|
||||
* @param blocks - Record of all blocks in the workflow
|
||||
* @returns Result containing deletable IDs, protected IDs, and whether all are protected
|
||||
*/
|
||||
export function filterProtectedBlocks(
|
||||
blockIds: string[],
|
||||
blocks: Record<string, BlockState>
|
||||
): FilterProtectedBlocksResult {
|
||||
const protectedIds = blockIds.filter((id) => isBlockProtected(id, blocks))
|
||||
const deletableIds = blockIds.filter((id) => !protectedIds.includes(id))
|
||||
|
||||
return {
|
||||
deletableIds,
|
||||
protectedIds,
|
||||
allProtected: protectedIds.length === blockIds.length && blockIds.length > 0,
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './auto-layout-utils'
|
||||
export * from './block-protection-utils'
|
||||
export * from './block-ring-utils'
|
||||
export * from './node-position-utils'
|
||||
export * from './workflow-canvas-helpers'
|
||||
|
||||
@@ -55,6 +55,7 @@ import {
|
||||
clearDragHighlights,
|
||||
computeClampedPositionUpdates,
|
||||
estimateBlockDimensions,
|
||||
filterProtectedBlocks,
|
||||
getClampedPositionForNode,
|
||||
isInEditableElement,
|
||||
resolveParentChildSelectionConflicts,
|
||||
@@ -1069,14 +1070,11 @@ const WorkflowContent = React.memo(() => {
|
||||
}, [contextMenuBlocks, copyBlocks, executePasteOperation])
|
||||
|
||||
const handleContextDelete = useCallback(() => {
|
||||
let blockIds = contextMenuBlocks.map((b) => b.id)
|
||||
// Filter out locked blocks and blocks inside locked containers
|
||||
const protectedBlockIds = contextMenuBlocks
|
||||
.filter((b) => b.locked || (b.parentId && blocks[b.parentId]?.locked))
|
||||
.map((b) => b.id)
|
||||
if (protectedBlockIds.length > 0) {
|
||||
blockIds = blockIds.filter((id) => !protectedBlockIds.includes(id))
|
||||
if (protectedBlockIds.length === contextMenuBlocks.length) {
|
||||
const blockIds = contextMenuBlocks.map((b) => b.id)
|
||||
const { deletableIds, protectedIds, allProtected } = filterProtectedBlocks(blockIds, blocks)
|
||||
|
||||
if (protectedIds.length > 0) {
|
||||
if (allProtected) {
|
||||
addNotification({
|
||||
level: 'info',
|
||||
message: 'Cannot delete locked blocks or blocks inside locked containers',
|
||||
@@ -1086,12 +1084,12 @@ const WorkflowContent = React.memo(() => {
|
||||
}
|
||||
addNotification({
|
||||
level: 'info',
|
||||
message: `Skipped ${protectedBlockIds.length} protected block(s)`,
|
||||
message: `Skipped ${protectedIds.length} protected block(s)`,
|
||||
workflowId: activeWorkflowId || undefined,
|
||||
})
|
||||
}
|
||||
if (blockIds.length > 0) {
|
||||
collaborativeBatchRemoveBlocks(blockIds)
|
||||
if (deletableIds.length > 0) {
|
||||
collaborativeBatchRemoveBlocks(deletableIds)
|
||||
}
|
||||
}, [contextMenuBlocks, collaborativeBatchRemoveBlocks, addNotification, activeWorkflowId, blocks])
|
||||
|
||||
@@ -3499,16 +3497,14 @@ const WorkflowContent = React.memo(() => {
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
let selectedIds = selectedNodes.map((node) => node.id)
|
||||
// Filter out locked blocks and blocks inside locked containers
|
||||
const protectedIds = selectedIds.filter(
|
||||
(id) =>
|
||||
blocks[id]?.locked ||
|
||||
(blocks[id]?.data?.parentId && blocks[blocks[id]?.data?.parentId]?.locked)
|
||||
const selectedIds = selectedNodes.map((node) => node.id)
|
||||
const { deletableIds, protectedIds, allProtected } = filterProtectedBlocks(
|
||||
selectedIds,
|
||||
blocks
|
||||
)
|
||||
|
||||
if (protectedIds.length > 0) {
|
||||
selectedIds = selectedIds.filter((id) => !protectedIds.includes(id))
|
||||
if (protectedIds.length === selectedNodes.length) {
|
||||
if (allProtected) {
|
||||
addNotification({
|
||||
level: 'info',
|
||||
message: 'Cannot delete locked blocks or blocks inside locked containers',
|
||||
@@ -3522,8 +3518,8 @@ const WorkflowContent = React.memo(() => {
|
||||
workflowId: activeWorkflowId || undefined,
|
||||
})
|
||||
}
|
||||
if (selectedIds.length > 0) {
|
||||
collaborativeBatchRemoveBlocks(selectedIds)
|
||||
if (deletableIds.length > 0) {
|
||||
collaborativeBatchRemoveBlocks(deletableIds)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user