import { Component, ElementRef, EventEmitter, OnDestroy, OnInit, Renderer2 } from '@angular/core'; import { pDatepickerAbstractComponent } from '../../base/p-datepicker-container'; import { pDatepickerConfig } from '../../p-datepicker.config'; import { DayViewModel } from '../../models'; import { pDatepickerActions } from '../../reducer/p-datepicker.actions'; import { pDatepickerEffects } from '../../reducer/p-datepicker.effects'; import { pDatepickerStore } from '../../reducer/p-datepicker.store'; import { PositioningService } from 'mirra-ui/positioning'; import { Subscription } from 'rxjs'; import { datepickerAnimation } from '../../datepicker-animations'; import { take } from 'rxjs/operators'; import { BsCustomDates } from './p-custom-dates-view.component'; @Component({ selector: 'p-daterangepicker-container', providers: [pDatepickerStore, pDatepickerEffects], templateUrl: './p-datepicker-view.html', host: { class: 'bottom', '(click)': '_stopPropagation($event)', role: 'dialog', 'aria-label': 'calendar' }, animations: [datepickerAnimation] }) export class pDaterangepickerContainerComponent extends pDatepickerAbstractComponent implements OnInit, OnDestroy { set value(value: Date[]) { this._effects.setRangeValue(value); } valueChange = new EventEmitter(); animationState = 'void'; _rangeStack: Date[] = []; chosenRange: Date[] = []; _subs: Subscription[] = []; constructor( _renderer: Renderer2, private _config: pDatepickerConfig, private _store: pDatepickerStore, private _element: ElementRef, private _actions: pDatepickerActions, _effects: pDatepickerEffects, private _positionService: PositioningService ) { super(); this._effects = _effects; this.customRanges = this._config.ranges; _renderer.setStyle(_element.nativeElement, 'display', 'block'); _renderer.setStyle(_element.nativeElement, 'position', 'absolute'); } ngOnInit(): void { this._positionService.setOptions({ modifiers: { flip: { enabled: this._config.adaptivePosition } }, allowedPositions: ['top', 'bottom'] }); this._positionService.event$ .pipe( take(1) ) .subscribe(() => { this._positionService.disable(); if (this._config.isAnimated) { this.animationState = this.isTopPosition ? 'animated-up' : 'animated-down'; return; } this.animationState = 'unanimated'; }); this.containerClass = this._config.containerClass; this.isOtherMonthsActive = this._config.selectFromOtherMonth; this._effects .init(this._store) // intial state options // todo: fix this, split configs .setOptions(this._config) // data binding view --> model .setBindings(this) // set event handlers .setEventHandlers(this) .registerDatepickerSideEffects(); // todo: move it somewhere else // on selected date change this._subs.push( this._store .select(state => state.selectedRange) .subscribe(date => { this.valueChange.emit(date); this.chosenRange = date; }) ); } get isTopPosition(): boolean { return this._element.nativeElement.classList.contains('top'); } positionServiceEnable(): void { this._positionService.enable(); } daySelectHandler(day: DayViewModel): void { if (!day) { return; } const isDisabled = this.isOtherMonthsActive ? day.isDisabled : (day.isOtherMonth || day.isDisabled); if (isDisabled) { return; } // if only one date is already selected // and user clicks on previous date // start selection from new date // but if new date is after initial one // than finish selection if (this._rangeStack.length === 1) { this._rangeStack = day.date >= this._rangeStack[0] ? [this._rangeStack[0], day.date] : [day.date]; } if (this._rangeStack.length === 0) { this._rangeStack = [day.date]; } this._store.dispatch(this._actions.selectRange(this._rangeStack)); if (this._rangeStack.length === 2) { this._rangeStack = []; } } ngOnDestroy(): void { for (const sub of this._subs) { sub.unsubscribe(); } this._effects.destroy(); } setRangeOnCalendar(dates: BsCustomDates): void { this._rangeStack = (dates === null) ? [] : (dates.value instanceof Date ? [dates.value] : dates.value); this._store.dispatch(this._actions.selectRange(this._rangeStack)); } }