@ -806,33 +806,37 @@ type ElementPartial<T extends ExcalidrawElement = ExcalidrawElement> = Omit<
* /
export class ElementsChange implements Change < SceneElementsMap > {
private constructor (
private readonly added : Map < string , Delta < ElementPartial > > ,
private readonly removed : Map < string , Delta < ElementPartial > > ,
private readonly updated : Map < string , Delta < ElementPartial > > ,
private readonly added : Record < string , Delta < ElementPartial > > ,
private readonly removed : Record < string , Delta < ElementPartial > > ,
private readonly updated : Record < string , Delta < ElementPartial > > ,
) { }
public static create (
added : Map < string , Delta < ElementPartial > > ,
removed : Map < string , Delta < ElementPartial > > ,
updated : Map < string , Delta < ElementPartial > > ,
added : Record < string , Delta < ElementPartial > > ,
removed : Record < string , Delta < ElementPartial > > ,
updated : Record < string , Delta < ElementPartial > > ,
options = { shouldRedistribute : false } ,
) {
let change : ElementsChange ;
if ( options . shouldRedistribute ) {
const nextAdded = new Map < string , Delta < ElementPartial > > ( ) ;
const nextRemoved = new Map < string , Delta < ElementPartial > > ( ) ;
const nextUpdated = new Map < string , Delta < ElementPartial > > ( ) ;
const nextAdded : Record < string , Delta < ElementPartial > > = { } ;
const nextRemoved : Record < string , Delta < ElementPartial > > = { } ;
const nextUpdated : Record < string , Delta < ElementPartial > > = { } ;
const deltas = [ . . . added , . . . removed , . . . updated ] ;
const deltas = [
. . . Object . entries ( added ) ,
. . . Object . entries ( removed ) ,
. . . Object . entries ( updated ) ,
] ;
for ( const [ id , delta ] of deltas ) {
if ( this . satisfiesAddition ( delta ) ) {
nextAdded . set ( id , delta ) ;
nextAdded [id ] = delta ;
} else if ( this . satisfiesRemoval ( delta ) ) {
nextRemoved .set ( id , delta ) ;
nextRemoved [id ] = delta ;
} else {
nextUpdated .set ( id , delta ) ;
nextUpdated [id ] = delta ;
}
}
@ -873,7 +877,7 @@ export class ElementsChange implements Change<SceneElementsMap> {
type : "added" | "removed" | "updated" ,
satifies : ( delta : Delta < ElementPartial > ) = > boolean ,
) {
for ( const [ id , delta ] of change [ type ] . entries ( ) ) {
for ( const [ id , delta ] of Object . entries ( change [ type ] ) ) {
if ( ! satifies ( delta ) ) {
console . error (
` Broken invariant for " ${ type } " delta, element " ${ id } ", delta: ` ,
@ -900,9 +904,9 @@ export class ElementsChange implements Change<SceneElementsMap> {
return ElementsChange . empty ( ) ;
}
const added = new Map < string , Delta < ElementPartial > > ( ) ;
const removed = new Map < string , Delta < ElementPartial > > ( ) ;
const updated = new Map < string , Delta < ElementPartial > > ( ) ;
const added : Record < string , Delta < ElementPartial > > = { } ;
const removed : Record < string , Delta < ElementPartial > > = { } ;
const updated : Record < string , Delta < ElementPartial > > = { } ;
// this might be needed only in same edge cases, like during collab, when `isDeleted` elements get removed or when we (un)intentionally remove the elements
for ( const prevElement of prevElements . values ( ) ) {
@ -918,7 +922,7 @@ export class ElementsChange implements Change<SceneElementsMap> {
ElementsChange . stripIrrelevantProps ,
) ;
removed .set ( prevElement . id , delta ) ;
removed [prevElement . id ] = delta ;
}
}
@ -938,7 +942,7 @@ export class ElementsChange implements Change<SceneElementsMap> {
ElementsChange . stripIrrelevantProps ,
) ;
added .set ( nextElement . id , delta ) ;
added [nextElement . id ] = delta ;
continue ;
}
@ -959,9 +963,9 @@ export class ElementsChange implements Change<SceneElementsMap> {
) {
// notice that other props could have been updated as well
if ( prevElement . isDeleted && ! nextElement . isDeleted ) {
added .set ( nextElement . id , delta ) ;
added [nextElement . id ] = delta ;
} else {
removed .set ( nextElement . id , delta ) ;
removed [nextElement . id ] = delta ;
}
continue ;
@ -969,7 +973,7 @@ export class ElementsChange implements Change<SceneElementsMap> {
// making sure there are at least some changes
if ( ! Delta . isEmpty ( delta ) ) {
updated .set ( nextElement . id , delta ) ;
updated [nextElement . id ] = delta ;
}
}
}
@ -978,15 +982,23 @@ export class ElementsChange implements Change<SceneElementsMap> {
}
public static empty() {
return ElementsChange . create ( new Map ( ) , new Map ( ) , new Map ( ) ) ;
return ElementsChange . create ( { } , { } , { } ) ;
}
public static load ( data : {
added : Record < string , Delta < ElementPartial > > ;
removed : Record < string , Delta < ElementPartial > > ;
updated : Record < string , Delta < ElementPartial > > ;
} ) {
return ElementsChange . create ( data . added , data . removed , data . updated ) ;
}
public inverse ( ) : ElementsChange {
const inverseInternal = ( deltas : Map < string , Delta < ElementPartial > > ) = > {
const inversedDeltas = new Map < string , Delta < ElementPartial > > ( ) ;
const inverseInternal = ( deltas : Record < string , Delta < ElementPartial > > ) = > {
const inversedDeltas : Record < string , Delta < ElementPartial > > = { } ;
for ( const [ id , delta ] of deltas . entries ( ) ) {
inversedDeltas . set ( id , Delta . create ( delta . inserted , delta . deleted ) ) ;
for ( const [ id , delta ] of Object . entries ( deltas ) ) {
inversedDeltas [id ] = Delta . create ( delta . inserted , delta . deleted ) ;
}
return inversedDeltas ;
@ -1002,9 +1014,9 @@ export class ElementsChange implements Change<SceneElementsMap> {
public isEmpty ( ) : boolean {
return (
this . added . size === 0 &&
this . removed . size === 0 &&
this . updated . size === 0
Object . keys ( this . added ) . length === 0 &&
Object . keys ( this . removed ) . length === 0 &&
Object . keys ( this . updated ) . length === 0
) ;
}
@ -1036,11 +1048,11 @@ export class ElementsChange implements Change<SceneElementsMap> {
} ;
const applyLatestChangesInternal = (
deltas : Map < string , Delta < ElementPartial > > ,
deltas : Record < string , Delta < ElementPartial > > ,
) = > {
const modifiedDeltas = new Map < string , Delta < ElementPartial > > ( ) ;
const modifiedDeltas : Record < string , Delta < ElementPartial > > = { } ;
for ( const [ id , delta ] of deltas . entries ( ) ) {
for ( const [ id , delta ] of Object . entries ( deltas ) ) {
const existingElement = elements . get ( id ) ;
if ( existingElement ) {
@ -1051,9 +1063,9 @@ export class ElementsChange implements Change<SceneElementsMap> {
"inserted" ,
) ;
modifiedDeltas .set ( id , modifiedDelta ) ;
modifiedDeltas [id ] = modifiedDelta ;
} else {
modifiedDeltas .set ( id , delta ) ;
modifiedDeltas [id ] = delta ;
}
}
@ -1158,8 +1170,8 @@ export class ElementsChange implements Change<SceneElementsMap> {
flags ,
) ;
return ( deltas : Map < string , Delta < ElementPartial > > ) = >
Array. from ( deltas . entries ( ) ) . reduce ( ( acc , [ id , delta ] ) = > {
return ( deltas : Record < string , Delta < ElementPartial > > ) = >
Object. entries ( deltas ) . reduce ( ( acc , [ id , delta ] ) = > {
const element = getElement ( id , delta . inserted ) ;
if ( element ) {
@ -1331,20 +1343,21 @@ export class ElementsChange implements Change<SceneElementsMap> {
} ;
// removed delta is affecting the bindings always, as all the affected elements of the removed elements need to be unbound
for ( const [ id ] of this . removed ) {
for ( const id of Object . keys ( this . removed ) ) {
ElementsChange . unbindAffected ( prevElements , nextElements , id , updater ) ;
}
// added delta is affecting the bindings always, all the affected elements of the added elements need to be rebound
for ( const [ id ] of this . added ) {
for ( const id of Object . keys ( this . added ) ) {
ElementsChange . rebindAffected ( prevElements , nextElements , id , updater ) ;
}
// updated delta is affecting the binding only in case it contains changed binding or bindable property
for ( const [ id ] of Array . from ( this . updated ) . filter ( ( [ _ , delta ] ) = >
Object . keys ( { . . . delta . deleted , . . . delta . inserted } ) . find ( ( prop ) = >
bindingProperties . has ( prop as BindingProp | BindableProp ) ,
) ,
for ( const [ id ] of Array . from ( Object . entries ( this . updated ) ) . filter (
( [ _ , delta ] ) = >
Object . keys ( { . . . delta . deleted , . . . delta . inserted } ) . find ( ( prop ) = >
bindingProperties . has ( prop as BindingProp | BindableProp ) ,
) ,
) ) {
const updatedElement = nextElements . get ( id ) ;
if ( ! updatedElement || updatedElement . isDeleted ) {
@ -1367,16 +1380,16 @@ export class ElementsChange implements Change<SceneElementsMap> {
nextAffectedElements ,
) ;
for ( const [ id , delta ] of added ) {
this . added .set ( id , delta ) ;
for ( const [ id , delta ] of Object . entries ( added ) ) {
this . added [id ] = delta ;
}
for ( const [ id , delta ] of removed ) {
this . removed .set ( id , delta ) ;
for ( const [ id , delta ] of Object . entries ( removed ) ) {
this . removed [id ] = delta ;
}
for ( const [ id , delta ] of updated ) {
this . updated .set ( id , delta ) ;
for ( const [ id , delta ] of Object . entries ( updated ) ) {
this . updated [id ] = delta ;
}
return nextAffectedElements ;