import { AfterContentInit , ChangeDetectorRef , ContentChildren , Directive , EventEmitter , HostBinding , Input , OnDestroy , Output , QueryList } from '@angular/core' ; import { debounceTime , takeUntil } from 'rxjs/operators' ; import { Subject } from 'rxjs' ; import { SelectionItemDirective } from './selection-item.directive' ; import { SelectionMode , SelectionService } from './selection.service' ; import { SelectionStrategy } from './strategies/selection.strategy' ; @ Directive ( { selector : '[uxSelection]' , exportAs : 'ux-selection' , providers : [ SelectionService ] } ) export class SelectionDirective < T > implements AfterContentInit , OnDestroy { /** Defines the items that should be selected. */ @ Input ( ) set uxSelection ( items ) { this . _lastSelection = items ; this . _selectionService . selectOnly ( ... items ) ; } /** Can be used to enabled/disable selection behavior. */ @ Input ( ) set disabled ( disabled ) { this . _selectionService . setDisabled ( disabled ) ; } /**
     * Defines the selection behavior. Alternatively, custom selection behavior can be defined by defining a
     * class which extends SelectionStrategy, and providing an instance of the custom class to this property.
     * See below for details of the SelectionStrategy class.
     */ @ Input ( ) set mode ( mode ) { this . _selectionService . setStrategy ( mode ) ; } /**
     * Can be used to enable/disable click selection on items. This can be used to manually control the selection of an item,
     * for example, binding the selection state to a checkbox.
     */ @ Input ( ) set clickSelection ( isClickEnabled ) { this . _selectionService . isClickEnabled = isClickEnabled ; } /** Can be used to enable/disable keyboard navigation on items. Use this if you wish to provide custom keyboard controls for selection. */ @ Input ( ) set keyboardSelection ( isKeyboardEnabled ) { this . _selectionService . isKeyboardEnabled = isKeyboardEnabled ; } /**
     * The full set of selection items.
     * Only needed if the full set of `uxSelectionItem`s is not available, e.g. within a virtual scroll container.
     */ @ Input ( ) set selectionItems ( value ) { this . _hasExplicitDataset = ! ! value ; if ( value ) { this . _selectionService . dataset = value ; } } /** The tabstop of the selection outer element */ @ Input ( ) @ HostBinding ( 'attr.tabindex' ) tabindex : string | number = null ; /** This event will be triggered when there is a change to the selected items. It will contain an array of the currently selected items. */ @ Output ( ) uxSelectionChange = new EventEmitter < T [ ] > ( ) ; /** Access all items within the list */ @ ContentChildren ( SelectionItemDirective ) items : QueryList < SelectionItemDirective < T > > ; /** Unsubscribe from all observables on component destroy */ private _onDestroy = new Subject < void > ( ) ; /** Store the previous selection so we don't emit more than we have to */ private _lastSelection : ReadonlyArray < T > = [ ] ; /** Whether a value has been provided to the `selectionItems` input. */ private _hasExplicitDataset : boolean = false ; constructor ( private _selectionService , private _cdRef ) { _selectionService . selection$ . pipe ( debounceTime ( 0 ) , takeUntil ( this . _onDestroy ) ) . subscribe ( items => { if ( this . isSelectionChanged ( items ) ) { this . uxSelectionChange . emit ( items ) ; } this . _lastSelection = [ ... items ] ; } ) ; } ngAfterContentInit ( ) { this . update ( ) ; this . items . changes . pipe ( takeUntil ( this . _onDestroy ) ) . subscribe ( ( ) => this . update ( ) ) ; } ngOnDestroy ( ) { this . _onDestroy . next ( ) ; this . _onDestroy . complete ( ) ; } /**
     * Update the dataset to reflect the latest selection items
     */ update ( ) { if ( ! this . _hasExplicitDataset ) { this . _selectionService . dataset = this . items . map ( item => item . uxSelectionItem ) ; } if ( this . _selectionService . focus$ . getValue ( ) === null && this . _selectionService . dataset . length > 0 ) { this . _selectionService . focus$ . next ( this . _selectionService . dataset [ 0 ] ) ; } this . _cdRef . detectChanges ( ) ; } /**
     * Select all the items in the list
     */ selectAll ( ) { if ( this . _selectionService . isEnabled ) { this . _selectionService . strategy . selectAll ( ) ; } } /**
     * Deselect all currently selected items
     */ deselectAll ( ) { if ( this . _selectionService . isEnabled ) { this . _selectionService . strategy . deselectAll ( ) ; } } /**
     * Determine if the previous selection is the same as the current selection
     */ private isSelectionChanged ( selection ) { if ( ! this . _lastSelection && selection || this . _lastSelection . length !== selection . length ) { return true ; } if ( this . _lastSelection . length === 0 && selection . length === 0 ) { return false ; } return ! this . _lastSelection . every ( item => selection . indexOf ( item ) !== - 1 ) ; } }