loopLock -> polygon

This commit is contained in:
dwelle 2025-05-08 23:04:39 +02:00
parent 35fa4fc041
commit 74dcaeebda
12 changed files with 38 additions and 37 deletions

View File

@ -1313,14 +1313,14 @@ export class LinearElementEditor {
element.points[element.points.length - 1]; element.points[element.points.length - 1];
// break polygon if deleting start/end point // break polygon if deleting start/end point
if (isLineElement(element) && element.loopLock) { if (isLineElement(element) && element.polygon) {
if ( if (
pointIndices.includes(0) || pointIndices.includes(0) ||
(pointIndices.includes(element.points.length - 1) && (pointIndices.includes(element.points.length - 1) &&
// don't disable polygon if cleaning up uncommitted point // don't disable polygon if cleaning up uncommitted point
!isUncommittedPoint) !isUncommittedPoint)
) { ) {
app.scene.mutateElement(element, { loopLock: false }); app.scene.mutateElement(element, { polygon: false });
} }
} }
@ -1328,7 +1328,7 @@ export class LinearElementEditor {
return !pointIndices.includes(idx); return !pointIndices.includes(idx);
}); });
if (isUncommittedPoint && isLineElement(element) && element.loopLock) { if (isUncommittedPoint && isLineElement(element) && element.polygon) {
nextPoints[0] = pointFrom( nextPoints[0] = pointFrom(
nextPoints[nextPoints.length - 1][0], nextPoints[nextPoints.length - 1][0],
nextPoints[nextPoints.length - 1][1], nextPoints[nextPoints.length - 1][1],
@ -1357,7 +1357,7 @@ export class LinearElementEditor {
) { ) {
const nextPoints = [...element.points, ...addedPoints]; const nextPoints = [...element.points, ...addedPoints];
if (isLineElement(element) && element.loopLock) { if (isLineElement(element) && element.polygon) {
nextPoints[0] = pointFrom( nextPoints[0] = pointFrom(
nextPoints[nextPoints.length - 1][0], nextPoints[nextPoints.length - 1][0],
nextPoints[nextPoints.length - 1][1], nextPoints[nextPoints.length - 1][1],
@ -1391,7 +1391,7 @@ export class LinearElementEditor {
const { points } = element; const { points } = element;
// Handle loop lock behavior // Handle loop lock behavior
if (isLineElement(element) && element.loopLock) { if (isLineElement(element) && element.polygon) {
const firstPointUpdate = pointUpdates.get(0); const firstPointUpdate = pointUpdates.get(0);
const lastPointUpdate = pointUpdates.get(points.length - 1); const lastPointUpdate = pointUpdates.get(points.length - 1);

View File

@ -475,7 +475,7 @@ export const newLinearElement = (
if (isLineElement(element)) { if (isLineElement(element)) {
const lineElement: NonDeleted<ExcalidrawLineElement> = { const lineElement: NonDeleted<ExcalidrawLineElement> = {
...element, ...element,
loopLock: false, polygon: false,
}; };
return lineElement; return lineElement;

View File

@ -426,7 +426,7 @@ export const toggleLinePolygonState = (
// TODO: satisfies ElementUpdate<ExcalidrawLineElement> // TODO: satisfies ElementUpdate<ExcalidrawLineElement>
const ret = { const ret = {
loopLock: nextPolygonState, polygon: nextPolygonState,
points: updatedPoints, points: updatedPoints,
}; };

View File

@ -331,7 +331,7 @@ export type ExcalidrawLinearElement = _ExcalidrawElementBase &
export type ExcalidrawLineElement = ExcalidrawLinearElement & export type ExcalidrawLineElement = ExcalidrawLinearElement &
Readonly<{ Readonly<{
type: "line"; type: "line";
loopLock: boolean; polygon: boolean;
}>; }>;
export type FixedSegment = { export type FixedSegment = {

View File

@ -136,7 +136,7 @@ export const actionFinalize = register({
if (isLineElement(multiPointElement)) { if (isLineElement(multiPointElement)) {
scene.mutateElement(multiPointElement, { scene.mutateElement(multiPointElement, {
points, points,
loopLock: true, polygon: true,
}); });
} else { } else {
scene.mutateElement(multiPointElement, { scene.mutateElement(multiPointElement, {

View File

@ -94,8 +94,8 @@ export const actionToggleLinearEditor = register({
}, },
}); });
export const actionToggleLoopLock = register({ export const actionTogglePolygon = register({
name: "toggleLoopLock", name: "togglePolygon",
category: DEFAULT_CATEGORIES.elements, category: DEFAULT_CATEGORIES.elements,
icon: polygonIcon, icon: polygonIcon,
keywords: ["loop"], keywords: ["loop"],
@ -108,10 +108,12 @@ export const actionToggleLoopLock = register({
const allLocked = const allLocked =
selectedElements.length > 0 && selectedElements.length > 0 &&
selectedElements.every( selectedElements.every(
(element) => isLineElement(element) && element.loopLock, (element) => isLineElement(element) && element.polygon,
); );
return allLocked ? "labels.loopLock.unlock" : "labels.loopLock.lock"; return allLocked
? "labels.polygon.breakPolygon"
: "labels.polygon.convertToPolygon";
}, },
trackEvent: { trackEvent: {
category: "element", category: "element",
@ -137,10 +139,8 @@ export const actionToggleLoopLock = register({
const targetElements = selectedElements as ExcalidrawLineElement[]; const targetElements = selectedElements as ExcalidrawLineElement[];
// Check if we should lock or unlock based on current state // if one element not a polygon, convert all to polygon
// If all elements are locked, unlock all. Otherwise, lock all. const nextPolygonState = targetElements.some((element) => !element.polygon);
const allLocked = targetElements.every((element) => element.loopLock);
const newLoopLockState = !allLocked;
const targetElementsMap = arrayToMap(targetElements); const targetElementsMap = arrayToMap(targetElements);
@ -151,10 +151,10 @@ export const actionToggleLoopLock = register({
} }
return newElementWith(element, { return newElementWith(element, {
backgroundColor: newLoopLockState backgroundColor: nextPolygonState
? element.backgroundColor ? element.backgroundColor
: "transparent", : "transparent",
...toggleLinePolygonState(element, newLoopLockState), ...toggleLinePolygonState(element, nextPolygonState),
}); });
}), }),
appState, appState,
@ -174,20 +174,21 @@ export const actionToggleLoopLock = register({
// only show polygon button if every selected element is already // only show polygon button if every selected element is already
// a polygon, effectively showing this button only to allow for // a polygon, effectively showing this button only to allow for
// disabling the polygon state // disabling the polygon state
!element.loopLock || !element.polygon ||
element.points.length < 3, element.points.length < 3,
) )
) { ) {
return null; return null;
} }
// If all are locked, show locked icon. Otherwise show unlocked const allPolygon = selectedElements.every(
const allLocked = selectedElements.every( (element) => isLineElement(element) && element.polygon,
(element) => isLineElement(element) && element.loopLock,
); );
const label = t( const label = t(
allLocked ? "labels.loopLock.unlock" : "labels.loopLock.lock", allPolygon
? "labels.polygon.breakPolygon"
: "labels.polygon.convertToPolygon",
); );
return ( return (
@ -195,7 +196,7 @@ export const actionToggleLoopLock = register({
icon={polygonIcon} icon={polygonIcon}
title={label} title={label}
aria-label={label} aria-label={label}
active={allLocked} active={allPolygon}
onClick={() => updateData(null)} onClick={() => updateData(null)}
style={{ marginLeft: "auto" }} style={{ marginLeft: "auto" }}
/> />

View File

@ -1439,7 +1439,7 @@ export const actionChangeRoundness = register({
)} )}
onChange={(value) => updateData(value)} onChange={(value) => updateData(value)}
> >
{renderAction("toggleLoopLock")} {renderAction("togglePolygon")}
</ButtonIconSelect> </ButtonIconSelect>
</fieldset> </fieldset>
); );

View File

@ -143,7 +143,7 @@ export type ActionName =
| "wrapSelectionInFrame" | "wrapSelectionInFrame"
| "toggleLassoTool" | "toggleLassoTool"
| "toggleShapeSwitch" | "toggleShapeSwitch"
| "toggleLoopLock"; | "togglePolygon";
export type PanelComponentProps = { export type PanelComponentProps = {
elements: readonly ExcalidrawElement[]; elements: readonly ExcalidrawElement[];

View File

@ -293,7 +293,7 @@ function CommandPaletteInner({
actionManager.actions.decreaseFontSize, actionManager.actions.decreaseFontSize,
actionManager.actions.toggleLinearEditor, actionManager.actions.toggleLinearEditor,
actionManager.actions.cropEditor, actionManager.actions.cropEditor,
actionManager.actions.toggleLoopLock, actionManager.actions.togglePolygon,
actionLink, actionLink,
actionCopyElementLink, actionCopyElementLink,
actionLinkToElement, actionLinkToElement,
@ -586,10 +586,10 @@ function CommandPaletteInner({
}); });
setAllCommands(allCommands); setAllCommands(allCommands);
setLastUsed( // setLastUsed(
allCommands.find((command) => command.label === lastUsed?.label) ?? // allCommands.find((command) => command.label === lastUsed?.label) ??
null, // null,
); // );
} }
}, [ }, [
stableDeps, stableDeps,

View File

@ -342,7 +342,7 @@ const restoreElement = (
x, x,
y, y,
...(isLineElement(element) ...(isLineElement(element)
? { loopLock: element.loopLock ?? false } ? { polygon: element.polygon ?? false }
: {}), : {}),
...getSizeFromPoints(points), ...getSizeFromPoints(points),
}); });

View File

@ -141,9 +141,9 @@
"edit": "Edit line", "edit": "Edit line",
"editArrow": "Edit arrow" "editArrow": "Edit arrow"
}, },
"loopLock": { "polygon": {
"unlock": "Break polygon", "breakPolygon": "Break polygon",
"lock": "Convert to polygon" "convertToPolygon": "Convert to polygon"
}, },
"elementLock": { "elementLock": {
"lock": "Lock", "lock": "Lock",

View File

@ -466,7 +466,7 @@ const renderLinearPointHandles = (
(idx !== points.length - 1 || (idx !== points.length - 1 ||
appState.editingLinearElement || appState.editingLinearElement ||
!_isLineElement || !_isLineElement ||
!element.loopLock) && !element.polygon) &&
pointsEqual( pointsEqual(
point, point,
idx === points.length - 1 ? points[0] : points[idx - 1], idx === points.length - 1 ? points[0] : points[idx - 1],