feat: do not delete frame children on frame delete (#9011)

This commit is contained in:
David Luzar 2025-01-14 21:08:25 +01:00 committed by GitHub
parent 46f42ef8d7
commit ae6bee3403
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 103 additions and 40 deletions

View file

@ -7,7 +7,7 @@ import { getNonDeletedElements } from "../element";
import type { ExcalidrawElement } from "../element/types"; import type { ExcalidrawElement } from "../element/types";
import type { AppClassProperties, AppState } from "../types"; import type { AppClassProperties, AppState } from "../types";
import { mutateElement, newElementWith } from "../element/mutateElement"; import { mutateElement, newElementWith } from "../element/mutateElement";
import { getElementsInGroup } from "../groups"; import { getElementsInGroup, selectGroupsForSelectedElements } from "../groups";
import { LinearElementEditor } from "../element/linearElementEditor"; import { LinearElementEditor } from "../element/linearElementEditor";
import { fixBindingsAfterDeletion } from "../element/binding"; import { fixBindingsAfterDeletion } from "../element/binding";
import { import {
@ -33,14 +33,15 @@ const deleteSelectedElements = (
).map((el) => el.id), ).map((el) => el.id),
); );
return { const selectedElementIds: Record<ExcalidrawElement["id"], true> = {};
elements: elements.map((el) => {
let shouldSelectEditingGroup = true;
const nextElements = elements.map((el) => {
if (appState.selectedElementIds[el.id]) { if (appState.selectedElementIds[el.id]) {
if (el.boundElements) { if (el.boundElements) {
el.boundElements.forEach((candidate) => { el.boundElements.forEach((candidate) => {
const bound = app.scene const bound = app.scene.getNonDeletedElementsMap().get(candidate.id);
.getNonDeletedElementsMap()
.get(candidate.id);
if (bound && isElbowArrow(bound)) { if (bound && isElbowArrow(bound)) {
mutateElement(bound, { mutateElement(bound, {
startBinding: startBinding:
@ -48,9 +49,7 @@ const deleteSelectedElements = (
? null ? null
: bound.startBinding, : bound.startBinding,
endBinding: endBinding:
el.id === bound.endBinding?.elementId el.id === bound.endBinding?.elementId ? null : bound.endBinding,
? null
: bound.endBinding,
}); });
mutateElbowArrow(bound, elementsMap, bound.points); mutateElbowArrow(bound, elementsMap, bound.points);
} }
@ -59,22 +58,74 @@ const deleteSelectedElements = (
return newElementWith(el, { isDeleted: true }); return newElementWith(el, { isDeleted: true });
} }
// if deleting a frame, remove the children from it and select them
if (el.frameId && framesToBeDeleted.has(el.frameId)) { if (el.frameId && framesToBeDeleted.has(el.frameId)) {
return newElementWith(el, { isDeleted: true }); shouldSelectEditingGroup = false;
selectedElementIds[el.id] = true;
return newElementWith(el, { frameId: null });
} }
if ( if (isBoundToContainer(el) && appState.selectedElementIds[el.containerId]) {
isBoundToContainer(el) &&
appState.selectedElementIds[el.containerId]
) {
return newElementWith(el, { isDeleted: true }); return newElementWith(el, { isDeleted: true });
} }
return el; return el;
}), });
let nextEditingGroupId = appState.editingGroupId;
// select next eligible element in currently editing group or supergroup
if (shouldSelectEditingGroup && appState.editingGroupId) {
const elems = getElementsInGroup(
nextElements,
appState.editingGroupId,
).filter((el) => !el.isDeleted);
if (elems.length > 1) {
if (elems[0]) {
selectedElementIds[elems[0].id] = true;
}
} else {
nextEditingGroupId = null;
if (elems[0]) {
selectedElementIds[elems[0].id] = true;
}
const lastElementInGroup = elems[0];
if (lastElementInGroup) {
const editingGroupIdx = lastElementInGroup.groupIds.findIndex(
(groupId) => {
return groupId === appState.editingGroupId;
},
);
const superGroupId = lastElementInGroup.groupIds[editingGroupIdx + 1];
if (superGroupId) {
const elems = getElementsInGroup(nextElements, superGroupId).filter(
(el) => !el.isDeleted,
);
if (elems.length > 1) {
nextEditingGroupId = superGroupId;
elems.forEach((el) => {
selectedElementIds[el.id] = true;
});
}
}
}
}
}
return {
elements: nextElements,
appState: { appState: {
...appState, ...appState,
selectedElementIds: {}, ...selectGroupsForSelectedElements(
selectedGroupIds: {}, {
selectedElementIds,
editingGroupId: nextEditingGroupId,
},
nextElements,
appState,
null,
),
}, },
}; };
}; };

View file

@ -105,6 +105,10 @@ export const selectGroupsForSelectedElements = (function () {
const groupElementsIndex: Record<GroupId, string[]> = {}; const groupElementsIndex: Record<GroupId, string[]> = {};
const selectedElementIdsInGroups = elements.reduce( const selectedElementIdsInGroups = elements.reduce(
(acc: Record<string, true>, element) => { (acc: Record<string, true>, element) => {
if (element.isDeleted) {
return acc;
}
const groupId = element.groupIds.find((id) => selectedGroupIds[id]); const groupId = element.groupIds.find((id) => selectedGroupIds[id]);
if (groupId) { if (groupId) {

View file

@ -4307,14 +4307,20 @@ History {
"appStateChange": AppStateChange { "appStateChange": AppStateChange {
"delta": Delta { "delta": Delta {
"deleted": { "deleted": {
"editingGroupId": null,
"selectedElementIds": { "selectedElementIds": {
"id1": true, "id1": true,
}, },
"selectedGroupIds": {
"id4": false,
},
}, },
"inserted": { "inserted": {
"editingGroupId": "id4",
"selectedElementIds": { "selectedElementIds": {
"id0": true, "id0": true,
}, },
"selectedGroupIds": {},
}, },
}, },
}, },
@ -4337,14 +4343,16 @@ History {
"appStateChange": AppStateChange { "appStateChange": AppStateChange {
"delta": Delta { "delta": Delta {
"deleted": { "deleted": {
"editingGroupId": null,
"selectedElementIds": {}, "selectedElementIds": {},
"selectedGroupIds": {},
}, },
"inserted": { "inserted": {
"editingGroupId": "id4",
"selectedElementIds": { "selectedElementIds": {
"id1": true, "id1": true,
}, },
"selectedGroupIds": {
"id4": false,
},
}, },
}, },
}, },