fix: incorrect selection on duplicating labeled containers

This commit is contained in:
dwelle 2025-04-17 12:03:11 +02:00
parent 887d4b514d
commit 3f75a4d04f
4 changed files with 80 additions and 68 deletions

View file

@ -7,13 +7,20 @@ import type {
import { getElementAbsoluteCoords, getElementBounds } from "./bounds";
import { isElementInViewport } from "./sizeHelpers";
import { isBoundToContainer, isFrameLikeElement } from "./typeChecks";
import {
isBoundToContainer,
isFrameLikeElement,
isLinearElement,
} from "./typeChecks";
import {
elementOverlapsWithFrame,
getContainingFrame,
getFrameChildren,
} from "./frame";
import { LinearElementEditor } from "./linearElementEditor";
import { selectGroupsForSelectedElements } from "./groups";
import type {
ElementsMap,
ElementsMapOrArray,
@ -254,3 +261,48 @@ export const makeNextSelectedElementIds = (
return nextSelectedElementIds;
};
const _getLinearElementEditor = (
targetElements: readonly ExcalidrawElement[],
) => {
const linears = targetElements.filter(isLinearElement);
if (linears.length === 1) {
const linear = linears[0];
const boundElements = linear.boundElements?.map((def) => def.id) ?? [];
const onlySingleLinearSelected = targetElements.every(
(el) => el.id === linear.id || boundElements.includes(el.id),
);
if (onlySingleLinearSelected) {
return new LinearElementEditor(linear);
}
}
return null;
};
export const getSelectionStateForElements = (
targetElements: readonly ExcalidrawElement[],
allElements: readonly NonDeletedExcalidrawElement[],
appState: AppState,
) => {
return {
selectedLinearElement: _getLinearElementEditor(targetElements),
...selectGroupsForSelectedElements(
{
editingGroupId: appState.editingGroupId,
selectedElementIds: excludeElementsInFramesFromSelection(
targetElements,
).reduce((acc: Record<ExcalidrawElement["id"], true>, element) => {
if (!isBoundToContainer(element)) {
acc[element.id] = true;
}
return acc;
}, {}),
},
allElements,
appState,
null,
),
};
};

View file

@ -614,10 +614,10 @@ describe("duplication z-order", () => {
]);
});
it("reverse-duplicating text container (in-order)", async () => {
it("alt-duplicating text container (in-order)", async () => {
const [rectangle, text] = API.createTextContainer();
API.setElements([rectangle, text]);
API.setSelectedElements([rectangle, text]);
API.setSelectedElements([rectangle]);
Keyboard.withModifierKeys({ alt: true }, () => {
mouse.down(rectangle.x + 5, rectangle.y + 5);
@ -631,15 +631,14 @@ describe("duplication z-order", () => {
{
[ORIG_ID]: text.id,
containerId: getCloneByOrigId(rectangle.id)?.id,
selected: true,
},
]);
});
it("reverse-duplicating text container (out-of-order)", async () => {
it("alt-duplicating text container (out-of-order)", async () => {
const [rectangle, text] = API.createTextContainer();
API.setElements([text, rectangle]);
API.setSelectedElements([rectangle, text]);
API.setSelectedElements([rectangle]);
Keyboard.withModifierKeys({ alt: true }, () => {
mouse.down(rectangle.x + 5, rectangle.y + 5);
@ -653,16 +652,15 @@ describe("duplication z-order", () => {
{
[ORIG_ID]: text.id,
containerId: getCloneByOrigId(rectangle.id)?.id,
selected: true,
},
]);
});
it("reverse-duplicating labeled arrows (in-order)", async () => {
it("alt-duplicating labeled arrows (in-order)", async () => {
const [arrow, text] = API.createLabeledArrow();
API.setElements([arrow, text]);
API.setSelectedElements([arrow, text]);
API.setSelectedElements([arrow]);
Keyboard.withModifierKeys({ alt: true }, () => {
mouse.down(arrow.x + 5, arrow.y + 5);
@ -676,16 +674,18 @@ describe("duplication z-order", () => {
{
[ORIG_ID]: text.id,
containerId: getCloneByOrigId(arrow.id)?.id,
selected: true,
},
]);
expect(h.state.selectedLinearElement).toEqual(
expect.objectContaining({ elementId: getCloneByOrigId(arrow.id)?.id }),
);
});
it("reverse-duplicating labeled arrows (out-of-order)", async () => {
it("alt-duplicating labeled arrows (out-of-order)", async () => {
const [arrow, text] = API.createLabeledArrow();
API.setElements([text, arrow]);
API.setSelectedElements([arrow, text]);
API.setSelectedElements([arrow]);
Keyboard.withModifierKeys({ alt: true }, () => {
mouse.down(arrow.x + 5, arrow.y + 5);
@ -699,12 +699,11 @@ describe("duplication z-order", () => {
{
[ORIG_ID]: text.id,
containerId: getCloneByOrigId(arrow.id)?.id,
selected: true,
},
]);
});
it("reverse-duplicating bindable element with bound arrow should keep the arrow on the duplicate", () => {
it("alt-duplicating bindable element with bound arrow should keep the arrow on the duplicate", () => {
const rect = UI.createElement("rectangle", {
x: 0,
y: 0,