mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
fix zindex to account for group boundaries (#2065)
This commit is contained in:
parent
ea020f2c50
commit
d07099aadd
10 changed files with 1287 additions and 416 deletions
|
@ -10,7 +10,11 @@ import { t } from "../i18n";
|
|||
import { getShortcutKey } from "../utils";
|
||||
import { LinearElementEditor } from "../element/linearElementEditor";
|
||||
import { mutateElement } from "../element/mutateElement";
|
||||
import { selectGroupsForSelectedElements } from "../groups";
|
||||
import {
|
||||
selectGroupsForSelectedElements,
|
||||
getSelectedGroupForElement,
|
||||
getElementsInGroup,
|
||||
} from "../groups";
|
||||
import { AppState } from "../types";
|
||||
import { fixBindingsAfterDuplication } from "../element/binding";
|
||||
import { ActionResult } from "./types";
|
||||
|
@ -82,28 +86,53 @@ const duplicateElements = (
|
|||
const newElements: ExcalidrawElement[] = [];
|
||||
const oldElements: ExcalidrawElement[] = [];
|
||||
const oldIdToDuplicatedId = new Map();
|
||||
const finalElements = elements.reduce(
|
||||
(acc: Array<ExcalidrawElement>, element: ExcalidrawElement) => {
|
||||
if (appState.selectedElementIds[element.id]) {
|
||||
const newElement = duplicateElement(
|
||||
appState.editingGroupId,
|
||||
groupIdMap,
|
||||
element,
|
||||
{
|
||||
x: element.x + 10,
|
||||
y: element.y + 10,
|
||||
},
|
||||
);
|
||||
oldIdToDuplicatedId.set(element.id, newElement.id);
|
||||
oldElements.push(element);
|
||||
newElements.push(newElement);
|
||||
return acc.concat([element, newElement]);
|
||||
|
||||
const duplicateAndOffsetElement = (element: ExcalidrawElement) => {
|
||||
const newElement = duplicateElement(
|
||||
appState.editingGroupId,
|
||||
groupIdMap,
|
||||
element,
|
||||
{
|
||||
x: element.x + 10,
|
||||
y: element.y + 10,
|
||||
},
|
||||
);
|
||||
oldIdToDuplicatedId.set(element.id, newElement.id);
|
||||
oldElements.push(element);
|
||||
newElements.push(newElement);
|
||||
return newElement;
|
||||
};
|
||||
|
||||
const finalElements: ExcalidrawElement[] = [];
|
||||
|
||||
let i = 0;
|
||||
while (i < elements.length) {
|
||||
const element = elements[i];
|
||||
if (appState.selectedElementIds[element.id]) {
|
||||
if (element.groupIds.length) {
|
||||
const groupId = getSelectedGroupForElement(appState, element);
|
||||
// if group selected, duplicate it atomically
|
||||
if (groupId) {
|
||||
const groupElements = getElementsInGroup(elements, groupId);
|
||||
finalElements.push(
|
||||
...groupElements,
|
||||
...groupElements.map((element) =>
|
||||
duplicateAndOffsetElement(element),
|
||||
),
|
||||
);
|
||||
i = i + groupElements.length;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return acc.concat(element);
|
||||
},
|
||||
[],
|
||||
);
|
||||
finalElements.push(element, duplicateAndOffsetElement(element));
|
||||
} else {
|
||||
finalElements.push(element);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
fixBindingsAfterDuplication(finalElements, oldElements, oldIdToDuplicatedId);
|
||||
|
||||
return {
|
||||
elements: finalElements,
|
||||
appState: selectGroupsForSelectedElements(
|
||||
|
|
|
@ -15,69 +15,12 @@ import {
|
|||
SendToBackIcon,
|
||||
BringForwardIcon,
|
||||
} from "../components/icons";
|
||||
import { ExcalidrawElement } from "../element/types";
|
||||
import { AppState } from "../types";
|
||||
|
||||
const getElementIndices = (
|
||||
direction: "left" | "right",
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
) => {
|
||||
const selectedIndices: number[] = [];
|
||||
let deletedIndicesCache: number[] = [];
|
||||
|
||||
const cb = (element: ExcalidrawElement, index: number) => {
|
||||
if (element.isDeleted) {
|
||||
// we want to build an array of deleted elements that are preceeding
|
||||
// a selected element so that we move them together
|
||||
deletedIndicesCache.push(index);
|
||||
} else {
|
||||
if (appState.selectedElementIds[element.id]) {
|
||||
selectedIndices.push(...deletedIndicesCache, index);
|
||||
}
|
||||
// always empty cache of deleted elements after either pushing a group
|
||||
// of selected/deleted elements, of after encountering non-deleted elem
|
||||
deletedIndicesCache = [];
|
||||
}
|
||||
};
|
||||
|
||||
// sending back → select contiguous deleted elements that are to the left of
|
||||
// selected element(s)
|
||||
if (direction === "left") {
|
||||
let i = -1;
|
||||
const len = elements.length;
|
||||
while (++i < len) {
|
||||
cb(elements[i], i);
|
||||
}
|
||||
// moving to front → loop from right to left so that we don't need to
|
||||
// backtrack when gathering deleted elements
|
||||
} else {
|
||||
let i = elements.length;
|
||||
while (--i > -1) {
|
||||
cb(elements[i], i);
|
||||
}
|
||||
}
|
||||
// sort in case we were gathering indexes from right to left
|
||||
return selectedIndices.sort();
|
||||
};
|
||||
|
||||
const moveElements = (
|
||||
func: typeof moveOneLeft,
|
||||
elements: readonly ExcalidrawElement[],
|
||||
appState: AppState,
|
||||
) => {
|
||||
const _elements = elements.slice();
|
||||
const direction =
|
||||
func === moveOneLeft || func === moveAllLeft ? "left" : "right";
|
||||
const indices = getElementIndices(direction, _elements, appState);
|
||||
return func(_elements, indices);
|
||||
};
|
||||
|
||||
export const actionSendBackward = register({
|
||||
name: "sendBackward",
|
||||
perform: (elements, appState) => {
|
||||
return {
|
||||
elements: moveElements(moveOneLeft, elements, appState),
|
||||
elements: moveOneLeft(elements, appState),
|
||||
appState,
|
||||
commitToHistory: true,
|
||||
};
|
||||
|
@ -102,7 +45,7 @@ export const actionBringForward = register({
|
|||
name: "bringForward",
|
||||
perform: (elements, appState) => {
|
||||
return {
|
||||
elements: moveElements(moveOneRight, elements, appState),
|
||||
elements: moveOneRight(elements, appState),
|
||||
appState,
|
||||
commitToHistory: true,
|
||||
};
|
||||
|
@ -127,7 +70,7 @@ export const actionSendToBack = register({
|
|||
name: "sendToBack",
|
||||
perform: (elements, appState) => {
|
||||
return {
|
||||
elements: moveElements(moveAllLeft, elements, appState),
|
||||
elements: moveAllLeft(elements, appState),
|
||||
appState,
|
||||
commitToHistory: true,
|
||||
};
|
||||
|
@ -160,7 +103,7 @@ export const actionBringToFront = register({
|
|||
name: "bringToFront",
|
||||
perform: (elements, appState) => {
|
||||
return {
|
||||
elements: moveElements(moveAllRight, elements, appState),
|
||||
elements: moveAllRight(elements, appState),
|
||||
appState,
|
||||
commitToHistory: true,
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue