import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable, Subject } from 'rxjs'; import { FuseNavigationItem } from '@fuse/types'; @Injectable({ providedIn: 'root' }) export class FuseNavigationService { onItemCollapsed: Subject; onItemCollapseToggled: Subject; // Private private _onNavigationChanged: BehaviorSubject; private _onNavigationRegistered: BehaviorSubject; private _onNavigationUnregistered: BehaviorSubject; private _currentNavigationKey: string; private _registry: { [key: string]: any } = {}; /** * Constructor */ constructor() { // Set the defaults this.onItemCollapsed = new Subject(); this.onItemCollapseToggled = new Subject(); // Set the private defaults this._currentNavigationKey = null; this._onNavigationChanged = new BehaviorSubject(null); this._onNavigationRegistered = new BehaviorSubject(null); this._onNavigationUnregistered = new BehaviorSubject(null); } // ----------------------------------------------------------------------------------------------------- // @ Accessors // ----------------------------------------------------------------------------------------------------- /** * Get onNavigationChanged * * @returns {Observable} */ get onNavigationChanged(): Observable { return this._onNavigationChanged.asObservable(); } /** * Get onNavigationRegistered * * @returns {Observable} */ get onNavigationRegistered(): Observable { return this._onNavigationRegistered.asObservable(); } /** * Get onNavigationUnregistered * * @returns {Observable} */ get onNavigationUnregistered(): Observable { return this._onNavigationUnregistered.asObservable(); } // ----------------------------------------------------------------------------------------------------- // @ Public methods // ----------------------------------------------------------------------------------------------------- /** * Register the given navigation * with the given key * * @param key * @param navigation */ register(key, navigation): void { // Check if the key already being used if ( this._registry[key] ) { console.error(`The navigation with the key '${key}' already exists. Either unregister it first or use a unique key.`); return; } // Add to the registry this._registry[key] = navigation; // Notify the subject this._onNavigationRegistered.next([key, navigation]); } /** * Unregister the navigation from the registry * @param key */ unregister(key): void { // Check if the navigation exists if ( !this._registry[key] ) { console.warn(`The navigation with the key '${key}' doesn't exist in the registry.`); } // Unregister the sidebar delete this._registry[key]; // Notify the subject this._onNavigationUnregistered.next(key); } /** * Get navigation from registry by key * * @param key * @returns {any} */ getNavigation(key): any { // Check if the navigation exists if ( !this._registry[key] ) { console.warn(`The navigation with the key '${key}' doesn't exist in the registry.`); return; } // Return the sidebar return this._registry[key]; } /** * Get flattened navigation array * * @param navigation * @param flatNavigation * @returns {any[]} */ getFlatNavigation(navigation, flatNavigation: FuseNavigationItem[] = []): any { for ( const item of navigation ) { if ( item.type === 'item' ) { flatNavigation.push(item); continue; } if ( item.type === 'collapsable' || item.type === 'group' ) { if ( item.children ) { this.getFlatNavigation(item.children, flatNavigation); } } } return flatNavigation; } /** * Get the current navigation * * @returns {any} */ getCurrentNavigation(): any { if ( !this._currentNavigationKey ) { console.warn(`The current navigation is not set.`); return; } return this.getNavigation(this._currentNavigationKey); } /** * Set the navigation with the key * as the current navigation * * @param key */ setCurrentNavigation(key): void { // Check if the sidebar exists if ( !this._registry[key] ) { console.warn(`The navigation with the key '${key}' doesn't exist in the registry.`); return; } // Set the current navigation key this._currentNavigationKey = key; // Notify the subject this._onNavigationChanged.next(key); } /** * Get navigation item by id from the * current navigation * * @param id * @param {any} navigation * @returns {any | boolean} */ getNavigationItem(id, navigation = null): any | boolean { if ( !navigation ) { navigation = this.getCurrentNavigation(); } for ( const item of navigation ) { if ( item.id === id ) { return item; } if ( item.children ) { const childItem = this.getNavigationItem(id, item.children); if ( childItem ) { return childItem; } } } return false; } /** * Get the parent of the navigation item * with the id * * @param id * @param {any} navigation * @param parent */ getNavigationItemParent(id, navigation = null, parent = null): any { if ( !navigation ) { navigation = this.getCurrentNavigation(); parent = navigation; } for ( const item of navigation ) { if ( item.id === id ) { return parent; } if ( item.children ) { const childItem = this.getNavigationItemParent(id, item.children, item); if ( childItem ) { return childItem; } } } return false; } /** * Add a navigation item to the specified location * * @param item * @param id */ addNavigationItem(item, id): void { // Get the current navigation const navigation: any[] = this.getCurrentNavigation(); // Add to the end of the navigation if ( id === 'end' ) { navigation.push(item); return; } // Add to the start of the navigation if ( id === 'start' ) { navigation.unshift(item); } // Add it to a specific location const parent: any = this.getNavigationItem(id); if ( parent ) { // Check if parent has a children entry, // and add it if it doesn't if ( !parent.children ) { parent.children = []; } // Add the item parent.children.push(item); } } /** * Remove navigation item with the given id * * @param id */ removeNavigationItem(id): void { const item = this.getNavigationItem(id); // Return, if there is not such an item if ( !item ) { return; } // Get the parent of the item let parent = this.getNavigationItemParent(id); // This check is required because of the first level // of the navigation, since the first level is not // inside the 'children' array parent = parent.children || parent; // Remove the item parent.splice(parent.indexOf(item), 1); } }