393 lines
8.9 KiB
TypeScript
393 lines
8.9 KiB
TypeScript
import { Injectable } from '@angular/core';
|
|
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
|
import * as _ from 'lodash';
|
|
|
|
import { FuseNavigationItem } from 'src/@fuse/types';
|
|
|
|
@Injectable({
|
|
providedIn: 'root'
|
|
})
|
|
export class FuseNavigationService {
|
|
onItemCollapsed: Subject<any>;
|
|
onItemCollapseToggled: Subject<any>;
|
|
|
|
// Private
|
|
private _onNavigationChanged: BehaviorSubject<any>;
|
|
private _onNavigationRegistered: BehaviorSubject<any>;
|
|
private _onNavigationUnregistered: BehaviorSubject<any>;
|
|
private _onNavigationItemAdded: BehaviorSubject<any>;
|
|
private _onNavigationItemUpdated: BehaviorSubject<any>;
|
|
private _onNavigationItemRemoved: BehaviorSubject<any>;
|
|
|
|
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);
|
|
this._onNavigationItemAdded = new BehaviorSubject(null);
|
|
this._onNavigationItemUpdated = new BehaviorSubject(null);
|
|
this._onNavigationItemRemoved = new BehaviorSubject(null);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------------
|
|
// @ Accessors
|
|
// -----------------------------------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Get onNavigationChanged
|
|
*
|
|
* @returns {Observable<any>}
|
|
*/
|
|
get onNavigationChanged(): Observable<any> {
|
|
return this._onNavigationChanged.asObservable();
|
|
}
|
|
|
|
/**
|
|
* Get onNavigationRegistered
|
|
*
|
|
* @returns {Observable<any>}
|
|
*/
|
|
get onNavigationRegistered(): Observable<any> {
|
|
return this._onNavigationRegistered.asObservable();
|
|
}
|
|
|
|
/**
|
|
* Get onNavigationUnregistered
|
|
*
|
|
* @returns {Observable<any>}
|
|
*/
|
|
get onNavigationUnregistered(): Observable<any> {
|
|
return this._onNavigationUnregistered.asObservable();
|
|
}
|
|
|
|
/**
|
|
* Get onNavigationItemAdded
|
|
*
|
|
* @returns {Observable<any>}
|
|
*/
|
|
get onNavigationItemAdded(): Observable<any> {
|
|
return this._onNavigationItemAdded.asObservable();
|
|
}
|
|
|
|
/**
|
|
* Get onNavigationItemUpdated
|
|
*
|
|
* @returns {Observable<any>}
|
|
*/
|
|
get onNavigationItemUpdated(): Observable<any> {
|
|
return this._onNavigationItemUpdated.asObservable();
|
|
}
|
|
|
|
/**
|
|
* Get onNavigationItemRemoved
|
|
*
|
|
* @returns {Observable<any>}
|
|
*/
|
|
get onNavigationItemRemoved(): Observable<any> {
|
|
return this._onNavigationItemRemoved.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);
|
|
|
|
// Trigger the observable
|
|
this._onNavigationItemAdded.next(true);
|
|
|
|
return;
|
|
}
|
|
|
|
// Add to the start of the navigation
|
|
if (id === 'start') {
|
|
navigation.unshift(item);
|
|
|
|
// Trigger the observable
|
|
this._onNavigationItemAdded.next(true);
|
|
|
|
return;
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
// Trigger the observable
|
|
this._onNavigationItemAdded.next(true);
|
|
}
|
|
|
|
/**
|
|
* Update navigation item with the given id
|
|
*
|
|
* @param id
|
|
* @param properties
|
|
*/
|
|
updateNavigationItem(id, properties): void {
|
|
// Get the navigation item
|
|
const navigationItem = this.getNavigationItem(id);
|
|
|
|
// If there is no navigation with the give id, return
|
|
if (!navigationItem) {
|
|
return;
|
|
}
|
|
|
|
// Merge the navigation properties
|
|
_.merge(navigationItem, properties);
|
|
|
|
// Trigger the observable
|
|
this._onNavigationItemUpdated.next(true);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
|
|
// Trigger the observable
|
|
this._onNavigationItemRemoved.next(true);
|
|
}
|
|
}
|