mirror of
https://github.com/simstudioai/sim.git
synced 2026-02-01 02:05:18 -05:00
unlock duplicates of locked blocks
This commit is contained in:
@@ -2980,11 +2980,12 @@ const WorkflowContent = React.memo(() => {
|
||||
// Don't process parent changes if the node hasn't actually changed parent or is being moved within same parent
|
||||
if (potentialParentId === dragStartParentId) return
|
||||
|
||||
// Prevent moving blocks out of locked containers
|
||||
if (dragStartParentId && blocks[dragStartParentId]?.locked) {
|
||||
// Prevent moving locked blocks out of locked containers
|
||||
// Unlocked blocks (e.g., duplicates) can be moved out freely
|
||||
if (dragStartParentId && blocks[dragStartParentId]?.locked && blocks[node.id]?.locked) {
|
||||
addNotification({
|
||||
level: 'info',
|
||||
message: 'Cannot move blocks out of locked containers',
|
||||
message: 'Cannot move locked blocks out of locked containers',
|
||||
workflowId: activeWorkflowId || undefined,
|
||||
})
|
||||
setPotentialParentId(dragStartParentId) // Reset to original parent
|
||||
|
||||
@@ -433,7 +433,7 @@ describe('regenerateBlockIds', () => {
|
||||
expect(duplicatedBlock.data?.parentId).toBe(loopId)
|
||||
})
|
||||
|
||||
it('should preserve locked state when pasting a locked block', () => {
|
||||
it('should unlock pasted block when source is locked', () => {
|
||||
const blockId = 'block-1'
|
||||
|
||||
const blocksToCopy = {
|
||||
@@ -459,11 +459,12 @@ describe('regenerateBlockIds', () => {
|
||||
const newBlocks = Object.values(result.blocks)
|
||||
expect(newBlocks).toHaveLength(1)
|
||||
|
||||
// Pasted blocks are always unlocked so users can edit them
|
||||
const pastedBlock = newBlocks[0]
|
||||
expect(pastedBlock.locked).toBe(true)
|
||||
expect(pastedBlock.locked).toBe(false)
|
||||
})
|
||||
|
||||
it('should preserve unlocked state when pasting an unlocked block', () => {
|
||||
it('should keep pasted block unlocked when source is unlocked', () => {
|
||||
const blockId = 'block-1'
|
||||
|
||||
const blocksToCopy = {
|
||||
@@ -493,20 +494,20 @@ describe('regenerateBlockIds', () => {
|
||||
expect(pastedBlock.locked).toBe(false)
|
||||
})
|
||||
|
||||
it('should preserve mixed locked states when pasting multiple blocks', () => {
|
||||
it('should unlock all pasted blocks regardless of source locked state', () => {
|
||||
const lockedId = 'locked-1'
|
||||
const unlockedId = 'unlocked-1'
|
||||
|
||||
const blocksToCopy = {
|
||||
[lockedId]: createAgentBlock({
|
||||
id: lockedId,
|
||||
name: 'Locked Agent',
|
||||
name: 'Originally Locked Agent',
|
||||
position: { x: 100, y: 50 },
|
||||
locked: true,
|
||||
}),
|
||||
[unlockedId]: createFunctionBlock({
|
||||
id: unlockedId,
|
||||
name: 'Unlocked Function',
|
||||
name: 'Originally Unlocked Function',
|
||||
position: { x: 200, y: 50 },
|
||||
locked: false,
|
||||
}),
|
||||
@@ -526,10 +527,9 @@ describe('regenerateBlockIds', () => {
|
||||
const newBlocks = Object.values(result.blocks)
|
||||
expect(newBlocks).toHaveLength(2)
|
||||
|
||||
const lockedBlock = newBlocks.find((b) => b.name.includes('Locked'))
|
||||
const unlockedBlock = newBlocks.find((b) => b.name.includes('Unlocked'))
|
||||
|
||||
expect(lockedBlock?.locked).toBe(true)
|
||||
expect(unlockedBlock?.locked).toBe(false)
|
||||
// All pasted blocks should be unlocked so users can edit them
|
||||
for (const block of newBlocks) {
|
||||
expect(block.locked).toBe(false)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -482,7 +482,8 @@ export function regenerateBlockIds(
|
||||
position: newPosition,
|
||||
// Temporarily keep data as-is, we'll fix parentId in second pass
|
||||
data: block.data ? { ...block.data } : block.data,
|
||||
// locked state is preserved via spread (same as Figma)
|
||||
// Duplicated blocks are always unlocked so users can edit them
|
||||
locked: false,
|
||||
}
|
||||
|
||||
newBlocks[newId] = newBlock
|
||||
|
||||
@@ -888,7 +888,7 @@ describe('workflow store', () => {
|
||||
})
|
||||
|
||||
describe('duplicateBlock with locked', () => {
|
||||
it('should preserve locked state when duplicating a locked block', () => {
|
||||
it('should unlock duplicate when duplicating a locked block', () => {
|
||||
const { addBlock, setBlockLocked, duplicateBlock } = useWorkflowStore.getState()
|
||||
|
||||
addBlock('original', 'agent', 'Original Agent', { x: 0, y: 0 })
|
||||
@@ -909,12 +909,12 @@ describe('workflow store', () => {
|
||||
if (duplicatedId) {
|
||||
// Original should still be locked
|
||||
expect(blocks.original.locked).toBe(true)
|
||||
// Duplicate should also be locked (preserves state like Figma)
|
||||
expect(blocks[duplicatedId].locked).toBe(true)
|
||||
// Duplicate should be unlocked so users can edit it
|
||||
expect(blocks[duplicatedId].locked).toBe(false)
|
||||
}
|
||||
})
|
||||
|
||||
it('should preserve unlocked state when duplicating an unlocked block', () => {
|
||||
it('should create unlocked duplicate when duplicating an unlocked block', () => {
|
||||
const { addBlock, duplicateBlock } = useWorkflowStore.getState()
|
||||
|
||||
addBlock('original', 'agent', 'Original Agent', { x: 0, y: 0 })
|
||||
|
||||
@@ -709,7 +709,7 @@ export const useWorkflowStore = create<WorkflowStore>()(
|
||||
name: newName,
|
||||
position: offsetPosition,
|
||||
subBlocks: newSubBlocks,
|
||||
// locked state is preserved via spread (same as Figma)
|
||||
locked: false,
|
||||
},
|
||||
},
|
||||
edges: [...get().edges],
|
||||
|
||||
Reference in New Issue
Block a user