mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-04-14 16:40:58 -04:00
fix: make LinearElementEditor independent of scene (#7670)
* fix: make LinearElementEditor independent of scene * more fixes * pass elements and elementsMap to maybeBindBindableElement,getHoveredElementForBinding,bindingBorderTest,getElligibleElementsForBindableElementAndWhere,isLinearElementEligibleForNewBindingByBindable * replace `ElementsMap` with `NonDeletedSceneElementsMap` & remove unused params * fix lint --------- Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
This commit is contained in:
parent
47f87f4ecb
commit
9013c84524
13 changed files with 172 additions and 123 deletions
|
@ -107,7 +107,7 @@ export const actionCut = register({
|
||||||
trackEvent: { category: "element" },
|
trackEvent: { category: "element" },
|
||||||
perform: (elements, appState, event: ClipboardEvent | null, app) => {
|
perform: (elements, appState, event: ClipboardEvent | null, app) => {
|
||||||
actionCopy.perform(elements, appState, event, app);
|
actionCopy.perform(elements, appState, event, app);
|
||||||
return actionDeleteSelected.perform(elements, appState);
|
return actionDeleteSelected.perform(elements, appState, null, app);
|
||||||
},
|
},
|
||||||
contextItemLabel: "labels.cut",
|
contextItemLabel: "labels.cut",
|
||||||
keyTest: (event) => event[KEYS.CTRL_OR_CMD] && event.key === KEYS.X,
|
keyTest: (event) => event[KEYS.CTRL_OR_CMD] && event.key === KEYS.X,
|
||||||
|
|
|
@ -73,7 +73,7 @@ const handleGroupEditingState = (
|
||||||
export const actionDeleteSelected = register({
|
export const actionDeleteSelected = register({
|
||||||
name: "deleteSelectedElements",
|
name: "deleteSelectedElements",
|
||||||
trackEvent: { category: "element", action: "delete" },
|
trackEvent: { category: "element", action: "delete" },
|
||||||
perform: (elements, appState) => {
|
perform: (elements, appState, formData, app) => {
|
||||||
if (appState.editingLinearElement) {
|
if (appState.editingLinearElement) {
|
||||||
const {
|
const {
|
||||||
elementId,
|
elementId,
|
||||||
|
@ -81,7 +81,8 @@ export const actionDeleteSelected = register({
|
||||||
startBindingElement,
|
startBindingElement,
|
||||||
endBindingElement,
|
endBindingElement,
|
||||||
} = appState.editingLinearElement;
|
} = appState.editingLinearElement;
|
||||||
const element = LinearElementEditor.getElement(elementId);
|
const elementsMap = app.scene.getNonDeletedElementsMap();
|
||||||
|
const element = LinearElementEditor.getElement(elementId, elementsMap);
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,10 +35,14 @@ import {
|
||||||
export const actionDuplicateSelection = register({
|
export const actionDuplicateSelection = register({
|
||||||
name: "duplicateSelection",
|
name: "duplicateSelection",
|
||||||
trackEvent: { category: "element" },
|
trackEvent: { category: "element" },
|
||||||
perform: (elements, appState) => {
|
perform: (elements, appState, formData, app) => {
|
||||||
|
const elementsMap = app.scene.getNonDeletedElementsMap();
|
||||||
// duplicate selected point(s) if editing a line
|
// duplicate selected point(s) if editing a line
|
||||||
if (appState.editingLinearElement) {
|
if (appState.editingLinearElement) {
|
||||||
const ret = LinearElementEditor.duplicateSelectedPoints(appState);
|
const ret = LinearElementEditor.duplicateSelectedPoints(
|
||||||
|
appState,
|
||||||
|
elementsMap,
|
||||||
|
);
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -26,12 +26,12 @@ export const actionFinalize = register({
|
||||||
_,
|
_,
|
||||||
{ interactiveCanvas, focusContainer, scene },
|
{ interactiveCanvas, focusContainer, scene },
|
||||||
) => {
|
) => {
|
||||||
const elementsMap = arrayToMap(elements);
|
const elementsMap = scene.getNonDeletedElementsMap();
|
||||||
|
|
||||||
if (appState.editingLinearElement) {
|
if (appState.editingLinearElement) {
|
||||||
const { elementId, startBindingElement, endBindingElement } =
|
const { elementId, startBindingElement, endBindingElement } =
|
||||||
appState.editingLinearElement;
|
appState.editingLinearElement;
|
||||||
const element = LinearElementEditor.getElement(elementId);
|
const element = LinearElementEditor.getElement(elementId, elementsMap);
|
||||||
|
|
||||||
if (element) {
|
if (element) {
|
||||||
if (isBindingElement(element)) {
|
if (isBindingElement(element)) {
|
||||||
|
@ -191,7 +191,7 @@ export const actionFinalize = register({
|
||||||
// To select the linear element when user has finished mutipoint editing
|
// To select the linear element when user has finished mutipoint editing
|
||||||
selectedLinearElement:
|
selectedLinearElement:
|
||||||
multiPointElement && isLinearElement(multiPointElement)
|
multiPointElement && isLinearElement(multiPointElement)
|
||||||
? new LinearElementEditor(multiPointElement, scene)
|
? new LinearElementEditor(multiPointElement)
|
||||||
: appState.selectedLinearElement,
|
: appState.selectedLinearElement,
|
||||||
pendingImageElementId: null,
|
pendingImageElementId: null,
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { getNonDeletedElements } from "../element";
|
||||||
import {
|
import {
|
||||||
ExcalidrawElement,
|
ExcalidrawElement,
|
||||||
NonDeleted,
|
NonDeleted,
|
||||||
NonDeletedElementsMap,
|
|
||||||
NonDeletedSceneElementsMap,
|
NonDeletedSceneElementsMap,
|
||||||
} from "../element/types";
|
} from "../element/types";
|
||||||
import { resizeMultipleElements } from "../element/resizeElements";
|
import { resizeMultipleElements } from "../element/resizeElements";
|
||||||
|
@ -68,7 +67,7 @@ export const actionFlipVertical = register({
|
||||||
|
|
||||||
const flipSelectedElements = (
|
const flipSelectedElements = (
|
||||||
elements: readonly ExcalidrawElement[],
|
elements: readonly ExcalidrawElement[],
|
||||||
elementsMap: NonDeletedElementsMap | NonDeletedSceneElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
appState: Readonly<AppState>,
|
appState: Readonly<AppState>,
|
||||||
flipDirection: "horizontal" | "vertical",
|
flipDirection: "horizontal" | "vertical",
|
||||||
) => {
|
) => {
|
||||||
|
@ -83,6 +82,7 @@ const flipSelectedElements = (
|
||||||
|
|
||||||
const updatedElements = flipElements(
|
const updatedElements = flipElements(
|
||||||
selectedElements,
|
selectedElements,
|
||||||
|
elements,
|
||||||
elementsMap,
|
elementsMap,
|
||||||
appState,
|
appState,
|
||||||
flipDirection,
|
flipDirection,
|
||||||
|
@ -97,7 +97,8 @@ const flipSelectedElements = (
|
||||||
|
|
||||||
const flipElements = (
|
const flipElements = (
|
||||||
selectedElements: NonDeleted<ExcalidrawElement>[],
|
selectedElements: NonDeleted<ExcalidrawElement>[],
|
||||||
elementsMap: NonDeletedElementsMap | NonDeletedSceneElementsMap,
|
elements: readonly ExcalidrawElement[],
|
||||||
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
flipDirection: "horizontal" | "vertical",
|
flipDirection: "horizontal" | "vertical",
|
||||||
): ExcalidrawElement[] => {
|
): ExcalidrawElement[] => {
|
||||||
|
@ -113,9 +114,9 @@ const flipElements = (
|
||||||
flipDirection === "horizontal" ? minY : maxY,
|
flipDirection === "horizontal" ? minY : maxY,
|
||||||
);
|
);
|
||||||
|
|
||||||
(isBindingEnabled(appState)
|
isBindingEnabled(appState)
|
||||||
? bindOrUnbindSelectedElements
|
? bindOrUnbindSelectedElements(selectedElements, elements, elementsMap)
|
||||||
: unbindLinearElements)(selectedElements, elementsMap);
|
: unbindLinearElements(selectedElements, elementsMap);
|
||||||
|
|
||||||
return selectedElements;
|
return selectedElements;
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,7 +24,7 @@ export const actionToggleLinearEditor = register({
|
||||||
const editingLinearElement =
|
const editingLinearElement =
|
||||||
appState.editingLinearElement?.elementId === selectedElement.id
|
appState.editingLinearElement?.elementId === selectedElement.id
|
||||||
? null
|
? null
|
||||||
: new LinearElementEditor(selectedElement, app.scene);
|
: new LinearElementEditor(selectedElement);
|
||||||
return {
|
return {
|
||||||
appState: {
|
appState: {
|
||||||
...appState,
|
...appState,
|
||||||
|
|
|
@ -43,7 +43,7 @@ export const actionSelectAll = register({
|
||||||
// single linear element selected
|
// single linear element selected
|
||||||
Object.keys(selectedElementIds).length === 1 &&
|
Object.keys(selectedElementIds).length === 1 &&
|
||||||
isLinearElement(elements[0])
|
isLinearElement(elements[0])
|
||||||
? new LinearElementEditor(elements[0], app.scene)
|
? new LinearElementEditor(elements[0])
|
||||||
: null,
|
: null,
|
||||||
},
|
},
|
||||||
commitToHistory: true,
|
commitToHistory: true,
|
||||||
|
|
|
@ -2603,7 +2603,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
componentDidUpdate(prevProps: AppProps, prevState: AppState) {
|
componentDidUpdate(prevProps: AppProps, prevState: AppState) {
|
||||||
this.updateEmbeddables();
|
this.updateEmbeddables();
|
||||||
const elements = this.scene.getElementsIncludingDeleted();
|
const elements = this.scene.getElementsIncludingDeleted();
|
||||||
const elementsMap = this.scene.getElementsMapIncludingDeleted();
|
const elementsMap = this.scene.getNonDeletedElementsMap();
|
||||||
|
|
||||||
if (!this.state.showWelcomeScreen && !elements.length) {
|
if (!this.state.showWelcomeScreen && !elements.length) {
|
||||||
this.setState({ showWelcomeScreen: true });
|
this.setState({ showWelcomeScreen: true });
|
||||||
|
@ -3860,7 +3860,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
this.setState({
|
this.setState({
|
||||||
editingLinearElement: new LinearElementEditor(
|
editingLinearElement: new LinearElementEditor(
|
||||||
selectedElement,
|
selectedElement,
|
||||||
this.scene,
|
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -4013,7 +4012,11 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
const selectedElements = this.scene.getSelectedElements(this.state);
|
const selectedElements = this.scene.getSelectedElements(this.state);
|
||||||
const elementsMap = this.scene.getNonDeletedElementsMap();
|
const elementsMap = this.scene.getNonDeletedElementsMap();
|
||||||
isBindingEnabled(this.state)
|
isBindingEnabled(this.state)
|
||||||
? bindOrUnbindSelectedElements(selectedElements, elementsMap)
|
? bindOrUnbindSelectedElements(
|
||||||
|
selectedElements,
|
||||||
|
this.scene.getNonDeletedElements(),
|
||||||
|
elementsMap,
|
||||||
|
)
|
||||||
: unbindLinearElements(selectedElements, elementsMap);
|
: unbindLinearElements(selectedElements, elementsMap);
|
||||||
this.setState({ suggestedBindings: [] });
|
this.setState({ suggestedBindings: [] });
|
||||||
}
|
}
|
||||||
|
@ -4578,10 +4581,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
) {
|
) {
|
||||||
this.history.resumeRecording();
|
this.history.resumeRecording();
|
||||||
this.setState({
|
this.setState({
|
||||||
editingLinearElement: new LinearElementEditor(
|
editingLinearElement: new LinearElementEditor(selectedElements[0]),
|
||||||
selectedElements[0],
|
|
||||||
this.scene,
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
} else if (
|
} else if (
|
||||||
|
@ -5305,10 +5305,12 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
scenePointerX: number,
|
scenePointerX: number,
|
||||||
scenePointerY: number,
|
scenePointerY: number,
|
||||||
) {
|
) {
|
||||||
|
const elementsMap = this.scene.getNonDeletedElementsMap();
|
||||||
|
|
||||||
const element = LinearElementEditor.getElement(
|
const element = LinearElementEditor.getElement(
|
||||||
linearElementEditor.elementId,
|
linearElementEditor.elementId,
|
||||||
|
elementsMap,
|
||||||
);
|
);
|
||||||
const elementsMap = this.scene.getNonDeletedElementsMap();
|
|
||||||
const boundTextElement = getBoundTextElement(element, elementsMap);
|
const boundTextElement = getBoundTextElement(element, elementsMap);
|
||||||
|
|
||||||
if (!element) {
|
if (!element) {
|
||||||
|
@ -6122,7 +6124,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
this.history,
|
this.history,
|
||||||
pointerDownState.origin,
|
pointerDownState.origin,
|
||||||
linearElementEditor,
|
linearElementEditor,
|
||||||
this.scene.getNonDeletedElementsMap(),
|
this.scene.getNonDeletedElements(),
|
||||||
|
elementsMap,
|
||||||
);
|
);
|
||||||
if (ret.hitElement) {
|
if (ret.hitElement) {
|
||||||
pointerDownState.hit.element = ret.hitElement;
|
pointerDownState.hit.element = ret.hitElement;
|
||||||
|
@ -6459,7 +6462,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
|
|
||||||
const boundElement = getHoveredElementForBinding(
|
const boundElement = getHoveredElementForBinding(
|
||||||
pointerDownState.origin,
|
pointerDownState.origin,
|
||||||
this.scene,
|
this.scene.getNonDeletedElements(),
|
||||||
|
this.scene.getNonDeletedElementsMap(),
|
||||||
);
|
);
|
||||||
this.scene.addNewElement(element);
|
this.scene.addNewElement(element);
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -6727,7 +6731,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
});
|
});
|
||||||
const boundElement = getHoveredElementForBinding(
|
const boundElement = getHoveredElementForBinding(
|
||||||
pointerDownState.origin,
|
pointerDownState.origin,
|
||||||
this.scene,
|
this.scene.getNonDeletedElements(),
|
||||||
|
this.scene.getNonDeletedElementsMap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.scene.addNewElement(element);
|
this.scene.addNewElement(element);
|
||||||
|
@ -6997,6 +7002,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const elementsMap = this.scene.getNonDeletedElementsMap();
|
||||||
|
|
||||||
if (this.state.selectedLinearElement) {
|
if (this.state.selectedLinearElement) {
|
||||||
const linearElementEditor =
|
const linearElementEditor =
|
||||||
|
@ -7007,6 +7013,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
this.state.selectedLinearElement,
|
this.state.selectedLinearElement,
|
||||||
pointerCoords,
|
pointerCoords,
|
||||||
this.state,
|
this.state,
|
||||||
|
elementsMap,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
const ret = LinearElementEditor.addMidpoint(
|
const ret = LinearElementEditor.addMidpoint(
|
||||||
|
@ -7014,7 +7021,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
pointerCoords,
|
pointerCoords,
|
||||||
this.state,
|
this.state,
|
||||||
!event[KEYS.CTRL_OR_CMD],
|
!event[KEYS.CTRL_OR_CMD],
|
||||||
this.scene.getNonDeletedElementsMap(),
|
elementsMap,
|
||||||
);
|
);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
return;
|
return;
|
||||||
|
@ -7435,10 +7442,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
selectedLinearElement:
|
selectedLinearElement:
|
||||||
elementsWithinSelection.length === 1 &&
|
elementsWithinSelection.length === 1 &&
|
||||||
isLinearElement(elementsWithinSelection[0])
|
isLinearElement(elementsWithinSelection[0])
|
||||||
? new LinearElementEditor(
|
? new LinearElementEditor(elementsWithinSelection[0])
|
||||||
elementsWithinSelection[0],
|
|
||||||
this.scene,
|
|
||||||
)
|
|
||||||
: null,
|
: null,
|
||||||
showHyperlinkPopup:
|
showHyperlinkPopup:
|
||||||
elementsWithinSelection.length === 1 &&
|
elementsWithinSelection.length === 1 &&
|
||||||
|
@ -7539,6 +7543,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
childEvent,
|
childEvent,
|
||||||
this.state.editingLinearElement,
|
this.state.editingLinearElement,
|
||||||
this.state,
|
this.state,
|
||||||
|
this.scene.getNonDeletedElements(),
|
||||||
elementsMap,
|
elementsMap,
|
||||||
);
|
);
|
||||||
if (editingLinearElement !== this.state.editingLinearElement) {
|
if (editingLinearElement !== this.state.editingLinearElement) {
|
||||||
|
@ -7563,6 +7568,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
childEvent,
|
childEvent,
|
||||||
this.state.selectedLinearElement,
|
this.state.selectedLinearElement,
|
||||||
this.state,
|
this.state,
|
||||||
|
this.scene.getNonDeletedElements(),
|
||||||
elementsMap,
|
elementsMap,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -7732,10 +7738,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
},
|
},
|
||||||
prevState,
|
prevState,
|
||||||
),
|
),
|
||||||
selectedLinearElement: new LinearElementEditor(
|
selectedLinearElement: new LinearElementEditor(draggingElement),
|
||||||
draggingElement,
|
|
||||||
this.scene,
|
|
||||||
),
|
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
this.setState((prevState) => ({
|
this.setState((prevState) => ({
|
||||||
|
@ -7975,10 +7978,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
// the one we've hit
|
// the one we've hit
|
||||||
if (selectedELements.length === 1) {
|
if (selectedELements.length === 1) {
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedLinearElement: new LinearElementEditor(
|
selectedLinearElement: new LinearElementEditor(hitElement),
|
||||||
hitElement,
|
|
||||||
this.scene,
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8091,10 +8091,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
selectedLinearElement:
|
selectedLinearElement:
|
||||||
newSelectedElements.length === 1 &&
|
newSelectedElements.length === 1 &&
|
||||||
isLinearElement(newSelectedElements[0])
|
isLinearElement(newSelectedElements[0])
|
||||||
? new LinearElementEditor(
|
? new LinearElementEditor(newSelectedElements[0])
|
||||||
newSelectedElements[0],
|
|
||||||
this.scene,
|
|
||||||
)
|
|
||||||
: prevState.selectedLinearElement,
|
: prevState.selectedLinearElement,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -8168,7 +8165,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
// Don't set `selectedLinearElement` if its same as the hitElement, this is mainly to prevent resetting the `hoverPointIndex` to -1.
|
// Don't set `selectedLinearElement` if its same as the hitElement, this is mainly to prevent resetting the `hoverPointIndex` to -1.
|
||||||
// Future we should update the API to take care of setting the correct `hoverPointIndex` when initialized
|
// Future we should update the API to take care of setting the correct `hoverPointIndex` when initialized
|
||||||
prevState.selectedLinearElement?.elementId !== hitElement.id
|
prevState.selectedLinearElement?.elementId !== hitElement.id
|
||||||
? new LinearElementEditor(hitElement, this.scene)
|
? new LinearElementEditor(hitElement)
|
||||||
: prevState.selectedLinearElement,
|
: prevState.selectedLinearElement,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -8232,12 +8229,16 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pointerDownState.drag.hasOccurred || isResizing || isRotating) {
|
if (pointerDownState.drag.hasOccurred || isResizing || isRotating) {
|
||||||
(isBindingEnabled(this.state)
|
isBindingEnabled(this.state)
|
||||||
? bindOrUnbindSelectedElements
|
? bindOrUnbindSelectedElements(
|
||||||
: unbindLinearElements)(
|
this.scene.getSelectedElements(this.state),
|
||||||
this.scene.getSelectedElements(this.state),
|
this.scene.getNonDeletedElements(),
|
||||||
elementsMap,
|
elementsMap,
|
||||||
);
|
)
|
||||||
|
: unbindLinearElements(
|
||||||
|
this.scene.getSelectedElements(this.state),
|
||||||
|
elementsMap,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeTool.type === "laser") {
|
if (activeTool.type === "laser") {
|
||||||
|
@ -8714,7 +8715,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
}): void => {
|
}): void => {
|
||||||
const hoveredBindableElement = getHoveredElementForBinding(
|
const hoveredBindableElement = getHoveredElementForBinding(
|
||||||
pointerCoords,
|
pointerCoords,
|
||||||
this.scene,
|
this.scene.getNonDeletedElements(),
|
||||||
|
this.scene.getNonDeletedElementsMap(),
|
||||||
);
|
);
|
||||||
this.setState({
|
this.setState({
|
||||||
suggestedBindings:
|
suggestedBindings:
|
||||||
|
@ -8741,7 +8743,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
(acc: NonDeleted<ExcalidrawBindableElement>[], coords) => {
|
(acc: NonDeleted<ExcalidrawBindableElement>[], coords) => {
|
||||||
const hoveredBindableElement = getHoveredElementForBinding(
|
const hoveredBindableElement = getHoveredElementForBinding(
|
||||||
coords,
|
coords,
|
||||||
this.scene,
|
this.scene.getNonDeletedElements(),
|
||||||
|
this.scene.getNonDeletedElementsMap(),
|
||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
hoveredBindableElement != null &&
|
hoveredBindableElement != null &&
|
||||||
|
@ -8769,6 +8772,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
}
|
}
|
||||||
const suggestedBindings = getEligibleElementsForBinding(
|
const suggestedBindings = getEligibleElementsForBinding(
|
||||||
selectedElements,
|
selectedElements,
|
||||||
|
this.scene.getNonDeletedElements(),
|
||||||
this.scene.getNonDeletedElementsMap(),
|
this.scene.getNonDeletedElementsMap(),
|
||||||
);
|
);
|
||||||
this.setState({ suggestedBindings });
|
this.setState({ suggestedBindings });
|
||||||
|
@ -9037,7 +9041,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
this,
|
this,
|
||||||
),
|
),
|
||||||
selectedLinearElement: isLinearElement(element)
|
selectedLinearElement: isLinearElement(element)
|
||||||
? new LinearElementEditor(element, this.scene)
|
? new LinearElementEditor(element)
|
||||||
: null,
|
: null,
|
||||||
}
|
}
|
||||||
: this.state),
|
: this.state),
|
||||||
|
|
|
@ -39,11 +39,12 @@ import {
|
||||||
ExcalidrawTextElement,
|
ExcalidrawTextElement,
|
||||||
FileId,
|
FileId,
|
||||||
FontFamilyValues,
|
FontFamilyValues,
|
||||||
|
NonDeletedSceneElementsMap,
|
||||||
TextAlign,
|
TextAlign,
|
||||||
VerticalAlign,
|
VerticalAlign,
|
||||||
} from "../element/types";
|
} from "../element/types";
|
||||||
import { MarkOptional } from "../utility-types";
|
import { MarkOptional } from "../utility-types";
|
||||||
import { arrayToMap, assertNever, cloneJSON, getFontString } from "../utils";
|
import { assertNever, cloneJSON, getFontString, toBrandedType } from "../utils";
|
||||||
import { getSizeFromPoints } from "../points";
|
import { getSizeFromPoints } from "../points";
|
||||||
import { randomId } from "../random";
|
import { randomId } from "../random";
|
||||||
|
|
||||||
|
@ -231,7 +232,7 @@ const bindLinearElementToElement = (
|
||||||
start: ValidLinearElement["start"],
|
start: ValidLinearElement["start"],
|
||||||
end: ValidLinearElement["end"],
|
end: ValidLinearElement["end"],
|
||||||
elementStore: ElementStore,
|
elementStore: ElementStore,
|
||||||
elementsMap: ElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
): {
|
): {
|
||||||
linearElement: ExcalidrawLinearElement;
|
linearElement: ExcalidrawLinearElement;
|
||||||
startBoundElement?: ExcalidrawElement;
|
startBoundElement?: ExcalidrawElement;
|
||||||
|
@ -460,6 +461,10 @@ class ElementStore {
|
||||||
return Array.from(this.excalidrawElements.values());
|
return Array.from(this.excalidrawElements.values());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getElementsMap = () => {
|
||||||
|
return toBrandedType<NonDeletedSceneElementsMap>(this.excalidrawElements);
|
||||||
|
};
|
||||||
|
|
||||||
getElement = (id: string) => {
|
getElement = (id: string) => {
|
||||||
return this.excalidrawElements.get(id);
|
return this.excalidrawElements.get(id);
|
||||||
};
|
};
|
||||||
|
@ -615,7 +620,7 @@ export const convertToExcalidrawElements = (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const elementsMap = arrayToMap(elementStore.getElements());
|
const elementsMap = elementStore.getElementsMap();
|
||||||
// Add labels and arrow bindings
|
// Add labels and arrow bindings
|
||||||
for (const [id, element] of elementsWithIds) {
|
for (const [id, element] of elementsWithIds) {
|
||||||
const excalidrawElement = elementStore.getElement(id)!;
|
const excalidrawElement = elementStore.getElement(id)!;
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
PointBinding,
|
PointBinding,
|
||||||
ExcalidrawElement,
|
ExcalidrawElement,
|
||||||
ElementsMap,
|
ElementsMap,
|
||||||
|
NonDeletedSceneElementsMap,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import { getElementAtPosition } from "../scene";
|
import { getElementAtPosition } from "../scene";
|
||||||
import { AppState } from "../types";
|
import { AppState } from "../types";
|
||||||
|
@ -67,7 +68,7 @@ export const bindOrUnbindLinearElement = (
|
||||||
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
||||||
startBindingElement: ExcalidrawBindableElement | null | "keep",
|
startBindingElement: ExcalidrawBindableElement | null | "keep",
|
||||||
endBindingElement: ExcalidrawBindableElement | null | "keep",
|
endBindingElement: ExcalidrawBindableElement | null | "keep",
|
||||||
elementsMap: ElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
): void => {
|
): void => {
|
||||||
const boundToElementIds: Set<ExcalidrawBindableElement["id"]> = new Set();
|
const boundToElementIds: Set<ExcalidrawBindableElement["id"]> = new Set();
|
||||||
const unboundFromElementIds: Set<ExcalidrawBindableElement["id"]> = new Set();
|
const unboundFromElementIds: Set<ExcalidrawBindableElement["id"]> = new Set();
|
||||||
|
@ -115,7 +116,7 @@ const bindOrUnbindLinearElementEdge = (
|
||||||
boundToElementIds: Set<ExcalidrawBindableElement["id"]>,
|
boundToElementIds: Set<ExcalidrawBindableElement["id"]>,
|
||||||
// Is mutated
|
// Is mutated
|
||||||
unboundFromElementIds: Set<ExcalidrawBindableElement["id"]>,
|
unboundFromElementIds: Set<ExcalidrawBindableElement["id"]>,
|
||||||
elementsMap: ElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
): void => {
|
): void => {
|
||||||
if (bindableElement !== "keep") {
|
if (bindableElement !== "keep") {
|
||||||
if (bindableElement != null) {
|
if (bindableElement != null) {
|
||||||
|
@ -151,7 +152,8 @@ const bindOrUnbindLinearElementEdge = (
|
||||||
|
|
||||||
export const bindOrUnbindSelectedElements = (
|
export const bindOrUnbindSelectedElements = (
|
||||||
selectedElements: NonDeleted<ExcalidrawElement>[],
|
selectedElements: NonDeleted<ExcalidrawElement>[],
|
||||||
elementsMap: ElementsMap,
|
elements: readonly ExcalidrawElement[],
|
||||||
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
): void => {
|
): void => {
|
||||||
selectedElements.forEach((selectedElement) => {
|
selectedElements.forEach((selectedElement) => {
|
||||||
if (isBindingElement(selectedElement)) {
|
if (isBindingElement(selectedElement)) {
|
||||||
|
@ -160,11 +162,13 @@ export const bindOrUnbindSelectedElements = (
|
||||||
getElligibleElementForBindingElement(
|
getElligibleElementForBindingElement(
|
||||||
selectedElement,
|
selectedElement,
|
||||||
"start",
|
"start",
|
||||||
|
elements,
|
||||||
elementsMap,
|
elementsMap,
|
||||||
),
|
),
|
||||||
getElligibleElementForBindingElement(
|
getElligibleElementForBindingElement(
|
||||||
selectedElement,
|
selectedElement,
|
||||||
"end",
|
"end",
|
||||||
|
elements,
|
||||||
elementsMap,
|
elementsMap,
|
||||||
),
|
),
|
||||||
elementsMap,
|
elementsMap,
|
||||||
|
@ -177,16 +181,18 @@ export const bindOrUnbindSelectedElements = (
|
||||||
|
|
||||||
const maybeBindBindableElement = (
|
const maybeBindBindableElement = (
|
||||||
bindableElement: NonDeleted<ExcalidrawBindableElement>,
|
bindableElement: NonDeleted<ExcalidrawBindableElement>,
|
||||||
elementsMap: ElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
): void => {
|
): void => {
|
||||||
getElligibleElementsForBindableElementAndWhere(bindableElement).forEach(
|
getElligibleElementsForBindableElementAndWhere(
|
||||||
([linearElement, where]) =>
|
bindableElement,
|
||||||
bindOrUnbindLinearElement(
|
elementsMap,
|
||||||
linearElement,
|
).forEach(([linearElement, where]) =>
|
||||||
where === "end" ? "keep" : bindableElement,
|
bindOrUnbindLinearElement(
|
||||||
where === "start" ? "keep" : bindableElement,
|
linearElement,
|
||||||
elementsMap,
|
where === "end" ? "keep" : bindableElement,
|
||||||
),
|
where === "start" ? "keep" : bindableElement,
|
||||||
|
elementsMap,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -195,7 +201,7 @@ export const maybeBindLinearElement = (
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
scene: Scene,
|
scene: Scene,
|
||||||
pointerCoords: { x: number; y: number },
|
pointerCoords: { x: number; y: number },
|
||||||
elementsMap: ElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
): void => {
|
): void => {
|
||||||
if (appState.startBoundElement != null) {
|
if (appState.startBoundElement != null) {
|
||||||
bindLinearElement(
|
bindLinearElement(
|
||||||
|
@ -205,7 +211,11 @@ export const maybeBindLinearElement = (
|
||||||
elementsMap,
|
elementsMap,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const hoveredElement = getHoveredElementForBinding(pointerCoords, scene);
|
const hoveredElement = getHoveredElementForBinding(
|
||||||
|
pointerCoords,
|
||||||
|
scene.getNonDeletedElements(),
|
||||||
|
elementsMap,
|
||||||
|
);
|
||||||
if (
|
if (
|
||||||
hoveredElement != null &&
|
hoveredElement != null &&
|
||||||
!isLinearElementSimpleAndAlreadyBoundOnOppositeEdge(
|
!isLinearElementSimpleAndAlreadyBoundOnOppositeEdge(
|
||||||
|
@ -222,7 +232,7 @@ export const bindLinearElement = (
|
||||||
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
||||||
hoveredElement: ExcalidrawBindableElement,
|
hoveredElement: ExcalidrawBindableElement,
|
||||||
startOrEnd: "start" | "end",
|
startOrEnd: "start" | "end",
|
||||||
elementsMap: ElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
): void => {
|
): void => {
|
||||||
mutateElement(linearElement, {
|
mutateElement(linearElement, {
|
||||||
[startOrEnd === "start" ? "startBinding" : "endBinding"]: {
|
[startOrEnd === "start" ? "startBinding" : "endBinding"]: {
|
||||||
|
@ -274,7 +284,7 @@ export const isLinearElementSimpleAndAlreadyBound = (
|
||||||
|
|
||||||
export const unbindLinearElements = (
|
export const unbindLinearElements = (
|
||||||
elements: NonDeleted<ExcalidrawElement>[],
|
elements: NonDeleted<ExcalidrawElement>[],
|
||||||
elementsMap: ElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
): void => {
|
): void => {
|
||||||
elements.forEach((element) => {
|
elements.forEach((element) => {
|
||||||
if (isBindingElement(element)) {
|
if (isBindingElement(element)) {
|
||||||
|
@ -301,17 +311,14 @@ export const getHoveredElementForBinding = (
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
},
|
},
|
||||||
scene: Scene,
|
elements: readonly NonDeletedExcalidrawElement[],
|
||||||
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
): NonDeleted<ExcalidrawBindableElement> | null => {
|
): NonDeleted<ExcalidrawBindableElement> | null => {
|
||||||
const hoveredElement = getElementAtPosition(
|
const hoveredElement = getElementAtPosition(
|
||||||
scene.getNonDeletedElements(),
|
elements,
|
||||||
(element) =>
|
(element) =>
|
||||||
isBindableElement(element, false) &&
|
isBindableElement(element, false) &&
|
||||||
bindingBorderTest(
|
bindingBorderTest(element, pointerCoords, elementsMap),
|
||||||
element,
|
|
||||||
pointerCoords,
|
|
||||||
scene.getNonDeletedElementsMap(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
return hoveredElement as NonDeleted<ExcalidrawBindableElement> | null;
|
return hoveredElement as NonDeleted<ExcalidrawBindableElement> | null;
|
||||||
};
|
};
|
||||||
|
@ -320,7 +327,7 @@ const calculateFocusAndGap = (
|
||||||
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
||||||
hoveredElement: ExcalidrawBindableElement,
|
hoveredElement: ExcalidrawBindableElement,
|
||||||
startOrEnd: "start" | "end",
|
startOrEnd: "start" | "end",
|
||||||
elementsMap: ElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
): { focus: number; gap: number } => {
|
): { focus: number; gap: number } => {
|
||||||
const direction = startOrEnd === "start" ? -1 : 1;
|
const direction = startOrEnd === "start" ? -1 : 1;
|
||||||
const edgePointIndex = direction === -1 ? 0 : linearElement.points.length - 1;
|
const edgePointIndex = direction === -1 ? 0 : linearElement.points.length - 1;
|
||||||
|
@ -539,33 +546,47 @@ const maybeCalculateNewGapWhenScaling = (
|
||||||
|
|
||||||
// TODO: this is a bottleneck, optimise
|
// TODO: this is a bottleneck, optimise
|
||||||
export const getEligibleElementsForBinding = (
|
export const getEligibleElementsForBinding = (
|
||||||
elements: NonDeleted<ExcalidrawElement>[],
|
selectedElements: NonDeleted<ExcalidrawElement>[],
|
||||||
elementsMap: ElementsMap,
|
elements: readonly ExcalidrawElement[],
|
||||||
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
): SuggestedBinding[] => {
|
): SuggestedBinding[] => {
|
||||||
const includedElementIds = new Set(elements.map(({ id }) => id));
|
const includedElementIds = new Set(selectedElements.map(({ id }) => id));
|
||||||
return elements.flatMap((element) =>
|
return selectedElements.flatMap((selectedElement) =>
|
||||||
isBindingElement(element, false)
|
isBindingElement(selectedElement, false)
|
||||||
? (getElligibleElementsForBindingElement(
|
? (getElligibleElementsForBindingElement(
|
||||||
element as NonDeleted<ExcalidrawLinearElement>,
|
selectedElement as NonDeleted<ExcalidrawLinearElement>,
|
||||||
|
elements,
|
||||||
elementsMap,
|
elementsMap,
|
||||||
).filter(
|
).filter(
|
||||||
(element) => !includedElementIds.has(element.id),
|
(element) => !includedElementIds.has(element.id),
|
||||||
) as SuggestedBinding[])
|
) as SuggestedBinding[])
|
||||||
: isBindableElement(element, false)
|
: isBindableElement(selectedElement, false)
|
||||||
? getElligibleElementsForBindableElementAndWhere(element).filter(
|
? getElligibleElementsForBindableElementAndWhere(
|
||||||
(binding) => !includedElementIds.has(binding[0].id),
|
selectedElement,
|
||||||
)
|
elementsMap,
|
||||||
|
).filter((binding) => !includedElementIds.has(binding[0].id))
|
||||||
: [],
|
: [],
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getElligibleElementsForBindingElement = (
|
const getElligibleElementsForBindingElement = (
|
||||||
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
||||||
elementsMap: ElementsMap,
|
elements: readonly ExcalidrawElement[],
|
||||||
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
): NonDeleted<ExcalidrawBindableElement>[] => {
|
): NonDeleted<ExcalidrawBindableElement>[] => {
|
||||||
return [
|
return [
|
||||||
getElligibleElementForBindingElement(linearElement, "start", elementsMap),
|
getElligibleElementForBindingElement(
|
||||||
getElligibleElementForBindingElement(linearElement, "end", elementsMap),
|
linearElement,
|
||||||
|
"start",
|
||||||
|
elements,
|
||||||
|
elementsMap,
|
||||||
|
),
|
||||||
|
getElligibleElementForBindingElement(
|
||||||
|
linearElement,
|
||||||
|
"end",
|
||||||
|
elements,
|
||||||
|
elementsMap,
|
||||||
|
),
|
||||||
].filter(
|
].filter(
|
||||||
(element): element is NonDeleted<ExcalidrawBindableElement> =>
|
(element): element is NonDeleted<ExcalidrawBindableElement> =>
|
||||||
element != null,
|
element != null,
|
||||||
|
@ -575,18 +596,20 @@ const getElligibleElementsForBindingElement = (
|
||||||
const getElligibleElementForBindingElement = (
|
const getElligibleElementForBindingElement = (
|
||||||
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
||||||
startOrEnd: "start" | "end",
|
startOrEnd: "start" | "end",
|
||||||
elementsMap: ElementsMap,
|
elements: readonly ExcalidrawElement[],
|
||||||
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
): NonDeleted<ExcalidrawBindableElement> | null => {
|
): NonDeleted<ExcalidrawBindableElement> | null => {
|
||||||
return getHoveredElementForBinding(
|
return getHoveredElementForBinding(
|
||||||
getLinearElementEdgeCoors(linearElement, startOrEnd, elementsMap),
|
getLinearElementEdgeCoors(linearElement, startOrEnd, elementsMap),
|
||||||
Scene.getScene(linearElement)!,
|
elements,
|
||||||
|
elementsMap,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getLinearElementEdgeCoors = (
|
const getLinearElementEdgeCoors = (
|
||||||
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
||||||
startOrEnd: "start" | "end",
|
startOrEnd: "start" | "end",
|
||||||
elementsMap: ElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
): { x: number; y: number } => {
|
): { x: number; y: number } => {
|
||||||
const index = startOrEnd === "start" ? 0 : -1;
|
const index = startOrEnd === "start" ? 0 : -1;
|
||||||
return tupleToCoors(
|
return tupleToCoors(
|
||||||
|
@ -600,6 +623,7 @@ const getLinearElementEdgeCoors = (
|
||||||
|
|
||||||
const getElligibleElementsForBindableElementAndWhere = (
|
const getElligibleElementsForBindableElementAndWhere = (
|
||||||
bindableElement: NonDeleted<ExcalidrawBindableElement>,
|
bindableElement: NonDeleted<ExcalidrawBindableElement>,
|
||||||
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
): SuggestedPointBinding[] => {
|
): SuggestedPointBinding[] => {
|
||||||
const scene = Scene.getScene(bindableElement)!;
|
const scene = Scene.getScene(bindableElement)!;
|
||||||
return scene
|
return scene
|
||||||
|
@ -612,13 +636,13 @@ const getElligibleElementsForBindableElementAndWhere = (
|
||||||
element,
|
element,
|
||||||
"start",
|
"start",
|
||||||
bindableElement,
|
bindableElement,
|
||||||
scene.getNonDeletedElementsMap(),
|
elementsMap,
|
||||||
);
|
);
|
||||||
const canBindEnd = isLinearElementEligibleForNewBindingByBindable(
|
const canBindEnd = isLinearElementEligibleForNewBindingByBindable(
|
||||||
element,
|
element,
|
||||||
"end",
|
"end",
|
||||||
bindableElement,
|
bindableElement,
|
||||||
scene.getNonDeletedElementsMap(),
|
elementsMap,
|
||||||
);
|
);
|
||||||
if (!canBindStart && !canBindEnd) {
|
if (!canBindStart && !canBindEnd) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -636,7 +660,7 @@ const isLinearElementEligibleForNewBindingByBindable = (
|
||||||
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
linearElement: NonDeleted<ExcalidrawLinearElement>,
|
||||||
startOrEnd: "start" | "end",
|
startOrEnd: "start" | "end",
|
||||||
bindableElement: NonDeleted<ExcalidrawBindableElement>,
|
bindableElement: NonDeleted<ExcalidrawBindableElement>,
|
||||||
elementsMap: ElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
): boolean => {
|
): boolean => {
|
||||||
const existingBinding =
|
const existingBinding =
|
||||||
linearElement[startOrEnd === "start" ? "startBinding" : "endBinding"];
|
linearElement[startOrEnd === "start" ? "startBinding" : "endBinding"];
|
||||||
|
|
|
@ -6,6 +6,8 @@ import {
|
||||||
ExcalidrawBindableElement,
|
ExcalidrawBindableElement,
|
||||||
ExcalidrawTextElementWithContainer,
|
ExcalidrawTextElementWithContainer,
|
||||||
ElementsMap,
|
ElementsMap,
|
||||||
|
NonDeletedExcalidrawElement,
|
||||||
|
NonDeletedSceneElementsMap,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import {
|
import {
|
||||||
distance2d,
|
distance2d,
|
||||||
|
@ -36,7 +38,6 @@ import {
|
||||||
import { mutateElement } from "./mutateElement";
|
import { mutateElement } from "./mutateElement";
|
||||||
import History from "../history";
|
import History from "../history";
|
||||||
|
|
||||||
import Scene from "../scene/Scene";
|
|
||||||
import {
|
import {
|
||||||
bindOrUnbindLinearElement,
|
bindOrUnbindLinearElement,
|
||||||
getHoveredElementForBinding,
|
getHoveredElementForBinding,
|
||||||
|
@ -86,11 +87,10 @@ export class LinearElementEditor {
|
||||||
public readonly hoverPointIndex: number;
|
public readonly hoverPointIndex: number;
|
||||||
public readonly segmentMidPointHoveredCoords: Point | null;
|
public readonly segmentMidPointHoveredCoords: Point | null;
|
||||||
|
|
||||||
constructor(element: NonDeleted<ExcalidrawLinearElement>, scene: Scene) {
|
constructor(element: NonDeleted<ExcalidrawLinearElement>) {
|
||||||
this.elementId = element.id as string & {
|
this.elementId = element.id as string & {
|
||||||
_brand: "excalidrawLinearElementId";
|
_brand: "excalidrawLinearElementId";
|
||||||
};
|
};
|
||||||
Scene.mapElementToScene(this.elementId, scene);
|
|
||||||
LinearElementEditor.normalizePoints(element);
|
LinearElementEditor.normalizePoints(element);
|
||||||
|
|
||||||
this.selectedPointsIndices = null;
|
this.selectedPointsIndices = null;
|
||||||
|
@ -123,8 +123,11 @@ export class LinearElementEditor {
|
||||||
* @param id the `elementId` from the instance of this class (so that we can
|
* @param id the `elementId` from the instance of this class (so that we can
|
||||||
* statically guarantee this method returns an ExcalidrawLinearElement)
|
* statically guarantee this method returns an ExcalidrawLinearElement)
|
||||||
*/
|
*/
|
||||||
static getElement(id: InstanceType<typeof LinearElementEditor>["elementId"]) {
|
static getElement(
|
||||||
const element = Scene.getScene(id)?.getNonDeletedElement(id);
|
id: InstanceType<typeof LinearElementEditor>["elementId"],
|
||||||
|
elementsMap: ElementsMap,
|
||||||
|
) {
|
||||||
|
const element = elementsMap.get(id);
|
||||||
if (element) {
|
if (element) {
|
||||||
return element as NonDeleted<ExcalidrawLinearElement>;
|
return element as NonDeleted<ExcalidrawLinearElement>;
|
||||||
}
|
}
|
||||||
|
@ -135,7 +138,7 @@ export class LinearElementEditor {
|
||||||
event: PointerEvent,
|
event: PointerEvent,
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
setState: React.Component<any, AppState>["setState"],
|
setState: React.Component<any, AppState>["setState"],
|
||||||
elementsMap: ElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
!appState.editingLinearElement ||
|
!appState.editingLinearElement ||
|
||||||
|
@ -146,7 +149,7 @@ export class LinearElementEditor {
|
||||||
const { editingLinearElement } = appState;
|
const { editingLinearElement } = appState;
|
||||||
const { selectedPointsIndices, elementId } = editingLinearElement;
|
const { selectedPointsIndices, elementId } = editingLinearElement;
|
||||||
|
|
||||||
const element = LinearElementEditor.getElement(elementId);
|
const element = LinearElementEditor.getElement(elementId, elementsMap);
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -197,13 +200,13 @@ export class LinearElementEditor {
|
||||||
pointSceneCoords: { x: number; y: number }[],
|
pointSceneCoords: { x: number; y: number }[],
|
||||||
) => void,
|
) => void,
|
||||||
linearElementEditor: LinearElementEditor,
|
linearElementEditor: LinearElementEditor,
|
||||||
elementsMap: ElementsMap,
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
): boolean {
|
): boolean {
|
||||||
if (!linearElementEditor) {
|
if (!linearElementEditor) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const { selectedPointsIndices, elementId } = linearElementEditor;
|
const { selectedPointsIndices, elementId } = linearElementEditor;
|
||||||
const element = LinearElementEditor.getElement(elementId);
|
const element = LinearElementEditor.getElement(elementId, elementsMap);
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -331,11 +334,12 @@ export class LinearElementEditor {
|
||||||
event: PointerEvent,
|
event: PointerEvent,
|
||||||
editingLinearElement: LinearElementEditor,
|
editingLinearElement: LinearElementEditor,
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
elementsMap: ElementsMap,
|
elements: readonly NonDeletedExcalidrawElement[],
|
||||||
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
): LinearElementEditor {
|
): LinearElementEditor {
|
||||||
const { elementId, selectedPointsIndices, isDragging, pointerDownState } =
|
const { elementId, selectedPointsIndices, isDragging, pointerDownState } =
|
||||||
editingLinearElement;
|
editingLinearElement;
|
||||||
const element = LinearElementEditor.getElement(elementId);
|
const element = LinearElementEditor.getElement(elementId, elementsMap);
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return editingLinearElement;
|
return editingLinearElement;
|
||||||
}
|
}
|
||||||
|
@ -376,7 +380,8 @@ export class LinearElementEditor {
|
||||||
elementsMap,
|
elementsMap,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Scene.getScene(element)!,
|
elements,
|
||||||
|
elementsMap,
|
||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
@ -490,7 +495,7 @@ export class LinearElementEditor {
|
||||||
elementsMap: ElementsMap,
|
elementsMap: ElementsMap,
|
||||||
) => {
|
) => {
|
||||||
const { elementId } = linearElementEditor;
|
const { elementId } = linearElementEditor;
|
||||||
const element = LinearElementEditor.getElement(elementId);
|
const element = LinearElementEditor.getElement(elementId, elementsMap);
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -614,6 +619,7 @@ export class LinearElementEditor {
|
||||||
) {
|
) {
|
||||||
const element = LinearElementEditor.getElement(
|
const element = LinearElementEditor.getElement(
|
||||||
linearElementEditor.elementId,
|
linearElementEditor.elementId,
|
||||||
|
elementsMap,
|
||||||
);
|
);
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -639,7 +645,8 @@ export class LinearElementEditor {
|
||||||
history: History,
|
history: History,
|
||||||
scenePointer: { x: number; y: number },
|
scenePointer: { x: number; y: number },
|
||||||
linearElementEditor: LinearElementEditor,
|
linearElementEditor: LinearElementEditor,
|
||||||
elementsMap: ElementsMap,
|
elements: readonly NonDeletedExcalidrawElement[],
|
||||||
|
elementsMap: NonDeletedSceneElementsMap,
|
||||||
): {
|
): {
|
||||||
didAddPoint: boolean;
|
didAddPoint: boolean;
|
||||||
hitElement: NonDeleted<ExcalidrawElement> | null;
|
hitElement: NonDeleted<ExcalidrawElement> | null;
|
||||||
|
@ -656,7 +663,7 @@ export class LinearElementEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { elementId } = linearElementEditor;
|
const { elementId } = linearElementEditor;
|
||||||
const element = LinearElementEditor.getElement(elementId);
|
const element = LinearElementEditor.getElement(elementId, elementsMap);
|
||||||
|
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -709,7 +716,8 @@ export class LinearElementEditor {
|
||||||
lastUncommittedPoint: null,
|
lastUncommittedPoint: null,
|
||||||
endBindingElement: getHoveredElementForBinding(
|
endBindingElement: getHoveredElementForBinding(
|
||||||
scenePointer,
|
scenePointer,
|
||||||
Scene.getScene(element)!,
|
elements,
|
||||||
|
elementsMap,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -813,7 +821,7 @@ export class LinearElementEditor {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const { elementId, lastUncommittedPoint } = appState.editingLinearElement;
|
const { elementId, lastUncommittedPoint } = appState.editingLinearElement;
|
||||||
const element = LinearElementEditor.getElement(elementId);
|
const element = LinearElementEditor.getElement(elementId, elementsMap);
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return appState.editingLinearElement;
|
return appState.editingLinearElement;
|
||||||
}
|
}
|
||||||
|
@ -1020,14 +1028,14 @@ export class LinearElementEditor {
|
||||||
mutateElement(element, LinearElementEditor.getNormalizedPoints(element));
|
mutateElement(element, LinearElementEditor.getNormalizedPoints(element));
|
||||||
}
|
}
|
||||||
|
|
||||||
static duplicateSelectedPoints(appState: AppState) {
|
static duplicateSelectedPoints(appState: AppState, elementsMap: ElementsMap) {
|
||||||
if (!appState.editingLinearElement) {
|
if (!appState.editingLinearElement) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { selectedPointsIndices, elementId } = appState.editingLinearElement;
|
const { selectedPointsIndices, elementId } = appState.editingLinearElement;
|
||||||
|
|
||||||
const element = LinearElementEditor.getElement(elementId);
|
const element = LinearElementEditor.getElement(elementId, elementsMap);
|
||||||
|
|
||||||
if (!element || selectedPointsIndices === null) {
|
if (!element || selectedPointsIndices === null) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1189,9 +1197,11 @@ export class LinearElementEditor {
|
||||||
linearElementEditor: LinearElementEditor,
|
linearElementEditor: LinearElementEditor,
|
||||||
pointerCoords: PointerCoords,
|
pointerCoords: PointerCoords,
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
|
elementsMap: ElementsMap,
|
||||||
) {
|
) {
|
||||||
const element = LinearElementEditor.getElement(
|
const element = LinearElementEditor.getElement(
|
||||||
linearElementEditor.elementId,
|
linearElementEditor.elementId,
|
||||||
|
elementsMap,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!element) {
|
if (!element) {
|
||||||
|
@ -1234,6 +1244,7 @@ export class LinearElementEditor {
|
||||||
) {
|
) {
|
||||||
const element = LinearElementEditor.getElement(
|
const element = LinearElementEditor.getElement(
|
||||||
linearElementEditor.elementId,
|
linearElementEditor.elementId,
|
||||||
|
elementsMap,
|
||||||
);
|
);
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -354,7 +354,8 @@ const renderLinearElementPointHighlight = (
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const element = LinearElementEditor.getElement(elementId);
|
const element = LinearElementEditor.getElement(elementId, elementsMap);
|
||||||
|
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import React from "react";
|
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import { render, fireEvent } from "./test-utils";
|
import { render, fireEvent } from "./test-utils";
|
||||||
import { Excalidraw } from "../index";
|
import { Excalidraw } from "../index";
|
||||||
|
@ -13,7 +12,6 @@ import {
|
||||||
import { UI, Pointer, Keyboard } from "./helpers/ui";
|
import { UI, Pointer, Keyboard } from "./helpers/ui";
|
||||||
import { KEYS } from "../keys";
|
import { KEYS } from "../keys";
|
||||||
import { vi } from "vitest";
|
import { vi } from "vitest";
|
||||||
import { arrayToMap } from "../utils";
|
|
||||||
|
|
||||||
// Unmount ReactDOM from root
|
// Unmount ReactDOM from root
|
||||||
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
||||||
|
@ -76,7 +74,7 @@ describe("move element", () => {
|
||||||
const rectA = UI.createElement("rectangle", { size: 100 });
|
const rectA = UI.createElement("rectangle", { size: 100 });
|
||||||
const rectB = UI.createElement("rectangle", { x: 200, y: 0, size: 300 });
|
const rectB = UI.createElement("rectangle", { x: 200, y: 0, size: 300 });
|
||||||
const line = UI.createElement("line", { x: 110, y: 50, size: 80 });
|
const line = UI.createElement("line", { x: 110, y: 50, size: 80 });
|
||||||
const elementsMap = arrayToMap(h.elements);
|
const elementsMap = h.app.scene.getNonDeletedElementsMap();
|
||||||
// bind line to two rectangles
|
// bind line to two rectangles
|
||||||
bindOrUnbindLinearElement(
|
bindOrUnbindLinearElement(
|
||||||
line.get() as NonDeleted<ExcalidrawLinearElement>,
|
line.get() as NonDeleted<ExcalidrawLinearElement>,
|
||||||
|
|
Loading…
Add table
Reference in a new issue