import { Component , HostBinding , Inject , Input , OnDestroy , Optional , ViewEncapsulation } from '@angular/core' ; import { ActivatedRoute , RouterLinkActive } from '@angular/router' ; import { TranslateService } from '@ngx-translate/core' ; import { takeUntil } from 'rxjs/operators' ; import { ObNavTreeItemModel } from './nav-tree-item.model' ; import { Subject } from 'rxjs' ; import { ObUseObliqueIcons } from '../icon/icon.model' ; @ Component ( { selector : 'ob-nav-tree' , exportAs : 'obNavTree' , templateUrl : './nav-tree.component.html' , styleUrls : [ './nav-tree.component.scss' ] , encapsulation : ViewEncapsulation . None } ) export class ObNavTreeComponent implements OnDestroy { static DEFAULTS = { VARIANT : 'ob-nav-bordered ob-nav-hover' , HIGHLIGHT : 'ob-pattern-highlight' , LABEL_FORMATTER : defaultLabelFormatterFactory } ; activeFragment : string ; @ Input ( ) items : ObNavTreeItemModel [ ] = [ ] ; @ Input ( ) prefix = 'nav-tree' ; @ Input ( ) filterPattern : string ; @ Input ( ) labelFormatter : ( item : ObNavTreeItemModel , filterPattern ? : string ) => string = ObNavTreeComponent . DEFAULTS . LABEL_FORMATTER ( this . translate ) ; @ Input ( ) variant = ObNavTreeComponent . DEFAULTS . VARIANT ; @ Input ( ) activateAncestors = true ; @ Input ( ) treeAriaLabelledBy : string ; @ Input ( ) treeAriaLabel : string ; @ HostBinding ( 'class.ob-font-awesome' ) useFontAwesomeIcon : boolean ; private readonly unsubscribe = new Subject ( ) ; constructor ( private readonly route , private readonly translate , @ Optional ( ) @ Inject ( ObUseObliqueIcons ) useObliqueIcon ) { this . useFontAwesomeIcon = ! useObliqueIcon ; this . route . fragment . pipe ( takeUntil ( this . unsubscribe ) ) . subscribe ( fragment => { this . activeFragment = fragment ; } ) ; } ngOnDestroy ( ) { this . unsubscribe . next ( ) ; this . unsubscribe . complete ( ) ; } @ Input ( ) patternMatcher ( item , pattern = '' ) { pattern = pattern . replace ( /[.*+?^@${}()|[\]\\]/g , '\\$&' ) ; const label = this . translate . instant ( item . label , item . labelParams ) ; const match = new RegExp ( pattern , 'gi' ) . test ( label ) ; const childMatch = ( item . items || [ ] ) . some ( subItem => { const subMatch = this . patternMatcher ( subItem , pattern . replace ( /\\/g , '' ) ) ; if ( subMatch ) { item . collapsed = false ; } return subMatch ; } ) ; return match || childMatch ; } visible ( item ) { return ! this . filterPattern || this . patternMatcher ( item , this . filterPattern ) ; } itemKey ( item ) { return `${ this . prefix }-${ item . id }` ; } isLinkActive ( rla , item ) { const isLinkActive = rla . isActive ; return item . fragment ? isLinkActive && this . activeFragment === item . fragment : isLinkActive ; } changeCollapsed ( items , collapsed , all = false ) { items . filter ( item => item . items ) . forEach ( ( item ) => { item . collapsed = collapsed ; if ( all ) { this . changeCollapsed ( item . items , collapsed , all ) ; } } ) ; } public collapseAll ( ) { this . changeCollapsed ( this . items , true , true ) ; } public expandAll ( ) { this . changeCollapsed ( this . items , false , true ) ; } } export function defaultLabelFormatterFactory ( translate ) { const formatter = ( item , filterPattern ) => { filterPattern = ( filterPattern || '' ) . replace ( /[.*+?^@${}()|[\]\\]/g , '\\$&' ) ; const label = translate . instant ( item . label , item . labelParams ) ; return ! filterPattern ? label : label . replace ( new RegExp ( filterPattern , 'ig' ) , text => `<span class="${ ObNavTreeComponent . DEFAULTS . HIGHLIGHT }">${ text }</span>` ) ; } ; return formatter ; }