mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
feat: make appState.selectedElementIds
more stable (#6745)
This commit is contained in:
parent
3ddcc48e4c
commit
49e4289878
15 changed files with 503 additions and 295 deletions
35
src/scene/selection.test.ts
Normal file
35
src/scene/selection.test.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { makeNextSelectedElementIds } from "./selection";
|
||||
|
||||
describe("makeNextSelectedElementIds", () => {
|
||||
const _makeNextSelectedElementIds = (
|
||||
selectedElementIds: { [id: string]: true },
|
||||
prevSelectedElementIds: { [id: string]: true },
|
||||
expectUpdated: boolean,
|
||||
) => {
|
||||
const ret = makeNextSelectedElementIds(selectedElementIds, {
|
||||
selectedElementIds: prevSelectedElementIds,
|
||||
});
|
||||
expect(ret === selectedElementIds).toBe(expectUpdated);
|
||||
};
|
||||
it("should return prevState selectedElementIds if no change", () => {
|
||||
_makeNextSelectedElementIds({}, {}, false);
|
||||
_makeNextSelectedElementIds({ 1: true }, { 1: true }, false);
|
||||
_makeNextSelectedElementIds(
|
||||
{ 1: true, 2: true },
|
||||
{ 1: true, 2: true },
|
||||
false,
|
||||
);
|
||||
});
|
||||
it("should return new selectedElementIds if changed", () => {
|
||||
// _makeNextSelectedElementIds({ 1: true }, { 1: false }, true);
|
||||
_makeNextSelectedElementIds({ 1: true }, {}, true);
|
||||
_makeNextSelectedElementIds({}, { 1: true }, true);
|
||||
_makeNextSelectedElementIds({ 1: true }, { 2: true }, true);
|
||||
_makeNextSelectedElementIds({ 1: true }, { 1: true, 2: true }, true);
|
||||
_makeNextSelectedElementIds(
|
||||
{ 1: true, 2: true },
|
||||
{ 1: true, 3: true },
|
||||
true,
|
||||
);
|
||||
});
|
||||
});
|
|
@ -10,6 +10,7 @@ import {
|
|||
getContainingFrame,
|
||||
getFrameElements,
|
||||
} from "../frame";
|
||||
import { isShallowEqual } from "../utils";
|
||||
|
||||
/**
|
||||
* Frames and their containing elements are not to be selected at the same time.
|
||||
|
@ -88,11 +89,41 @@ export const getElementsWithinSelection = (
|
|||
return elementsInSelection;
|
||||
};
|
||||
|
||||
export const isSomeElementSelected = (
|
||||
elements: readonly NonDeletedExcalidrawElement[],
|
||||
appState: Pick<AppState, "selectedElementIds">,
|
||||
): boolean =>
|
||||
elements.some((element) => appState.selectedElementIds[element.id]);
|
||||
// FIXME move this into the editor instance to keep utility methods stateless
|
||||
export const isSomeElementSelected = (function () {
|
||||
let lastElements: readonly NonDeletedExcalidrawElement[] | null = null;
|
||||
let lastSelectedElementIds: AppState["selectedElementIds"] | null = null;
|
||||
let isSelected: boolean | null = null;
|
||||
|
||||
const ret = (
|
||||
elements: readonly NonDeletedExcalidrawElement[],
|
||||
appState: Pick<AppState, "selectedElementIds">,
|
||||
): boolean => {
|
||||
if (
|
||||
isSelected != null &&
|
||||
elements === lastElements &&
|
||||
appState.selectedElementIds === lastSelectedElementIds
|
||||
) {
|
||||
return isSelected;
|
||||
}
|
||||
|
||||
isSelected = elements.some(
|
||||
(element) => appState.selectedElementIds[element.id],
|
||||
);
|
||||
lastElements = elements;
|
||||
lastSelectedElementIds = appState.selectedElementIds;
|
||||
|
||||
return isSelected;
|
||||
};
|
||||
|
||||
ret.clearCache = () => {
|
||||
lastElements = null;
|
||||
lastSelectedElementIds = null;
|
||||
isSelected = null;
|
||||
};
|
||||
|
||||
return ret;
|
||||
})();
|
||||
|
||||
/**
|
||||
* Returns common attribute (picked by `getAttribute` callback) of selected
|
||||
|
@ -161,3 +192,18 @@ export const getTargetElements = (
|
|||
: getSelectedElements(elements, appState, {
|
||||
includeBoundTextElement: true,
|
||||
});
|
||||
|
||||
/**
|
||||
* returns prevState's selectedElementids if no change from previous, so as to
|
||||
* retain reference identity for memoization
|
||||
*/
|
||||
export const makeNextSelectedElementIds = (
|
||||
nextSelectedElementIds: AppState["selectedElementIds"],
|
||||
prevState: Pick<AppState, "selectedElementIds">,
|
||||
) => {
|
||||
if (isShallowEqual(prevState.selectedElementIds, nextSelectedElementIds)) {
|
||||
return prevState.selectedElementIds;
|
||||
}
|
||||
|
||||
return nextSelectedElementIds;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue