@ -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 . sc ene. getE lementsIncludingDeleted( ) ) ;
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 . sc ene. getNonDeletedE lementsMap( ) ,
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 ) ) ;
} ) ;