license import { TrackByFunction } from '@angular/core' ; import { Subject } from 'rxjs' ; /**
 * Maintains a set of selected values. One or more values can be added to or removed from the
 * selection.
 */ interface TrackBySelection < T > { isSelected ( value ) : boolean ; select ( ... values ) : void ; deselect ( ... values ) : void ; changed : Subject < SelectionChange < T > > ; } /**
 * A selectable value with an optional index. The index is required when the selection is used with
 * `trackBy`.
 */ export interface SelectableWithIndex < T > { value : T ; index ? : number ; } /**
 * Represents the change in the selection set.
 */ export interface SelectionChange < T > { before : SelectableWithIndex < T > [ ] ; after : SelectableWithIndex < T > [ ] ; } /**
 * Maintains a set of selected items. Support selecting and deselecting items, and checking if a
 * value is selected.
 * When constructed with a `trackByFn`, all the items will be identified by applying the `trackByFn`
 * on them. Because `trackByFn` requires the index of the item to be passed in, the `index` field is
 * expected to be set when calling `isSelected`, `select` and `deselect`.
 */ export class SelectionSet < T > implements TrackBySelection < T > { private _selectionMap = new Map < T | ReturnType < TrackByFunction < T > > , SelectableWithIndex < T > > ( ) ; changed = new Subject < SelectionChange < T > > ( ) ; constructor ( private _multiple = false , private _trackByFn ? ) { } isSelected ( value ) { return this . _selectionMap . has ( this . _getTrackedByValue ( value ) ) ; } select ( ... selects ) { if ( ! this . _multiple && selects . length > 1 && ( typeof ngDevMode === 'undefined' || ngDevMode ) ) { throw Error ( 'SelectionSet: not multiple selection' ) ; } const before = this . _getCurrentSelection ( ) ; if ( ! this . _multiple ) { this . _selectionMap . clear ( ) ; } const toSelect = [ ] ; for ( const select of selects ) { if ( this . isSelected ( select ) ) { continue ; } toSelect . push ( select ) ; this . _markSelected ( this . _getTrackedByValue ( select ) , select ) ; } const after = this . _getCurrentSelection ( ) ; this . changed . next ( { before , after } ) ; } deselect ( ... selects ) { if ( ! this . _multiple && selects . length > 1 && ( typeof ngDevMode === 'undefined' || ngDevMode ) ) { throw Error ( 'SelectionSet: not multiple selection' ) ; } const before = this . _getCurrentSelection ( ) ; const toDeselect = [ ] ; for ( const select of selects ) { if ( ! this . isSelected ( select ) ) { continue ; } toDeselect . push ( select ) ; this . _markDeselected ( this . _getTrackedByValue ( select ) ) ; } const after = this . _getCurrentSelection ( ) ; this . changed . next ( { before , after } ) ; } private _markSelected ( key , toSelect ) { this . _selectionMap . set ( key , toSelect ) ; } private _markDeselected ( key ) { this . _selectionMap . delete ( key ) ; } private _getTrackedByValue ( select ) { if ( ! this . _trackByFn ) { return select . value ; } if ( select . index == null && ( typeof ngDevMode === 'undefined' || ngDevMode ) ) { throw Error ( 'SelectionSet: index required when trackByFn is used.' ) ; } return this . _trackByFn ( select . index ! , select . value ) ; } private _getCurrentSelection ( ) { return Array . from ( this . _selectionMap . values ( ) ) ; } }