UNPKG

@progress/kendo-angular-dateinputs

Version:

Kendo UI for Angular Date Inputs Package - Everything you need to add date selection functionality to apps (DatePicker, TimePicker, DateInput, DateRangePicker, DateTimePicker, Calendar, and MultiViewCalendar).

223 lines (222 loc) 10.3 kB
/**----------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the project root for more information *-------------------------------------------------------------------------------------------*/ import { ChangeDetectorRef, Directive, ElementRef, EventEmitter, Optional, Input, Output, Renderer2 } from '@angular/core'; import { Subscription, fromEvent } from 'rxjs'; import { DateRangeService } from './date-range.service'; import { MultiViewCalendarComponent } from '../calendar/multiview-calendar.component'; import { EMPTY_SELECTIONRANGE } from '../calendar/models/selection-range.interface'; import { isEqual } from '@progress/kendo-date-math'; import { clampRange, either, isEqualRange } from '../util'; import * as i0 from "@angular/core"; import * as i1 from "../calendar/multiview-calendar.component"; import * as i2 from "./date-range.service"; /** * A directive which manages the MultiViewCalendar range selection. * This directive will be `deprecated` in a future version. * We recommend using the MultiViewCalendar [range selection]({% slug selection_multiviewcalendar %}#toc-range-selection). */ export class DateRangeSelectionDirective { calendar; cdr; element; dateRangeService; /** * Specifies the auto-correction behavior. If the start date is greater than the end date, * the directive fixes the date range to a single date either on input change or on blur * ([see example](slug:autocorrect_daterange#toc-calendar-selection-directive)). * * By default, the auto-correction is triggered on change. * To disable this behavior, set the `autoCorrectOn` property to `none`. */ autoCorrectOn = 'change'; /** * Gets or sets the selection range of the calendar. When a new range is set, * the connected DateRangeService notifies all related parties. */ get selectionRange() { return this.calendar ? this.calendar.selectionRange : null; } set selectionRange(range) { if (!this.isEqualCalendarRange(range)) { this.setSelectionRange(range); } if (!isEqualRange(this.dateRangeService.selectionRange, range)) { this.dateRangeService.setRange(range); } this.updateFocusedDate(range); } /** * Gets or sets the active end of the selection range. This option determines which range end will be updated on * user interaction. When a new active end is set, the connected DateRangeService notifies all related parties. */ /** * Specifies which end of the selection range will be marked as active. The active end gets modified upon user * interaction. When a new active end is set, the wired DateRangeService notifies all related components. For * example, the start and end DateInput components. * * > If the selection range is undefined, the value is ignored. */ get activeRangeEnd() { return this.calendar.activeRangeEnd; } set activeRangeEnd(activeRange) { if (this.dateRangeService.activeRangeEnd === activeRange) { return; } this.calendar.activeRangeEnd = activeRange; this.dateRangeService.setActiveRangeEnd(activeRange); } /** * @hidden * When in adaptive mode range should not be set automatically on calendar value change but only on accept */ shouldSetRange = true; /** * Fires when the active range end is changed. For more information, refer to * the section on [events](slug:events_multiviewcalendar). */ activeRangeEndChange = new EventEmitter(); /** * Fires when the selection range is changed. For more information, refer to * the section on [events](slug:events_multiviewcalendar). */ selectionRangeChange = new EventEmitter(); get calendarRange() { return this.selectionRange || EMPTY_SELECTIONRANGE; } calendarSubscriptions = new Subscription(); range; constructor(calendar, cdr, element, renderer, dateRangeService) { this.calendar = calendar; this.cdr = cdr; this.element = element; this.dateRangeService = dateRangeService; this.dateRangeService = this.dateRangeService || new DateRangeService(renderer); renderer.setAttribute(element.nativeElement, 'aria-multiselectable', 'true'); } ngOnInit() { const calendar = this.calendar; const dateRangeService = this.dateRangeService; calendar.min = either(dateRangeService.min, calendar.min); calendar.max = either(dateRangeService.max, calendar.max); this.addSubscriptions(calendar.cellEnter.subscribe((value) => this.handleHover(value)), calendar.valueChange.subscribe((value) => this.handleChange(value)), dateRangeService.focusedDate$.subscribe(focusedDate => { if (!isEqual(calendar.focusedDate, focusedDate)) { calendar.focusedDate = focusedDate; } }), dateRangeService.activeRangeEnd$.subscribe(rangeEnd => { if (calendar.activeRangeEnd === rangeEnd) { return; } calendar.activeRangeEnd = rangeEnd; this.activeRangeEndChange.emit(rangeEnd); this.cdr.markForCheck(); }), dateRangeService.range$.subscribe(range => { if (!this.isEqualCalendarRange(range)) { this.acceptAndEmit(range); } this.updateFocusedDate(range); }), fromEvent(this.element.nativeElement, 'blur').subscribe(() => this.handleBlur())); } ngOnDestroy() { this.calendarSubscriptions.unsubscribe(); } /** * @hidden */ handleChange(value) { const service = this.dateRangeService; const autoCorrect = this.autoCorrectOn === 'change' && this.shouldAutoCorrect(value); const activeEnd = this.calendar.activeRangeEnd !== 'end' ? 'end' : (autoCorrect ? 'end' : 'start'); this.range = autoCorrect ? clampRange(value) : this.updateRange(value); if (!isEqualRange(service.selectionRange, this.range)) { this.acceptAndEmit(this.range); service.setActiveRangeEnd(activeEnd); if (this.shouldSetRange) { this.setRange(); } } } setRange(range) { this.dateRangeService.setRange(this.range ? this.range : range); } addSubscriptions(...subscriptions) { subscriptions.map(s => this.calendarSubscriptions.add(s)); } isEqualCalendarRange(range) { return isEqualRange(this.calendar.selectionRange, range); } handleBlur() { const { start, end } = this.calendarRange; const autoCorrect = this.autoCorrectOn === 'blur' && start !== null && end !== null && end < start; if (autoCorrect) { this.dateRangeService.setRange(clampRange(start)); } } handleHover(value) { if (this.hasCompleteRange()) { return; } const { start, end } = this.calendarRange; const activeRangeEnd = this.calendar.activeRangeEnd; const updateRange = (start && activeRangeEnd === 'end') || (end && activeRangeEnd === 'start'); if (updateRange) { this.setSelectionRange(this.updateRange(value)); } } hasCompleteRange() { const { start, end } = this.dateRangeService.selectionRange || EMPTY_SELECTIONRANGE; return Boolean(start) && Boolean(end); } shouldAutoCorrect(value) { const { end, start } = this.calendarRange; if (this.calendar.activeRangeEnd !== 'end') { return end !== null && value > end; } else { return start !== null && value < start; } } updateFocusedDate(range) { if (!range || this.dateRangeService.focusedDate) { return; } this.dateRangeService.setFocusedDate(range.start || range.end); } updateRange(value) { const { end, start } = this.calendarRange; return this.calendar.activeRangeEnd !== 'end' ? ({ start: value, end }) : ({ start, end: value }); } setSelectionRange(range) { this.calendar.selectionRange = range; this.calendar.writeValue(null); } acceptAndEmit(range) { this.setSelectionRange(range); this.selectionRangeChange.emit(range); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DateRangeSelectionDirective, deps: [{ token: i1.MultiViewCalendarComponent }, { token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i2.DateRangeService, optional: true }], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: DateRangeSelectionDirective, isStandalone: true, selector: "[kendoDateRangeSelection]", inputs: { autoCorrectOn: "autoCorrectOn", selectionRange: "selectionRange", activeRangeEnd: "activeRangeEnd", shouldSetRange: "shouldSetRange" }, outputs: { activeRangeEndChange: "activeRangeEndChange", selectionRangeChange: "selectionRangeChange" }, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: DateRangeSelectionDirective, decorators: [{ type: Directive, args: [{ selector: '[kendoDateRangeSelection]', standalone: true }] }], ctorParameters: function () { return [{ type: i1.MultiViewCalendarComponent }, { type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i2.DateRangeService, decorators: [{ type: Optional }] }]; }, propDecorators: { autoCorrectOn: [{ type: Input }], selectionRange: [{ type: Input }], activeRangeEnd: [{ type: Input }], shouldSetRange: [{ type: Input }], activeRangeEndChange: [{ type: Output }], selectionRangeChange: [{ type: Output }] } });