import { FocusMonitor , FocusOrigin } from '@angular/cdk/a11y' ; import { Renderer2 } from '@angular/core' ; import { BehaviorSubject , Subject } from 'rxjs' ; import { takeUntil } from 'rxjs/operators' ; import { FocusIndicatorOptions } from './focus-indicator-options.interface' ; import { FocusIndicatorOriginService } from './focus-indicator-origin/focus-indicator-origin.service' ; export class FocusIndicator { /** Apply a class when the item is focused */ set isFocused ( isFocused ) { isFocused ? this . _renderer . addClass ( this . _element , 'ux-focus-indicator-active' ) : this . _renderer . removeClass ( this . _element , 'ux-focus-indicator-active' ) ; this . isFocused$ . next ( isFocused ) ; } /** Provide a convenience getter to allow access to focus state without a subscription */ get isFocused ( ) : boolean { return this . isFocused$ . value ; } /** An observable to monitor the focus state */ readonly isFocused$ = new BehaviorSubject < boolean > ( false ) ; /** An observable to monitor the focus origin */ readonly origin$ = new Subject < FocusOrigin > ( ) ; /** Remove all subscriptions on destroy */ private readonly _onDestroy = new Subject < void > ( ) ; constructor ( private readonly _element , private readonly _focusMonitor , private readonly _renderer , private _options , private _focusIndicatorOrigin ) { if ( ! _element . classList . contains ( 'ux-focus-indicator' ) ) { this . initialise ( ) ; } } /** Setup the focus monitoring */ private initialise ( ) { this . _renderer . addClass ( this . _element , 'ux-focus-indicator' ) ; this . _focusMonitor . monitor ( this . _element , this . _options . checkChildren ) . pipe ( takeUntil ( this . _onDestroy ) ) . subscribe ( this . onFocusChange . bind ( this ) ) ; } /** Focus the element with a specific origin */ focus ( origin ? , options ? ) { this . _focusIndicatorOrigin . setOrigin ( origin ) ; this . _element . focus ( options ) ; } /** Tear down the subscriptions */ destroy ( ) { this . _onDestroy . next ( ) ; this . _onDestroy . complete ( ) ; this . isFocused$ . complete ( ) ; this . _focusMonitor . stopMonitoring ( this . _element ) ; } /** Allow the options to be updates */ setOptions ( options ) { this . _options = { ... this . _options , ... options } ; } /** Monitor changes to an elements focus state */ private onFocusChange ( origin ) { if ( origin === null ) { this . isFocused = false ; this . origin$ . next ( null ) ; return ; } const syntheticOrigin = this . _focusIndicatorOrigin . getOrigin ( ) ; this . origin$ . next ( syntheticOrigin || origin ) ; switch ( syntheticOrigin || origin ) { case 'mouse' : this . isFocused = this . _options . mouseFocusIndicator ; break ; case 'touch' : this . isFocused = this . _options . touchFocusIndicator ; break ; case 'keyboard' : this . isFocused = this . _options . keyboardFocusIndicator ; break ; case 'program' : this . isFocused = this . _options . programmaticFocusIndicator ; break ; default : this . isFocused = false ; } } }