import { Key , TreeMate , MergedKeys , TreeNode } from './interface' ; import { isExpilicitlyNotLoaded , merge , minus , traverseWithCb , TRAVERSE_COMMAND } from './utils' ; export class SubtreeNotLoadedError extends Error { constructor ( ) { super ( ) ; this . message = 'SubtreeNotLoadedError: checking a subtree whose required nodes are not fully loaded.' ; } } function getExtendedCheckedKeySetAfterCheck < R , G , I > ( checkKeys , currentCheckedKeys , treeMate ) { return getExtendedCheckedKeySet ( currentCheckedKeys . concat ( checkKeys ) , treeMate ) ; } function getAvailableAscendantNodeSet < R , G , I > ( uncheckedKeys , treeMate ) { const visitedKeys = new Set ( ) ; uncheckedKeys . forEach ( ( uncheckedKey ) => { const uncheckedTreeNode = treeMate . treeNodeMap . get ( uncheckedKey ) ; if ( uncheckedTreeNode !== undefined ) { let nodeCursor = uncheckedTreeNode . parent ; while ( nodeCursor !== null ) { if ( nodeCursor . disabled ) break ; if ( visitedKeys . has ( nodeCursor . key ) ) break ; else { visitedKeys . add ( nodeCursor . key ) ; } nodeCursor = nodeCursor . parent ; } } } ) ; return visitedKeys ; } function getExtendedCheckedKeySetAfterUncheck < R , G , I > ( uncheckedKeys , currentCheckedKeys , treeMate ) { const extendedCheckedKeySet = getExtendedCheckedKeySet ( currentCheckedKeys , treeMate ) ; const extendedKeySetToUncheck = getExtendedCheckedKeySet ( uncheckedKeys , treeMate , true ) ; const ascendantKeySet = getAvailableAscendantNodeSet ( uncheckedKeys , treeMate ) ; const keysToRemove = [ ] ; extendedCheckedKeySet . forEach ( ( key ) => { if ( extendedKeySetToUncheck . has ( key ) || ascendantKeySet . has ( key ) ) { keysToRemove . push ( key ) ; } } ) ; keysToRemove . forEach ( ( key ) => extendedCheckedKeySet . delete ( key ) ) ; return extendedCheckedKeySet ; } export function getCheckedKeys < R , G , I > ( options , treeMate ) { const { checkedKeys , keysToCheck , keysToUncheck , indeterminateKeys , cascade , leafOnly , checkStrategy } = options ; if ( ! cascade ) { if ( keysToCheck !== undefined ) { return { checkedKeys : merge ( checkedKeys , keysToCheck ) , indeterminateKeys : Array . from ( indeterminateKeys ) } ; } else if ( keysToUncheck !== undefined ) { return { checkedKeys : minus ( checkedKeys , keysToUncheck ) , indeterminateKeys : Array . from ( indeterminateKeys ) } ; } else { return { checkedKeys : Array . from ( checkedKeys ) , indeterminateKeys : Array . from ( indeterminateKeys ) } ; } } const { levelTreeNodeMap } = treeMate ; let extendedCheckedKeySet ; if ( keysToUncheck !== undefined ) { extendedCheckedKeySet = getExtendedCheckedKeySetAfterUncheck ( keysToUncheck , checkedKeys , treeMate ) ; } else if ( keysToCheck !== undefined ) { extendedCheckedKeySet = getExtendedCheckedKeySetAfterCheck ( keysToCheck , checkedKeys , treeMate ) ; } else { extendedCheckedKeySet = getExtendedCheckedKeySet ( checkedKeys , treeMate ) ; } const checkStrategyIsParent = checkStrategy === 'parent' ; const checkStrategyIsChild = checkStrategy === 'child' || leafOnly ; const syntheticCheckedKeySet = extendedCheckedKeySet ; const syntheticIndeterminateKeySet = new Set ( ) ; const maxLevel = Math . max . apply ( null , Array . from ( levelTreeNodeMap . keys ( ) ) ) ; for ( let level = maxLevel ; level >= 0 ; level -= 1 ) { const levelIsZero = level === 0 ; const levelTreeNodes = levelTreeNodeMap . get ( level ) ; for ( const levelTreeNode of levelTreeNodes as Array < TreeNode < R , G , I > > ) { if ( levelTreeNode . isLeaf ) continue ; const { key : levelTreeNodeKey , shallowLoaded } = levelTreeNode ; if ( checkStrategyIsChild && shallowLoaded ) { levelTreeNode . children ! . forEach ( ( v ) => { if ( ! v . disabled && ! v . isLeaf && v . shallowLoaded && syntheticCheckedKeySet . has ( v . key ) ) { syntheticCheckedKeySet . delete ( v . key ) ; } } ) ; } if ( levelTreeNode . disabled || ! shallowLoaded ) { continue ; } let fullyChecked = true ; let partialChecked = false ; let allDisabled = true ; for ( const childNode of levelTreeNode . children as Array < TreeNode < R , G , I > > ) { const childKey = childNode . key ; if ( childNode . disabled ) continue ; if ( allDisabled ) allDisabled = false ; if ( syntheticCheckedKeySet . has ( childKey ) ) { partialChecked = true ; } else if ( syntheticIndeterminateKeySet . has ( childKey ) ) { partialChecked = true ; fullyChecked = false ; break ; } else { fullyChecked = false ; if ( partialChecked ) { break ; } } } if ( fullyChecked && ! allDisabled ) { if ( checkStrategyIsParent ) { levelTreeNode . children ! . forEach ( ( v ) => { if ( ! v . disabled && syntheticCheckedKeySet . has ( v . key ) ) { syntheticCheckedKeySet . delete ( v . key ) ; } } ) ; } syntheticCheckedKeySet . add ( levelTreeNodeKey ) ; } else if ( partialChecked ) { syntheticIndeterminateKeySet . add ( levelTreeNodeKey ) ; } if ( levelIsZero && checkStrategyIsChild && syntheticCheckedKeySet . has ( levelTreeNodeKey ) ) { syntheticCheckedKeySet . delete ( levelTreeNodeKey ) ; } } } return { checkedKeys : Array . from ( syntheticCheckedKeySet ) , indeterminateKeys : Array . from ( syntheticIndeterminateKeySet ) } ; } export function getExtendedCheckedKeySet < R , G , I > ( checkedKeys , treeMate , isUnchecking = false ) { const { treeNodeMap , getChildren } = treeMate ; const visitedKeySet = new Set ( ) ; const extendedKeySet = new Set ( checkedKeys ) ; checkedKeys . forEach ( ( checkedKey ) => { const checkedTreeNode = treeNodeMap . get ( checkedKey ) ; if ( checkedTreeNode !== undefined ) { traverseWithCb ( checkedTreeNode , ( treeNode ) => { if ( treeNode . disabled ) { return TRAVERSE_COMMAND . STOP ; } const { key } = treeNode ; if ( visitedKeySet . has ( key ) ) return ; visitedKeySet . add ( key ) ; extendedKeySet . add ( key ) ; if ( isExpilicitlyNotLoaded ( treeNode . rawNode , getChildren ) ) { if ( isUnchecking ) { return TRAVERSE_COMMAND . STOP ; } else { throw new SubtreeNotLoadedError ( ) ; } } } ) ; } } ) ; return extendedKeySet ; }