import { NavigatorRoute } from '..' ; import { getParentPath , isPath , getByPath } from './utils' ; import { ERROR_TREE_PARENT_DOESNT_EXIST , ERROR_TREE_NO_ROUTE , ERROR_ROUTE_NOT_REGISTERED , ERROR_NODE_TO_REMOVE_NOT_EXIST , ERROR_PARENT_DOESNT_EXIST , } from '../constants' ; import RouteNode from './RouteNode' ; import { TreeCallback } from './types' ; export interface TreeRoutesConfig { errorLogger ? : ( err : string ) => void ; useAdapter ? : boolean ; } /**
 * Класс дерева навигации. Хранит узлы навигации и содержит в себе методы поиска удаления/добавления узлов дерева.
 */ export default class TreeRoutes { private readonly root : RouteNode ; private readonly errorLogger = ( err , arg ? ) => console . error ( err , arg ) ; constructor ( config ) { if ( config . errorLogger ) { this . errorLogger = config . errorLogger ; } const rootData = { name : '' , params : '' , children : [ ] , } ; this . root = new RouteNode ( rootData ) ; } public contains = ( callback ) => { this . traverse ( callback ) ; } ; public getParentNode = ( path ) => { const pathToParent = getParentPath ( path ) ; const parent = this . getByPath ( pathToParent ) ; if ( parent ) { return parent ; } } ; public add = ( routeNode , parentNode ? ) => { let child = routeNode instanceof RouteNode ? routeNode : new RouteNode ( routeNode ) ; let parent = null ; this . contains ( ( node ) => { if ( parentNode && node && node . name === parentNode . name ) { parent = node ; } else if ( ! parentNode ) { parent = this . root ; } } ) ; if ( parent ) { parent . children = parent . children || [ ] ; parent . children . push ( child ) ; child . parent = parent ; } else { this . errorLogger ( ERROR_TREE_PARENT_DOESNT_EXIST ) ; } } ; private readonly getByPath = ( pathToRoute ) => { const resultNode = getByPath ( this . root . children , pathToRoute ) ; if ( ! resultNode ) { this . errorLogger ( ERROR_TREE_NO_ROUTE ) ; } return resultNode ; } ; private readonly findByName = ( nodeName ) => { let resultNode ; this . contains ( ( node ) => { if ( node . name === nodeName ) { resultNode = node ; } } ) ; if ( ! resultNode ) { this . errorLogger ( ERROR_TREE_NO_ROUTE ) ; } return resultNode ; } ; public remove = ( routeName ) => { let parent = null ; let childToRemove = null ; let index ; this . contains ( ( node ) => { if ( node . name === routeName ) { parent = node ; } } ) ; if ( parent ) { index = this . findIndex ( parent . children , routeName ) ; if ( index === undefined ) { this . errorLogger ( ERROR_NODE_TO_REMOVE_NOT_EXIST ) ; } else { childToRemove = parent . children . splice ( index , 1 ) ; } } else { this . errorLogger ( ERROR_PARENT_DOESNT_EXIST ) ; } return childToRemove ; } ; private readonly traverse = ( callback ) => { ( function recurse ( currentNode ) { for ( let i = 0 , length = currentNode . children ? currentNode . children . length : 0 ; i < length ; i ++ ) { recurse ( currentNode . children [ i ] ) ; } callback ( currentNode ) ; } ) ( this . root ) ; } ; public findIndex = ( routes , routeName ) => { return routes . findIndex ( ( route ) => route . name === routeName ) ; } ; public getRouteNode = ( routeName = '' ) : RouteNode => { const pathToRoute = isPath ( routeName ) ? routeName : '' ; const routeNode = pathToRoute ? this . getByPath ( pathToRoute ) : this . findByName ( routeName ) ; if ( ! ( routeNode instanceof RouteNode ) ) { this . errorLogger ( ERROR_ROUTE_NOT_REGISTERED , pathToRoute ) ; } return routeNode ; } ; } const getParentNode = ( routes , path ) => { const parentPath = getParentPath ( path ) ; const parent = getByPath ( routes , parentPath ) ; return parent ; } ; const makePreTreeRoute = ( route ) => { const { path , ... routeProps } = route ; const preTreeRoute = { ... routeProps , } ; return preTreeRoute ; } ; const createPreTree = ( routes ) => { const preTree = [ ] ; const iterateRoute = ( route , parent ? ) => { if ( Array . isArray ( route ) ) { route . forEach ( ( el ) => iterateRoute ( el ) ) ; } else if ( route ) { const preTreeRoute = makePreTreeRoute ( route ) ; if ( parent ) { parent . children . push ( preTreeRoute ) ; } else if ( isPath ( route . name ) ) { const parent = getParentNode ( preTree , route . name ) ; if ( parent ) { parent . children = parent . children || [ ] ; parent . children . push ( preTreeRoute ) ; } } else { preTree . push ( preTreeRoute ) ; } if ( Array . isArray ( route . children ) ) { route . children . forEach ( ( el ) => iterateRoute ( el , route ) ) ; } } } ; iterateRoute ( routes ) ; return preTree ; } ; /**
 * Фабрика создания объекта дерева
 */ export function createRoutesTree ( routes , config = { } ) { const proceedRoutes = createPreTree ( routes ) ; const RoutesTree = new TreeRoutes ( config ) ; ( function addRoutes ( route , parent ? ) { if ( Array . isArray ( route ) ) { route . forEach ( ( el ) => addRoutes ( el ) ) ; } else if ( route ) { const routeNode = new RouteNode ( route ) ; if ( parent ) { RoutesTree . add ( routeNode , parent ) ; } else { RoutesTree . add ( routeNode ) ; } if ( Array . isArray ( route . children ) ) { route . children . forEach ( ( el ) => addRoutes ( el , routeNode ) ) ; } } } ) ( proceedRoutes ) ; return RoutesTree ; }