fix: remove scene from getElementAbsoluteCoords and dependent functions and use elementsMap (#7663)

* fix: remove scene from getElementAbsoluteCoords and dependent functions and use elementsMap

* lint

* fix

* use non deleted elements where possible

* use non deleted elements map in actions

* pass elementsMap instead of array to elementOverlapsWithFrame

* lint

* fix

* pass elementsMap to getElementsCorners

* pass elementsMap to getEligibleElementsForBinding

* pass elementsMap in bindOrUnbindSelectedElements and unbindLinearElements

* pass elementsMap in elementsAreInFrameBounds,elementOverlapsWithFrame,isCursorInFrame,getElementsInResizingFrame

* pass elementsMap in getElementsWithinSelection, getElementsCompletelyInFrame, isElementContainingFrame, getElementsInNewFrame

* pass elementsMap to getElementWithTransformHandleType

* pass elementsMap to getVisibleGaps, getMaximumGroups,getReferenceSnapPoints,snapDraggedElements

* lint

* pass elementsMap to bindTextToShapeAfterDuplication,bindLinearElementToElement,getTextBindableContainerAtPosition

* revert changes for bindTextToShapeAfterDuplication
This commit is contained in:
Aakansha Doshi 2024-02-16 11:35:01 +05:30 committed by GitHub
parent 73bf50e8a8
commit 47f87f4ecb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 779 additions and 270 deletions

View file

@ -1536,6 +1536,7 @@ class App extends React.Component<AppProps, AppState> {
<Hyperlink
key={firstSelectedElement.id}
element={firstSelectedElement}
elementsMap={allElementsMap}
setAppState={this.setAppState}
onLinkOpen={this.props.onLinkOpen}
setToast={this.setToast}
@ -1549,6 +1550,7 @@ class App extends React.Component<AppProps, AppState> {
isMagicFrameElement(firstSelectedElement) && (
<ElementCanvasButtons
element={firstSelectedElement}
elementsMap={elementsMap}
>
<ElementCanvasButton
title={t("labels.convertToCode")}
@ -1569,6 +1571,7 @@ class App extends React.Component<AppProps, AppState> {
?.status === "done" && (
<ElementCanvasButtons
element={firstSelectedElement}
elementsMap={elementsMap}
>
<ElementCanvasButton
title={t("labels.copySource")}
@ -2599,10 +2602,10 @@ class App extends React.Component<AppProps, AppState> {
componentDidUpdate(prevProps: AppProps, prevState: AppState) {
this.updateEmbeddables();
if (
!this.state.showWelcomeScreen &&
!this.scene.getElementsIncludingDeleted().length
) {
const elements = this.scene.getElementsIncludingDeleted();
const elementsMap = this.scene.getElementsMapIncludingDeleted();
if (!this.state.showWelcomeScreen && !elements.length) {
this.setState({ showWelcomeScreen: true });
}
@ -2756,27 +2759,21 @@ class App extends React.Component<AppProps, AppState> {
LinearElementEditor.getPointAtIndexGlobalCoordinates(
multiElement,
-1,
elementsMap,
),
),
elementsMap,
);
}
this.history.record(this.state, this.scene.getElementsIncludingDeleted());
this.history.record(this.state, elements);
// Do not notify consumers if we're still loading the scene. Among other
// potential issues, this fixes a case where the tab isn't focused during
// init, which would trigger onChange with empty elements, which would then
// override whatever is in localStorage currently.
if (!this.state.isLoading) {
this.props.onChange?.(
this.scene.getElementsIncludingDeleted(),
this.state,
this.files,
);
this.onChangeEmitter.trigger(
this.scene.getElementsIncludingDeleted(),
this.state,
this.files,
);
this.props.onChange?.(elements, this.state, this.files);
this.onChangeEmitter.trigger(elements, this.state, this.files);
}
}
@ -3126,7 +3123,11 @@ class App extends React.Component<AppProps, AppState> {
newElement,
this.scene.getElementsMapIncludingDeleted(),
);
redrawTextBoundingBox(newElement, container);
redrawTextBoundingBox(
newElement,
container,
this.scene.getElementsMapIncludingDeleted(),
);
}
});
@ -3836,7 +3837,7 @@ class App extends React.Component<AppProps, AppState> {
y: element.y + offsetY,
});
updateBoundElements(element, {
updateBoundElements(element, this.scene.getNonDeletedElementsMap(), {
simultaneouslyUpdated: selectedElements,
});
});
@ -4010,9 +4011,10 @@ class App extends React.Component<AppProps, AppState> {
}
if (isArrowKey(event.key)) {
const selectedElements = this.scene.getSelectedElements(this.state);
const elementsMap = this.scene.getNonDeletedElementsMap();
isBindingEnabled(this.state)
? bindOrUnbindSelectedElements(selectedElements)
: unbindLinearElements(selectedElements);
? bindOrUnbindSelectedElements(selectedElements, elementsMap)
: unbindLinearElements(selectedElements, elementsMap);
this.setState({ suggestedBindings: [] });
}
});
@ -4193,20 +4195,21 @@ class App extends React.Component<AppProps, AppState> {
isExistingElement?: boolean;
},
) {
const elementsMap = this.scene.getElementsMapIncludingDeleted();
const updateElement = (
text: string,
originalText: string,
isDeleted: boolean,
) => {
this.scene.replaceAllElements([
// Not sure why we include deleted elements as well hence using deleted elements map
...this.scene.getElementsIncludingDeleted().map((_element) => {
if (_element.id === element.id && isTextElement(_element)) {
return updateTextElement(
_element,
getContainerElement(
_element,
this.scene.getElementsMapIncludingDeleted(),
),
getContainerElement(_element, elementsMap),
elementsMap,
{
text,
isDeleted,
@ -4238,7 +4241,7 @@ class App extends React.Component<AppProps, AppState> {
onChange: withBatchedUpdates((text) => {
updateElement(text, text, false);
if (isNonDeletedElement(element)) {
updateBoundElements(element);
updateBoundElements(element, elementsMap);
}
}),
onSubmit: withBatchedUpdates(({ text, viaKeyboard, originalText }) => {
@ -4377,6 +4380,7 @@ class App extends React.Component<AppProps, AppState> {
!(isTextElement(element) && element.containerId)),
);
const elementsMap = this.scene.getNonDeletedElementsMap();
return getElementsAtPosition(elements, (element) =>
hitTest(
element,
@ -4384,7 +4388,7 @@ class App extends React.Component<AppProps, AppState> {
this.frameNameBoundsCache,
x,
y,
this.scene.getNonDeletedElementsMap(),
elementsMap,
),
).filter((element) => {
// hitting a frame's element from outside the frame is not considered a hit
@ -4392,7 +4396,7 @@ class App extends React.Component<AppProps, AppState> {
return containingFrame &&
this.state.frameRendering.enabled &&
this.state.frameRendering.clip
? isCursorInFrame({ x, y }, containingFrame)
? isCursorInFrame({ x, y }, containingFrame, elementsMap)
: true;
});
}
@ -4637,6 +4641,7 @@ class App extends React.Component<AppProps, AppState> {
this.state,
sceneX,
sceneY,
this.scene.getNonDeletedElementsMap(),
);
if (container) {
@ -4648,6 +4653,7 @@ class App extends React.Component<AppProps, AppState> {
this.state,
this.frameNameBoundsCache,
[sceneX, sceneY],
this.scene.getNonDeletedElementsMap(),
)
) {
const midPoint = getContainerCenter(
@ -4688,6 +4694,7 @@ class App extends React.Component<AppProps, AppState> {
index <= hitElementIndex &&
isPointHittingLink(
element,
this.scene.getNonDeletedElementsMap(),
this.state,
[scenePointer.x, scenePointer.y],
this.device.editor.isMobile,
@ -4718,8 +4725,10 @@ class App extends React.Component<AppProps, AppState> {
this.lastPointerDownEvent!,
this.state,
);
const elementsMap = this.scene.getNonDeletedElementsMap();
const lastPointerDownHittingLinkIcon = isPointHittingLink(
this.hitLinkElement,
elementsMap,
this.state,
[lastPointerDownCoords.x, lastPointerDownCoords.y],
this.device.editor.isMobile,
@ -4730,6 +4739,7 @@ class App extends React.Component<AppProps, AppState> {
);
const lastPointerUpHittingLinkIcon = isPointHittingLink(
this.hitLinkElement,
elementsMap,
this.state,
[lastPointerUpCoords.x, lastPointerUpCoords.y],
this.device.editor.isMobile,
@ -4766,10 +4776,11 @@ class App extends React.Component<AppProps, AppState> {
x: number;
y: number;
}) => {
const elementsMap = this.scene.getNonDeletedElementsMap();
const frames = this.scene
.getNonDeletedFramesLikes()
.filter((frame): frame is ExcalidrawFrameLikeElement =>
isCursorInFrame(sceneCoords, frame),
isCursorInFrame(sceneCoords, frame, elementsMap),
);
return frames.length ? frames[frames.length - 1] : null;
@ -4873,6 +4884,7 @@ class App extends React.Component<AppProps, AppState> {
y: scenePointerY,
},
event,
this.scene.getNonDeletedElementsMap(),
);
this.setState((prevState) => {
@ -4912,6 +4924,7 @@ class App extends React.Component<AppProps, AppState> {
scenePointerX,
scenePointerY,
this.state,
this.scene.getNonDeletedElementsMap(),
);
if (
@ -5062,6 +5075,7 @@ class App extends React.Component<AppProps, AppState> {
scenePointerY,
this.state.zoom,
event.pointerType,
this.scene.getNonDeletedElementsMap(),
);
if (
elementWithTransformHandleType &&
@ -5109,7 +5123,11 @@ class App extends React.Component<AppProps, AppState> {
!this.state.selectedElementIds[this.hitLinkElement.id]
) {
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
showHyperlinkTooltip(this.hitLinkElement, this.state);
showHyperlinkTooltip(
this.hitLinkElement,
this.state,
this.scene.getNonDeletedElementsMap(),
);
} else {
hideHyperlinkToolip();
if (
@ -5305,10 +5323,12 @@ class App extends React.Component<AppProps, AppState> {
this.state,
this.frameNameBoundsCache,
[scenePointerX, scenePointerY],
elementsMap,
)
) {
hoverPointIndex = LinearElementEditor.getPointIndexUnderCursor(
element,
elementsMap,
this.state.zoom,
scenePointerX,
scenePointerY,
@ -5738,10 +5758,12 @@ class App extends React.Component<AppProps, AppState> {
if (
clicklength < 300 &&
isIframeLikeElement(this.hitLinkElement) &&
!isPointHittingLinkIcon(this.hitLinkElement, this.state, [
scenePointer.x,
scenePointer.y,
])
!isPointHittingLinkIcon(
this.hitLinkElement,
this.scene.getNonDeletedElementsMap(),
this.state,
[scenePointer.x, scenePointer.y],
)
) {
this.handleEmbeddableCenterClick(this.hitLinkElement);
} else {
@ -6039,7 +6061,9 @@ class App extends React.Component<AppProps, AppState> {
): boolean => {
if (this.state.activeTool.type === "selection") {
const elements = this.scene.getNonDeletedElements();
const elementsMap = this.scene.getNonDeletedElementsMap();
const selectedElements = this.scene.getSelectedElements(this.state);
if (selectedElements.length === 1 && !this.state.editingLinearElement) {
const elementWithTransformHandleType =
getElementWithTransformHandleType(
@ -6049,6 +6073,7 @@ class App extends React.Component<AppProps, AppState> {
pointerDownState.origin.y,
this.state.zoom,
event.pointerType,
this.scene.getNonDeletedElementsMap(),
);
if (elementWithTransformHandleType != null) {
this.setState({
@ -6072,6 +6097,7 @@ class App extends React.Component<AppProps, AppState> {
getResizeOffsetXY(
pointerDownState.resize.handleType,
selectedElements,
elementsMap,
pointerDownState.origin.x,
pointerDownState.origin.y,
),
@ -6352,6 +6378,7 @@ class App extends React.Component<AppProps, AppState> {
this.state,
sceneX,
sceneY,
this.scene.getNonDeletedElementsMap(),
);
if (hasBoundTextElement(element)) {
@ -6846,6 +6873,7 @@ class App extends React.Component<AppProps, AppState> {
this.scene.getNonDeletedElements(),
selectedElements,
this.state,
this.scene.getNonDeletedElementsMap(),
),
);
}
@ -6869,6 +6897,7 @@ class App extends React.Component<AppProps, AppState> {
this.scene.getNonDeletedElements(),
selectedElements,
this.state,
this.scene.getNonDeletedElementsMap(),
),
);
}
@ -6985,6 +7014,7 @@ class App extends React.Component<AppProps, AppState> {
pointerCoords,
this.state,
!event[KEYS.CTRL_OR_CMD],
this.scene.getNonDeletedElementsMap(),
);
if (!ret) {
return;
@ -7143,10 +7173,11 @@ class App extends React.Component<AppProps, AppState> {
this.maybeCacheReferenceSnapPoints(event, selectedElements);
const { snapOffset, snapLines } = snapDraggedElements(
getSelectedElements(originalElements, this.state),
originalElements,
dragOffset,
this.state,
event,
this.scene.getNonDeletedElementsMap(),
);
this.setState({ snapLines });
@ -7330,6 +7361,7 @@ class App extends React.Component<AppProps, AppState> {
event,
this.state,
this.setState.bind(this),
this.scene.getNonDeletedElementsMap(),
);
// regular box-select
} else {
@ -7360,6 +7392,7 @@ class App extends React.Component<AppProps, AppState> {
const elementsWithinSelection = getElementsWithinSelection(
elements,
draggingElement,
this.scene.getNonDeletedElementsMap(),
);
this.setState((prevState) => {
@ -7491,7 +7524,7 @@ class App extends React.Component<AppProps, AppState> {
this.setState({
selectedElementsAreBeingDragged: false,
});
const elementsMap = this.scene.getNonDeletedElementsMap();
// Handle end of dragging a point of a linear element, might close a loop
// and sets binding element
if (this.state.editingLinearElement) {
@ -7506,6 +7539,7 @@ class App extends React.Component<AppProps, AppState> {
childEvent,
this.state.editingLinearElement,
this.state,
elementsMap,
);
if (editingLinearElement !== this.state.editingLinearElement) {
this.setState({
@ -7529,6 +7563,7 @@ class App extends React.Component<AppProps, AppState> {
childEvent,
this.state.selectedLinearElement,
this.state,
elementsMap,
);
const { startBindingElement, endBindingElement } =
@ -7539,6 +7574,7 @@ class App extends React.Component<AppProps, AppState> {
element,
startBindingElement,
endBindingElement,
elementsMap,
);
}
@ -7678,6 +7714,7 @@ class App extends React.Component<AppProps, AppState> {
this.state,
this.scene,
pointerCoords,
elementsMap,
);
}
this.setState({ suggestedBindings: [], startBoundElement: null });
@ -7748,7 +7785,13 @@ class App extends React.Component<AppProps, AppState> {
const frame = getContainingFrame(linearElement);
if (frame && linearElement) {
if (!elementOverlapsWithFrame(linearElement, frame)) {
if (
!elementOverlapsWithFrame(
linearElement,
frame,
this.scene.getNonDeletedElementsMap(),
)
) {
// remove the linear element from all groups
// before removing it from the frame as well
mutateElement(linearElement, {
@ -7859,6 +7902,7 @@ class App extends React.Component<AppProps, AppState> {
const elementsInsideFrame = getElementsInNewFrame(
this.scene.getElementsIncludingDeleted(),
draggingElement,
this.scene.getNonDeletedElementsMap(),
);
this.scene.replaceAllElements(
@ -7909,6 +7953,7 @@ class App extends React.Component<AppProps, AppState> {
this.scene.getElementsIncludingDeleted(),
frame,
this.state,
elementsMap,
),
frame,
this,
@ -8189,7 +8234,10 @@ class App extends React.Component<AppProps, AppState> {
if (pointerDownState.drag.hasOccurred || isResizing || isRotating) {
(isBindingEnabled(this.state)
? bindOrUnbindSelectedElements
: unbindLinearElements)(this.scene.getSelectedElements(this.state));
: unbindLinearElements)(
this.scene.getSelectedElements(this.state),
elementsMap,
);
}
if (activeTool.type === "laser") {
@ -8719,7 +8767,10 @@ class App extends React.Component<AppProps, AppState> {
if (selectedElements.length > 50) {
return;
}
const suggestedBindings = getEligibleElementsForBinding(selectedElements);
const suggestedBindings = getEligibleElementsForBinding(
selectedElements,
this.scene.getNonDeletedElementsMap(),
);
this.setState({ suggestedBindings });
}
@ -9058,6 +9109,7 @@ class App extends React.Component<AppProps, AppState> {
x: gridX - pointerDownState.originInGrid.x,
y: gridY - pointerDownState.originInGrid.y,
},
this.scene.getNonDeletedElementsMap(),
);
gridX += snapOffset.x;
@ -9096,6 +9148,7 @@ class App extends React.Component<AppProps, AppState> {
this.scene.getNonDeletedElements(),
draggingElement as ExcalidrawFrameLikeElement,
this.state,
this.scene.getNonDeletedElementsMap(),
),
});
}
@ -9215,6 +9268,7 @@ class App extends React.Component<AppProps, AppState> {
this.scene.getNonDeletedElements(),
frame,
this.state,
this.scene.getNonDeletedElementsMap(),
).forEach((element) => elementsToHighlight.add(element));
});