import { Logger } from "@adpt/utils" ; import { AdaptElement , AdaptMountedElement , childrenToArray , ElementID , isMountedElement , } from "./jsx" ; export interface DomDiff < E extends AdaptMountedElement = AdaptMountedElement > { added : Set < E > ; deleted : Set < E > ; commonOld : Set < E > ; commonNew : Set < E > ; } export function domForEach < E extends AdaptMountedElement = AdaptMountedElement > ( dom , f ) { if ( dom == null ) return ; f ( dom ) ; childrenToArray ( dom . props . children ) . forEach ( ( c ) => isMountedElement ( c ) && domForEach ( c as E , f ) ) ; } export function domMap < T , E extends AdaptMountedElement = AdaptMountedElement > ( dom , f ) { const ret = [ ] ; domForEach ( dom , ( el ) => ret . push ( f ( el ) ) ) ; return ret ; } export type DomDiffIdFunc = ( el : AdaptMountedElement ) => string ; export const defaultDomDiffId = ( el ) => el . id ; export function domDiff < E extends AdaptMountedElement = AdaptMountedElement > ( oldDom , newDom , idFunc = defaultDomDiffId ) { const oldElems = domMap ( oldDom , ( el ) => el ) ; const newElems = domMap ( newDom , ( el ) => el ) ; return domDiffElements ( oldElems , newElems , idFunc ) ; } export function domDiffElements < E extends AdaptMountedElement = AdaptMountedElement > ( oldElems , newElems , idFunc = defaultDomDiffId ) { const byId = new Map < ElementID , E > ( ) ; const added = new Set < E > ( ) ; const deleted = new Set < E > ( ) ; const commonOld = new Set < E > ( ) ; const commonNew = new Set < E > ( ) ; oldElems . forEach ( ( el ) => byId . set ( idFunc ( el ) , el ) ) ; newElems . forEach ( ( el ) => { const id = idFunc ( el ) ; const old = byId . get ( id ) ; if ( old ) { commonOld . add ( old ) ; commonNew . add ( el ) ; byId . delete ( id ) ; } else { added . add ( el ) ; } } ) ; byId . forEach ( ( el ) => deleted . add ( el ) ) ; return { added , deleted , commonOld , commonNew } ; } /**
 * Given a DomDiff, generated from an old and new DOM, returns an Array of
 * the Elements that will be active if this DomDiff is deployed. That means
 * all of the Elements in the new DOM plus the deleted Elements from the
 * old DOM.
 */ export function domActiveElems < E extends AdaptMountedElement = AdaptMountedElement > ( diff ) { const a = [ ] ; return a . concat ( Array . from ( diff . added ) , Array . from ( diff . commonNew ) , Array . from ( diff . deleted ) ) ; } export function logElements ( msg , elements , logger ) { const els = elements . map ( ( el ) => { let path = "[not mounted]" ; let id = "[not mounted]" ; if ( isMountedElement ( el ) ) { path = el . path ; id = el . id ; } return `${ el . componentName } (key=${ el . props . key })\n  path: ${ path }\n  ID: ${ id }` ; } ) ; logger ( msg + els . join ( "\n" ) ) ; }