import { DOWN_ARROW , SPACE , UP_ARROW } from '@angular/cdk/keycodes' ; import { SelectionStrategy } from './selection.strategy' ; export class RowSelectionStrategy < T > extends SelectionStrategy < T > { private _selection : Selection < T > = { start : null , end : null } ; /**
     * By default on shift click the browser will highlight
     * text. This looks bad and we don't want this to occur
     */ mousedown ( event ) { event . preventDefault ( ) ; } /**
     * When a row is clicked we want to handle selection
     */ click ( event , data ) { const { ctrlKey , shiftKey } = event ; if ( shiftKey ) { return this . multipleSelect ( data ) ; } if ( ctrlKey ) { return this . toggle ( data ) ; } this . singleSelect ( data ) ; } /**
     * To support full keyboard control we need to support the following:
     * 1. Arrow keys to navigate up and down
     * 2. Spacebar to toggle selection
     * 3. Shift + Arrow keys to multiple select
     * 4. Ctrl + Arrow keys to allow retained selection and navigation
     */ keydown ( event , data ) { switch ( event . which ) { case UP_ARROW : case DOWN_ARROW : event . preventDefault ( ) ; this . navigate ( event , data ) ; break ; case SPACE : event . preventDefault ( ) ; this . selectionService . strategy . toggle ( data ) ; this . selectionService . activate ( data ) ; break ; } } /**
     * Override the standard toggle function to store or clear the
     * most recently selected item
     */ toggle ( data ) { super . toggle ( data ) ; this . selectionService . isSelected ( data ) ? this . setSelectionStart ( data ) : this . clearSelection ( ) ; } /**
     * Clear all other selected items and select only
     * the most recently selected item
     */ private singleSelect ( data ) { this . deselectAll ( ) ; this . select ( data ) ; this . setSelectionStart ( data ) ; } /**
     * Handle multiple selection:
     * 1. If no start item selected - select it
     * 2. If a start item has been selected - select all in between
     * 3. If a start and end item have been selected clear the range and then select the new range
     */ protected multipleSelect ( data ) { if ( ! this . _selection . start ) { this . select ( data ) ; return this . setSelectionStart ( data ) ; } if ( this . _selection . start && this . _selection . end ) { this . deselect ( ... this . getSelectedItems ( ) ) ; } this . setSelectionEnd ( data ) ; this . select ( ... this . getSelectedItems ( ) ) ; } /**
     * Set the selection start point. If there was previously a
     * selection end point then clear it as this is a new selection
     */ private setSelectionStart ( data ) { this . _selection . start = data ; this . _selection . end = null ; this . selectionService . activate ( data ) ; } /**
     * Set the selection end point
     */ private setSelectionEnd ( data ) { this . _selection . end = data ; this . selectionService . activate ( data ) ; } /**
     * Clear both start and end selection points
     */ protected clearSelection ( deactivate = true ) { this . _selection = { start : null , end : null } ; if ( deactivate ) { this . selectionService . deactivate ( ) ; } } /**
     * Determine all the items affected by the current selection.
     * Note that the end point may be above the start point so
     * we need to account for this.
     */ private getSelectedItems ( ) { const { dataset } = this . selectionService ; const startIdx = dataset . indexOf ( this . _selection . start ) ; const endIdx = dataset . indexOf ( this . _selection . end ) ; return dataset . slice ( Math . min ( startIdx , endIdx ) , Math . max ( startIdx , endIdx ) + 1 ) ; } /**
     * Activate the sibling item when arrow keys are pressed
     */ private navigate ( event , data ) { const { ctrlKey , shiftKey } = event ; if ( ! ctrlKey && ! shiftKey ) { this . deselectAll ( ) ; this . clearSelection ( false ) ; } const sibling = this . selectionService . activateSibling ( event . which === UP_ARROW ) ; if ( shiftKey && sibling ) { if ( ! this . _selection . start ) { this . multipleSelect ( data ) ; } this . multipleSelect ( sibling ) ; } } } export interface Selection < T > { start : T ; end : T ; }