|
|
|
@@ -2158,9 +2158,14 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
|
|
|
|
|
public dismissLinearEditor = () => {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
this.setState({
|
|
|
|
|
editingLinearElement: null,
|
|
|
|
|
});
|
|
|
|
|
if (this.state.selectedLinearElement?.isEditing) {
|
|
|
|
|
this.setState({
|
|
|
|
|
selectedLinearElement: {
|
|
|
|
|
...this.state.selectedLinearElement,
|
|
|
|
|
isEditing: false,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@@ -2856,15 +2861,15 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
this.state.editingLinearElement &&
|
|
|
|
|
!this.state.selectedElementIds[this.state.editingLinearElement.elementId]
|
|
|
|
|
this.state.selectedLinearElement?.isEditing &&
|
|
|
|
|
!this.state.selectedElementIds[this.state.selectedLinearElement.elementId]
|
|
|
|
|
) {
|
|
|
|
|
// defer so that the scheduleCapture flag isn't reset via current update
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
// execute only if the condition still holds when the deferred callback
|
|
|
|
|
// executes (it can be scheduled multiple times depending on how
|
|
|
|
|
// many times the component renders)
|
|
|
|
|
this.state.editingLinearElement &&
|
|
|
|
|
this.state.selectedLinearElement?.isEditing &&
|
|
|
|
|
this.actionManager.executeAction(actionFinalize);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
@@ -4419,17 +4424,13 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
if (event[KEYS.CTRL_OR_CMD] || isLineElement(selectedElement)) {
|
|
|
|
|
if (isLinearElement(selectedElement)) {
|
|
|
|
|
if (
|
|
|
|
|
!this.state.editingLinearElement ||
|
|
|
|
|
this.state.editingLinearElement.elementId !== selectedElement.id
|
|
|
|
|
!this.state.selectedLinearElement?.isEditing ||
|
|
|
|
|
this.state.selectedLinearElement.elementId !==
|
|
|
|
|
selectedElement.id
|
|
|
|
|
) {
|
|
|
|
|
this.store.scheduleCapture();
|
|
|
|
|
if (!isElbowArrow(selectedElement)) {
|
|
|
|
|
this.setState({
|
|
|
|
|
editingLinearElement: new LinearElementEditor(
|
|
|
|
|
selectedElement,
|
|
|
|
|
this.scene.getNonDeletedElementsMap(),
|
|
|
|
|
),
|
|
|
|
|
});
|
|
|
|
|
this.actionManager.executeAction(actionToggleLinearEditor);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -5432,15 +5433,12 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
if (
|
|
|
|
|
((event[KEYS.CTRL_OR_CMD] && isSimpleArrow(selectedLinearElement)) ||
|
|
|
|
|
isLineElement(selectedLinearElement)) &&
|
|
|
|
|
this.state.editingLinearElement?.elementId !== selectedLinearElement.id
|
|
|
|
|
(!this.state.selectedLinearElement?.isEditing ||
|
|
|
|
|
this.state.selectedLinearElement.elementId !==
|
|
|
|
|
selectedLinearElement.id)
|
|
|
|
|
) {
|
|
|
|
|
this.store.scheduleCapture();
|
|
|
|
|
this.setState({
|
|
|
|
|
editingLinearElement: new LinearElementEditor(
|
|
|
|
|
selectedLinearElement,
|
|
|
|
|
this.scene.getNonDeletedElementsMap(),
|
|
|
|
|
),
|
|
|
|
|
});
|
|
|
|
|
// Use the proper action to ensure immediate history capture
|
|
|
|
|
this.actionManager.executeAction(actionToggleLinearEditor);
|
|
|
|
|
return;
|
|
|
|
|
} else if (
|
|
|
|
|
this.state.selectedLinearElement &&
|
|
|
|
@@ -5505,8 +5503,8 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else if (
|
|
|
|
|
this.state.editingLinearElement &&
|
|
|
|
|
this.state.editingLinearElement.elementId ===
|
|
|
|
|
this.state.selectedLinearElement?.isEditing &&
|
|
|
|
|
this.state.selectedLinearElement.elementId ===
|
|
|
|
|
selectedLinearElement.id &&
|
|
|
|
|
isLineElement(selectedLinearElement)
|
|
|
|
|
) {
|
|
|
|
@@ -5561,7 +5559,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
|
|
|
|
|
// shouldn't edit/create text when inside line editor (often false positive)
|
|
|
|
|
|
|
|
|
|
if (!this.state.editingLinearElement) {
|
|
|
|
|
if (!this.state.selectedLinearElement?.isEditing) {
|
|
|
|
|
const container = this.getTextBindableContainerAtPosition(
|
|
|
|
|
sceneX,
|
|
|
|
|
sceneY,
|
|
|
|
@@ -5859,8 +5857,8 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
this.state.editingLinearElement &&
|
|
|
|
|
!this.state.editingLinearElement.isDragging
|
|
|
|
|
this.state.selectedLinearElement?.isEditing &&
|
|
|
|
|
!this.state.selectedLinearElement.isDragging
|
|
|
|
|
) {
|
|
|
|
|
const editingLinearElement = LinearElementEditor.handlePointerMove(
|
|
|
|
|
event,
|
|
|
|
@@ -5874,14 +5872,14 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
editingLinearElement &&
|
|
|
|
|
editingLinearElement !== this.state.editingLinearElement
|
|
|
|
|
editingLinearElement !== this.state.selectedLinearElement
|
|
|
|
|
) {
|
|
|
|
|
// Since we are reading from previous state which is not possible with
|
|
|
|
|
// automatic batching in React 18 hence using flush sync to synchronously
|
|
|
|
|
// update the state. Check https://github.com/excalidraw/excalidraw/pull/5508 for more details.
|
|
|
|
|
flushSync(() => {
|
|
|
|
|
this.setState({
|
|
|
|
|
editingLinearElement,
|
|
|
|
|
selectedLinearElement: editingLinearElement,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
@@ -6041,7 +6039,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
if (
|
|
|
|
|
selectedElements.length === 1 &&
|
|
|
|
|
!isOverScrollBar &&
|
|
|
|
|
!this.state.editingLinearElement
|
|
|
|
|
!this.state.selectedLinearElement?.isEditing
|
|
|
|
|
) {
|
|
|
|
|
// for linear elements, we'd like to prioritize point dragging over edge resizing
|
|
|
|
|
// therefore, we update and check hovered point index first
|
|
|
|
@@ -7045,7 +7043,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
selectedElements.length === 1 &&
|
|
|
|
|
!this.state.editingLinearElement &&
|
|
|
|
|
!this.state.selectedLinearElement?.isEditing &&
|
|
|
|
|
!isElbowArrow(selectedElements[0]) &&
|
|
|
|
|
!(
|
|
|
|
|
this.state.selectedLinearElement &&
|
|
|
|
@@ -7116,8 +7114,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (this.state.selectedLinearElement) {
|
|
|
|
|
const linearElementEditor =
|
|
|
|
|
this.state.editingLinearElement || this.state.selectedLinearElement;
|
|
|
|
|
const linearElementEditor = this.state.selectedLinearElement;
|
|
|
|
|
const ret = LinearElementEditor.handlePointerDown(
|
|
|
|
|
event,
|
|
|
|
|
this,
|
|
|
|
@@ -7131,10 +7128,6 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
}
|
|
|
|
|
if (ret.linearElementEditor) {
|
|
|
|
|
this.setState({ selectedLinearElement: ret.linearElementEditor });
|
|
|
|
|
|
|
|
|
|
if (this.state.editingLinearElement) {
|
|
|
|
|
this.setState({ editingLinearElement: ret.linearElementEditor });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (ret.didAddPoint) {
|
|
|
|
|
return true;
|
|
|
|
@@ -7235,11 +7228,11 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
this.clearSelection(hitElement);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.state.editingLinearElement) {
|
|
|
|
|
if (this.state.selectedLinearElement?.isEditing) {
|
|
|
|
|
this.setState({
|
|
|
|
|
selectedElementIds: makeNextSelectedElementIds(
|
|
|
|
|
{
|
|
|
|
|
[this.state.editingLinearElement.elementId]: true,
|
|
|
|
|
[this.state.selectedLinearElement.elementId]: true,
|
|
|
|
|
},
|
|
|
|
|
this.state,
|
|
|
|
|
),
|
|
|
|
@@ -8182,8 +8175,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
const elementsMap = this.scene.getNonDeletedElementsMap();
|
|
|
|
|
|
|
|
|
|
if (this.state.selectedLinearElement) {
|
|
|
|
|
const linearElementEditor =
|
|
|
|
|
this.state.editingLinearElement || this.state.selectedLinearElement;
|
|
|
|
|
const linearElementEditor = this.state.selectedLinearElement;
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
LinearElementEditor.shouldAddMidpoint(
|
|
|
|
@@ -8219,16 +8211,6 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (this.state.editingLinearElement) {
|
|
|
|
|
this.setState({
|
|
|
|
|
editingLinearElement: {
|
|
|
|
|
...this.state.editingLinearElement,
|
|
|
|
|
pointerDownState: ret.pointerDownState,
|
|
|
|
|
selectedPointsIndices: ret.selectedPointsIndices,
|
|
|
|
|
segmentMidPointHoveredCoords: null,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
@@ -8262,9 +8244,9 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const isSelectingPointsInLineEditor =
|
|
|
|
|
this.state.editingLinearElement &&
|
|
|
|
|
this.state.selectedLinearElement?.isEditing &&
|
|
|
|
|
event.shiftKey &&
|
|
|
|
|
this.state.editingLinearElement.elementId ===
|
|
|
|
|
this.state.selectedLinearElement.elementId ===
|
|
|
|
|
pointerDownState.hit.element?.id;
|
|
|
|
|
if (
|
|
|
|
|
(hasHitASelectedElement ||
|
|
|
|
@@ -8751,7 +8733,7 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
const elements = this.scene.getNonDeletedElements();
|
|
|
|
|
|
|
|
|
|
// box-select line editor points
|
|
|
|
|
if (this.state.editingLinearElement) {
|
|
|
|
|
if (this.state.selectedLinearElement?.isEditing) {
|
|
|
|
|
LinearElementEditor.handleBoxSelection(
|
|
|
|
|
event,
|
|
|
|
|
this.state,
|
|
|
|
@@ -8994,23 +8976,23 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
|
|
|
|
|
// Handle end of dragging a point of a linear element, might close a loop
|
|
|
|
|
// and sets binding element
|
|
|
|
|
if (this.state.editingLinearElement) {
|
|
|
|
|
if (this.state.selectedLinearElement?.isEditing) {
|
|
|
|
|
if (
|
|
|
|
|
!pointerDownState.boxSelection.hasOccurred &&
|
|
|
|
|
pointerDownState.hit?.element?.id !==
|
|
|
|
|
this.state.editingLinearElement.elementId
|
|
|
|
|
this.state.selectedLinearElement.elementId
|
|
|
|
|
) {
|
|
|
|
|
this.actionManager.executeAction(actionFinalize);
|
|
|
|
|
} else {
|
|
|
|
|
const editingLinearElement = LinearElementEditor.handlePointerUp(
|
|
|
|
|
childEvent,
|
|
|
|
|
this.state.editingLinearElement,
|
|
|
|
|
this.state.selectedLinearElement,
|
|
|
|
|
this.state,
|
|
|
|
|
this.scene,
|
|
|
|
|
);
|
|
|
|
|
if (editingLinearElement !== this.state.editingLinearElement) {
|
|
|
|
|
if (editingLinearElement !== this.state.selectedLinearElement) {
|
|
|
|
|
this.setState({
|
|
|
|
|
editingLinearElement,
|
|
|
|
|
selectedLinearElement: editingLinearElement,
|
|
|
|
|
suggestedBindings: [],
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
@@ -9523,14 +9505,17 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
!pointerDownState.hit.wasAddedToSelection &&
|
|
|
|
|
// if we're editing a line, pointerup shouldn't switch selection if
|
|
|
|
|
// box selected
|
|
|
|
|
(!this.state.editingLinearElement ||
|
|
|
|
|
(!this.state.selectedLinearElement?.isEditing ||
|
|
|
|
|
!pointerDownState.boxSelection.hasOccurred) &&
|
|
|
|
|
// hitElement can be set when alt + ctrl to toggle lasso and we will
|
|
|
|
|
// just respect the selected elements from lasso instead
|
|
|
|
|
this.state.activeTool.type !== "lasso"
|
|
|
|
|
) {
|
|
|
|
|
// when inside line editor, shift selects points instead
|
|
|
|
|
if (childEvent.shiftKey && !this.state.editingLinearElement) {
|
|
|
|
|
if (
|
|
|
|
|
childEvent.shiftKey &&
|
|
|
|
|
!this.state.selectedLinearElement?.isEditing
|
|
|
|
|
) {
|
|
|
|
|
if (this.state.selectedElementIds[hitElement.id]) {
|
|
|
|
|
if (isSelectedViaGroup(this.state, hitElement)) {
|
|
|
|
|
this.setState((_prevState) => {
|
|
|
|
@@ -9708,8 +9693,9 @@ class App extends React.Component<AppProps, AppState> {
|
|
|
|
|
(!hitElement &&
|
|
|
|
|
pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements))
|
|
|
|
|
) {
|
|
|
|
|
if (this.state.editingLinearElement) {
|
|
|
|
|
this.setState({ editingLinearElement: null });
|
|
|
|
|
if (this.state.selectedLinearElement?.isEditing) {
|
|
|
|
|
// Exit editing mode but keep the element selected
|
|
|
|
|
this.actionManager.executeAction(actionToggleLinearEditor);
|
|
|
|
|
} else {
|
|
|
|
|
// Deselect selected elements
|
|
|
|
|
this.setState({
|
|
|
|
|