Fix binding bugs
This commit is contained in:
parent
010a09ff19
commit
13a40d1b9c
6 changed files with 103 additions and 86 deletions
|
@ -112,4 +112,6 @@ describe('Delete command', () => {
|
||||||
expect(tlstate.getShape('newGroup')).toBeUndefined()
|
expect(tlstate.getShape('newGroup')).toBeUndefined()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it.todo('Does not delete uneffected bindings.')
|
||||||
})
|
})
|
||||||
|
|
|
@ -158,4 +158,6 @@ describe('Duplicate command', () => {
|
||||||
expect(Object.keys(tlstate.page.shapes).length).toBe(beforeShapeIds.length + 1)
|
expect(Object.keys(tlstate.page.shapes).length).toBe(beforeShapeIds.length + 1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it.todo('Does not delete uneffected bindings.')
|
||||||
})
|
})
|
||||||
|
|
|
@ -83,46 +83,48 @@ export function duplicate(data: Data, ids: string[]): TLDrawCommand {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Which ids did we end up duplicating?
|
// Which ids did we end up duplicating?
|
||||||
const duplicatedShapeIds = Object.keys(duplicateMap)
|
const dupedShapeIds = new Set(Object.keys(duplicateMap))
|
||||||
|
|
||||||
// Handle bindings that effect duplicated shapes
|
// Handle bindings that effect duplicated shapes
|
||||||
Object.values(page.bindings).forEach((binding) => {
|
Object.values(page.bindings)
|
||||||
if (duplicatedShapeIds.includes(binding.fromId)) {
|
.filter((binding) => dupedShapeIds.has(binding.fromId) || dupedShapeIds.has(binding.toId))
|
||||||
if (duplicatedShapeIds.includes(binding.toId)) {
|
.forEach((binding) => {
|
||||||
// If the binding is between two duplicating shapes then
|
if (dupedShapeIds.has(binding.fromId)) {
|
||||||
// duplicate the binding, too
|
if (dupedShapeIds.has(binding.toId)) {
|
||||||
const duplicatedBindingId = Utils.uniqueId()
|
// If the binding is between two duplicating shapes then
|
||||||
|
// duplicate the binding, too
|
||||||
|
const duplicatedBindingId = Utils.uniqueId()
|
||||||
|
|
||||||
const duplicatedBinding = {
|
const duplicatedBinding = {
|
||||||
...Utils.deepClone(binding),
|
...Utils.deepClone(binding),
|
||||||
id: duplicatedBindingId,
|
id: duplicatedBindingId,
|
||||||
fromId: duplicateMap[binding.fromId],
|
fromId: duplicateMap[binding.fromId],
|
||||||
toId: duplicateMap[binding.toId],
|
toId: duplicateMap[binding.toId],
|
||||||
|
}
|
||||||
|
|
||||||
|
before.bindings[duplicatedBindingId] = undefined
|
||||||
|
after.bindings[duplicatedBindingId] = duplicatedBinding
|
||||||
|
|
||||||
|
// Change the duplicated shape's handle so that it reference
|
||||||
|
// the duplicated binding
|
||||||
|
const boundShape = after.shapes[duplicatedBinding.fromId]
|
||||||
|
Object.values(boundShape!.handles!).forEach((handle) => {
|
||||||
|
if (handle!.bindingId === binding.id) {
|
||||||
|
handle!.bindingId = duplicatedBindingId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// If only the fromId is selected, delete the binding on
|
||||||
|
// the duplicated shape's handles
|
||||||
|
const boundShape = after.shapes[duplicateMap[binding.fromId]]
|
||||||
|
Object.values(boundShape!.handles!).forEach((handle) => {
|
||||||
|
if (handle!.bindingId === binding.id) {
|
||||||
|
handle!.bindingId = undefined
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
before.bindings[duplicatedBindingId] = undefined
|
|
||||||
after.bindings[duplicatedBindingId] = duplicatedBinding
|
|
||||||
|
|
||||||
// Change the duplicated shape's handle so that it reference
|
|
||||||
// the duplicated binding
|
|
||||||
const boundShape = after.shapes[duplicatedBinding.fromId]
|
|
||||||
Object.values(boundShape!.handles!).forEach((handle) => {
|
|
||||||
if (handle!.bindingId === binding.id) {
|
|
||||||
handle!.bindingId = duplicatedBindingId
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// If only the fromId is selected, delete the binding on
|
|
||||||
// the duplicated shape's handles
|
|
||||||
const boundShape = after.shapes[duplicateMap[binding.fromId]]
|
|
||||||
Object.values(boundShape!.handles!).forEach((handle) => {
|
|
||||||
if (handle!.bindingId === binding.id) {
|
|
||||||
handle!.bindingId = undefined
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: 'duplicate',
|
id: 'duplicate',
|
||||||
|
@ -142,7 +144,9 @@ export function duplicate(data: Data, ids: string[]): TLDrawCommand {
|
||||||
[currentPageId]: after,
|
[currentPageId]: after,
|
||||||
},
|
},
|
||||||
pageStates: {
|
pageStates: {
|
||||||
[currentPageId]: { selectedIds: duplicatedShapeIds.map((id) => duplicateMap[id]) },
|
[currentPageId]: {
|
||||||
|
selectedIds: Array.from(dupedShapeIds.values()).map((id) => duplicateMap[id]),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -247,4 +247,6 @@ describe('Move to page command', () => {
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it.todo('Does not delete uneffected bindings.')
|
||||||
})
|
})
|
||||||
|
|
|
@ -52,44 +52,49 @@ export function removeShapesFromPage(data: Data, ids: string[], pageId: string)
|
||||||
const page = TLDR.getPage(data, pageId)
|
const page = TLDR.getPage(data, pageId)
|
||||||
|
|
||||||
// We also need to delete bindings that reference the deleted shapes
|
// We also need to delete bindings that reference the deleted shapes
|
||||||
Object.values(page.bindings).forEach((binding) => {
|
Object.values(page.bindings)
|
||||||
for (const id of [binding.toId, binding.fromId]) {
|
.filter((binding) => deletedIds.has(binding.fromId) || deletedIds.has(binding.toId))
|
||||||
// If the binding references a deleted shape...
|
.forEach((binding) => {
|
||||||
if (after.shapes[id] === undefined) {
|
for (const id of [binding.toId, binding.fromId]) {
|
||||||
// Delete this binding
|
// If the binding references a deleted shape...
|
||||||
before.bindings[binding.id] = binding
|
if (after.shapes[id] === undefined) {
|
||||||
after.bindings[binding.id] = undefined
|
// Delete this binding
|
||||||
|
before.bindings[binding.id] = binding
|
||||||
|
after.bindings[binding.id] = undefined
|
||||||
|
|
||||||
// Let's also look each the bound shape...
|
// Let's also look each the bound shape...
|
||||||
const shape = TLDR.getShape(data, id, pageId)
|
const shape = TLDR.getShape(data, id, pageId)
|
||||||
|
|
||||||
// If the bound shape has a handle that references the deleted binding...
|
// If the bound shape has a handle that references the deleted binding...
|
||||||
if (shape.handles) {
|
if (shape.handles) {
|
||||||
Object.values(shape.handles)
|
Object.values(shape.handles)
|
||||||
.filter((handle) => handle.bindingId === binding.id)
|
.filter((handle) => handle.bindingId === binding.id)
|
||||||
.forEach((handle) => {
|
.forEach((handle) => {
|
||||||
// Save the binding reference in the before patch
|
// Save the binding reference in the before patch
|
||||||
before.shapes[id] = {
|
before.shapes[id] = {
|
||||||
...before.shapes[id],
|
...before.shapes[id],
|
||||||
handles: {
|
handles: {
|
||||||
...before.shapes[id]?.handles,
|
...before.shapes[id]?.handles,
|
||||||
[handle.id]: { bindingId: binding.id },
|
[handle.id]: { bindingId: binding.id },
|
||||||
},
|
},
|
||||||
}
|
|
||||||
|
|
||||||
// Unless we're currently deleting the shape, remove the
|
|
||||||
// binding reference from the after patch
|
|
||||||
if (!deletedIds.has(id)) {
|
|
||||||
after.shapes[id] = {
|
|
||||||
...after.shapes[id],
|
|
||||||
handles: { ...after.shapes[id]?.handles, [handle.id]: { bindingId: undefined } },
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
// Unless we're currently deleting the shape, remove the
|
||||||
|
// binding reference from the after patch
|
||||||
|
if (!deletedIds.has(id)) {
|
||||||
|
after.shapes[id] = {
|
||||||
|
...after.shapes[id],
|
||||||
|
handles: {
|
||||||
|
...after.shapes[id]?.handles,
|
||||||
|
[handle.id]: { bindingId: undefined },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
|
||||||
|
|
||||||
return { before, after }
|
return { before, after }
|
||||||
}
|
}
|
||||||
|
|
|
@ -382,30 +382,32 @@ export function getTranslateSnapshot(data: Data) {
|
||||||
|
|
||||||
// Potentially confusing name here: these are the ids of the
|
// Potentially confusing name here: these are the ids of the
|
||||||
// original shapes that were cloned, not their clones' ids.
|
// original shapes that were cloned, not their clones' ids.
|
||||||
const clonedShapeIds = Object.keys(cloneMap)
|
const clonedShapeIds = new Set(Object.keys(cloneMap))
|
||||||
|
|
||||||
const bindingsToDelete: TLDrawBinding[] = []
|
const bindingsToDelete: TLDrawBinding[] = []
|
||||||
|
|
||||||
// Create cloned bindings for shapes where both to and from shapes are selected
|
// Create cloned bindings for shapes where both to and from shapes are selected
|
||||||
// (if the user clones, then we will create a new binding for the clones)
|
// (if the user clones, then we will create a new binding for the clones)
|
||||||
Object.values(page.bindings).forEach((binding) => {
|
Object.values(page.bindings)
|
||||||
if (clonedShapeIds.includes(binding.fromId)) {
|
.filter((binding) => clonedShapeIds.has(binding.fromId) || clonedShapeIds.has(binding.toId))
|
||||||
if (clonedShapeIds.includes(binding.toId)) {
|
.forEach((binding) => {
|
||||||
const cloneId = Utils.uniqueId()
|
if (clonedShapeIds.has(binding.fromId)) {
|
||||||
const cloneBinding = {
|
if (clonedShapeIds.has(binding.toId)) {
|
||||||
...Utils.deepClone(binding),
|
const cloneId = Utils.uniqueId()
|
||||||
id: cloneId,
|
const cloneBinding = {
|
||||||
fromId: cloneMap[binding.fromId] || binding.fromId,
|
...Utils.deepClone(binding),
|
||||||
toId: cloneMap[binding.toId] || binding.toId,
|
id: cloneId,
|
||||||
}
|
fromId: cloneMap[binding.fromId] || binding.fromId,
|
||||||
|
toId: cloneMap[binding.toId] || binding.toId,
|
||||||
|
}
|
||||||
|
|
||||||
clonedBindingsMap[binding.id] = cloneId
|
clonedBindingsMap[binding.id] = cloneId
|
||||||
clonedBindings.push(cloneBinding)
|
clonedBindings.push(cloneBinding)
|
||||||
} else {
|
} else {
|
||||||
bindingsToDelete.push(binding)
|
bindingsToDelete.push(binding)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
|
||||||
|
|
||||||
// Assign new binding ids to clones (or delete them!)
|
// Assign new binding ids to clones (or delete them!)
|
||||||
clones.forEach((clone) => {
|
clones.forEach((clone) => {
|
||||||
|
|
Loading…
Reference in a new issue