feat: better unlock (#9546)
* change lock label * feat: add unlock logic for single units on pointer up * feat: add unlock popup * fix: linting errors * style: padding tweaks * style: remove redundant line * feat: lock multiple units together * style: tweak color & position * feat: add highlight for locked elements * feat: select groups correctly after unlocking * test: update snapshots * fix: lasso from selecting locked elements * fix: should prevent selecting unlocked elements and setting locked id at the same time * fix: reset hit locked id immediately when appropriate * feat: capture locked units in delta * test: update locking test * feat: show lock highlight when locking (including undo/redo) * feat: make locked highlighting consistent * feat: show correct cursor type when moving over locked elements * fix history * remove `lockedUnits.singleUnits` * tweak button * do not render UnlockPopup if not locked element selected * tweak actions * refactor: simplify type * refactor: rename type * refactor: simplify hit element setting & checking * fix: prefer locked over link * rename to `activeLockedId` * refactor: getElementAtPosition takes an optional hitelments array * fix: avoid setting active locked id after resizing --------- Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import {
|
||||
getCommonBounds,
|
||||
getElementsInGroup,
|
||||
selectGroupsFromGivenElements,
|
||||
} from "@excalidraw/element";
|
||||
import { sceneCoordsToViewportCoords } from "@excalidraw/common";
|
||||
|
||||
import { flushSync } from "react-dom";
|
||||
|
||||
import { actionToggleElementLock } from "../actions";
|
||||
import { t } from "../i18n";
|
||||
|
||||
import "./UnlockPopup.scss";
|
||||
|
||||
import { LockedIconFilled } from "./icons";
|
||||
|
||||
import type App from "./App";
|
||||
|
||||
import type { AppState } from "../types";
|
||||
|
||||
const UnlockPopup = ({
|
||||
app,
|
||||
activeLockedId,
|
||||
}: {
|
||||
app: App;
|
||||
activeLockedId: NonNullable<AppState["activeLockedId"]>;
|
||||
}) => {
|
||||
const element = app.scene.getElement(activeLockedId);
|
||||
|
||||
const elements = element
|
||||
? [element]
|
||||
: getElementsInGroup(app.scene.getNonDeletedElementsMap(), activeLockedId);
|
||||
|
||||
if (elements.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [x, y] = getCommonBounds(elements);
|
||||
const { x: viewX, y: viewY } = sceneCoordsToViewportCoords(
|
||||
{ sceneX: x, sceneY: y },
|
||||
app.state,
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="UnlockPopup"
|
||||
style={{
|
||||
bottom: `${app.state.height + 12 - viewY + app.state.offsetTop}px`,
|
||||
left: `${viewX - app.state.offsetLeft}px`,
|
||||
}}
|
||||
onClick={() => {
|
||||
flushSync(() => {
|
||||
const groupIds = selectGroupsFromGivenElements(elements, app.state);
|
||||
app.setState({
|
||||
selectedElementIds: elements.reduce(
|
||||
(acc, element) => ({
|
||||
...acc,
|
||||
[element.id]: true,
|
||||
}),
|
||||
{},
|
||||
),
|
||||
selectedGroupIds: groupIds,
|
||||
activeLockedId: null,
|
||||
});
|
||||
});
|
||||
app.actionManager.executeAction(actionToggleElementLock);
|
||||
}}
|
||||
title={t("labels.elementLock.unlock")}
|
||||
>
|
||||
{LockedIconFilled}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UnlockPopup;
|
||||
Reference in New Issue
Block a user