1
- import { Subscription } from 'rxjs' ;
2
- import { take } from 'rxjs/operators' ;
1
+ import { fromEvent , merge , Subject } from 'rxjs' ;
2
+ import { filter , take , takeUntil } from 'rxjs/operators' ;
3
3
import {
4
4
ChangeDetectionStrategy ,
5
5
ChangeDetectorRef ,
6
6
Component ,
7
+ ElementRef ,
8
+ EventEmitter ,
9
+ forwardRef ,
7
10
Input ,
11
+ NgZone ,
8
12
OnChanges ,
9
- TemplateRef ,
10
- forwardRef ,
13
+ OnDestroy ,
11
14
OnInit ,
12
- SimpleChanges ,
13
- EventEmitter ,
14
15
Output ,
15
- OnDestroy ,
16
- ElementRef ,
17
- NgZone ,
16
+ SimpleChanges ,
17
+ TemplateRef ,
18
+ ViewChild ,
18
19
ViewEncapsulation
19
20
} from '@angular/core' ;
20
- import { NG_VALUE_ACCESSOR , ControlValueAccessor } from '@angular/forms' ;
21
+ import { ControlValueAccessor , NG_VALUE_ACCESSOR } from '@angular/forms' ;
21
22
import { NgbCalendar } from './ngb-calendar' ;
22
23
import { NgbDate } from './ngb-date' ;
23
24
import { NgbDatepickerService } from './datepicker-service' ;
@@ -29,6 +30,7 @@ import {NgbDateAdapter} from './adapters/ngb-date-adapter';
29
30
import { NgbDateStruct } from './ngb-date-struct' ;
30
31
import { NgbDatepickerI18n } from './datepicker-i18n' ;
31
32
import { isChangedDate } from './datepicker-tools' ;
33
+ import { hasClassName } from '../util/util' ;
32
34
33
35
const NGB_DATEPICKER_VALUE_ACCESSOR = {
34
36
provide : NG_VALUE_ACCESSOR ,
@@ -85,7 +87,7 @@ export interface NgbDatepickerNavigateEvent {
85
87
</ngb-datepicker-navigation>
86
88
</div>
87
89
88
- <div class="ngb-dp-months" (keydown)="onKeyDown($event)" (focusin)="showFocus(true)" (focusout)="showFocus(false )">
90
+ <div #months class="ngb-dp-months" (keydown)="onKeyDown($event)">
89
91
<ng-template ngFor let-month [ngForOf]="model.months" let-i="index">
90
92
<div class="ngb-dp-month">
91
93
<div *ngIf="navigation === 'none' || (displayMonths > 1 && navigation === 'select')"
@@ -111,9 +113,10 @@ export class NgbDatepicker implements OnDestroy,
111
113
OnChanges , OnInit , ControlValueAccessor {
112
114
model : DatepickerViewModel ;
113
115
116
+ @ViewChild ( 'months' ) private _monthsEl : ElementRef < HTMLElement > ;
114
117
private _controlValue : NgbDate ;
115
- private _subscription : Subscription ;
116
- private _selectSubscription : Subscription ;
118
+ private _destroyed$ = new Subject < void > ( ) ;
119
+
117
120
/**
118
121
* Reference for the custom template for the day display
119
122
*/
@@ -214,9 +217,9 @@ export class NgbDatepicker implements OnDestroy,
214
217
'maxDate' , 'navigation' , 'outsideDays' , 'showWeekdays' , 'showWeekNumbers' , 'startDate' ]
215
218
. forEach ( input => this [ input ] = config [ input ] ) ;
216
219
217
- this . _selectSubscription = _service . select$ . subscribe ( date => { this . select . emit ( date ) ; } ) ;
220
+ _service . select$ . pipe ( takeUntil ( this . _destroyed$ ) ) . subscribe ( date => { this . select . emit ( date ) ; } ) ;
218
221
219
- this . _subscription = _service . model$ . subscribe ( model => {
222
+ _service . model$ . pipe ( takeUntil ( this . _destroyed$ ) ) . subscribe ( model => {
220
223
const newDate = model . firstDate ;
221
224
const oldDate = this . model ? this . model . firstDate : null ;
222
225
const newSelectedDate = model . selectedDate ;
@@ -271,11 +274,25 @@ export class NgbDatepicker implements OnDestroy,
271
274
this . _service . open ( NgbDate . from ( date ? date . day ? date as NgbDateStruct : { ...date , day : 1 } : null ) ) ;
272
275
}
273
276
274
- ngOnDestroy ( ) {
275
- this . _subscription . unsubscribe ( ) ;
276
- this . _selectSubscription . unsubscribe ( ) ;
277
+ ngAfterContentInit ( ) {
278
+ this . _ngZone . runOutsideAngular ( ( ) => {
279
+ const focusIns$ = fromEvent < FocusEvent > ( this . _monthsEl . nativeElement , 'focusin' ) ;
280
+ const focusOuts$ = fromEvent < FocusEvent > ( this . _monthsEl . nativeElement , 'focusout' ) ;
281
+
282
+ // we're changing 'focusVisible' only when entering or leaving months view
283
+ // and ignoring all focus events where both 'target' and 'related' target are day cells
284
+ merge ( focusIns$ , focusOuts$ )
285
+ . pipe (
286
+ filter (
287
+ ( { target, relatedTarget} ) =>
288
+ ! ( hasClassName ( target , 'ngb-dp-day' ) && hasClassName ( relatedTarget , 'ngb-dp-day' ) ) ) ,
289
+ takeUntil ( this . _destroyed$ ) )
290
+ . subscribe ( ( { type} ) => this . _ngZone . run ( ( ) => this . _service . focusVisible = type === 'focusin' ) ) ;
291
+ } ) ;
277
292
}
278
293
294
+ ngOnDestroy ( ) { this . _destroyed$ . next ( ) ; }
295
+
279
296
ngOnInit ( ) {
280
297
if ( this . model === undefined ) {
281
298
[ 'dayTemplateData' , 'displayMonths' , 'markDisabled' , 'firstDayOfWeek' , 'navigation' , 'minDate' , 'maxDate' ,
@@ -322,8 +339,6 @@ export class NgbDatepicker implements OnDestroy,
322
339
323
340
setDisabledState ( isDisabled : boolean ) { this . _service . disabled = isDisabled ; }
324
341
325
- showFocus ( focusVisible : boolean ) { this . _service . focusVisible = focusVisible ; }
326
-
327
342
writeValue ( value ) {
328
343
this . _controlValue = NgbDate . from ( this . _ngbDateAdapter . fromModel ( value ) ) ;
329
344
this . _service . select ( this . _controlValue ) ;
0 commit comments