Implement line editing (#1616)

* implement line editing

* line editing with rotation

* ensure adding new points is disabled on point dragging

* fix hotkey replacement

* don't paint bounding box when creating new multipoint

* tweak points style, account for zoom and z-index

* don't persist editingLinearElement to localStorage

* don't mutate on noop points updates

* account for rotation when adding new point

* ensure clicking on points doesn't deselect element

* tweak history handling around editingline element

* update snapshots

* refactor pointerMove handling

* factor out point dragging

* factor out pointerDown

* improve positioning with rotation

* revert to use roughjs for calculating points bounds

* migrate from storing editingLinearElement.element to id

* make GlobalScene.getElement into O(1)

* use Alt for adding new points

* fix adding and deleting a point with rotation

* disable resize handlers & bounding box on line edit

Co-authored-by: daishi <daishi@axlight.com>
This commit is contained in:
David Luzar 2020-06-01 11:35:44 +02:00 committed by GitHub
parent db316f32e0
commit 14a66956d7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 1129 additions and 76 deletions

View file

@ -10,6 +10,7 @@ import { ExcalidrawElement } from "../element/types";
import { AppState } from "../types";
import { newElementWith } from "../element/mutateElement";
import { getElementsInGroup } from "../groups";
import { LinearElementEditor } from "../element/linearElementEditor";
const deleteSelectedElements = (
elements: readonly ExcalidrawElement[],
@ -29,26 +30,80 @@ const deleteSelectedElements = (
};
};
function handleGroupEditingState(
appState: AppState,
elements: readonly ExcalidrawElement[],
): AppState {
if (appState.editingGroupId) {
const siblingElements = getElementsInGroup(
getNonDeletedElements(elements),
appState.editingGroupId!,
);
if (siblingElements.length) {
return {
...appState,
selectedElementIds: { [siblingElements[0].id]: true },
};
}
}
return appState;
}
export const actionDeleteSelected = register({
name: "deleteSelectedElements",
perform: (elements, appState) => {
if (
appState.editingLinearElement?.activePointIndex != null &&
appState.editingLinearElement?.activePointIndex > -1
) {
const { elementId } = appState.editingLinearElement;
const element = LinearElementEditor.getElement(elementId);
if (element) {
// case: deleting last point
if (element.points.length < 2) {
const nextElements = elements.filter((el) => el.id !== element.id);
const nextAppState = handleGroupEditingState(appState, nextElements);
return {
elements: nextElements,
appState: {
...nextAppState,
editingLinearElement: null,
},
commitToHistory: false,
};
}
LinearElementEditor.movePoint(
element,
appState.editingLinearElement.activePointIndex,
"delete",
);
return {
elements: elements,
appState: {
...appState,
editingLinearElement: {
...appState.editingLinearElement,
activePointIndex:
appState.editingLinearElement.activePointIndex > 0
? appState.editingLinearElement.activePointIndex - 1
: 0,
},
},
commitToHistory: true,
};
}
}
let {
elements: nextElements,
appState: nextAppState,
} = deleteSelectedElements(elements, appState);
if (appState.editingGroupId) {
const siblingElements = getElementsInGroup(
getNonDeletedElements(nextElements),
appState.editingGroupId!,
);
if (siblingElements.length) {
nextAppState = {
...nextAppState,
selectedElementIds: { [siblingElements[0].id]: true },
};
}
}
nextAppState = handleGroupEditingState(nextAppState, nextElements);
return {
elements: nextElements,
appState: {