igniteui-angular
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
1,067 lines (1,063 loc) • 146 kB
JavaScript
import * as i2 from 'igniteui-angular/core';
import { PickerInteractionMode, PickerHeaderOrientation, IgxPickerToggleComponent, IgxPickerClearComponent, DateRangePickerResourceStringsEN, CalendarDay, DateTimeUtil, IgxOverlayService, PlatformUtil, PickerCalendarOrientation, getCurrentResourceStrings, DatePickerResourceStringsEN, AutoPositionStrategy, AbsoluteScrollStrategy, isDate, isDateInRanges, DateRangeType, IgxPickerActionsDirective, clamp, parseDate, calendarRange } from 'igniteui-angular/core';
import * as i0 from '@angular/core';
import { inject, ElementRef, LOCALE_ID, EventEmitter, booleanAttribute, ViewChild, ContentChildren, Output, Input, Directive, ChangeDetectionStrategy, Component, HostListener, HostBinding, Injector, Renderer2, ChangeDetectorRef, ViewContainerRef, ContentChild, Pipe, TemplateRef, NgModule } from '@angular/core';
import { NgControl, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
import * as i4 from 'igniteui-angular/calendar';
import { IgxCalendarComponent, IgxCalendarHeaderTitleTemplateDirective, IgxCalendarHeaderTemplateDirective, IgxCalendarSubheaderTemplateDirective, CalendarSelection } from 'igniteui-angular/calendar';
import * as i3 from 'igniteui-angular/input-group';
import { IGX_INPUT_GROUP_TYPE, IgxInputGroupComponent, IgxPrefixDirective, IgxSuffixDirective, IgxInputState, IgxInputDirective, IgxReadOnlyInputDirective, IgxLabelDirective, IgxHintDirective, IgxInputGroupBase } from 'igniteui-angular/input-group';
import { Subject, merge, noop, fromEvent } from 'rxjs';
import { takeUntil, filter } from 'rxjs/operators';
import { IgxButtonDirective, IgxRippleDirective, IgxDividerDirective, IgxDateTimeEditorDirective, IgxTextSelectionDirective } from 'igniteui-angular/directives';
import { IgxIconComponent } from 'igniteui-angular/icon';
import { fadeOut, fadeIn } from 'igniteui-angular/animations';
import { getLocaleFirstDayOfWeek, NgTemplateOutlet } from '@angular/common';
import { IgxChipComponent } from 'igniteui-angular/chips';
class PickerBaseDirective {
/**
* @example
* ```html
* <igx-date-picker locale="jp"></igx-date-picker>
* ```
*/
/**
* Gets the `locale` of the date-picker.
* If not set, defaults to applciation's locale..
*/
get locale() {
return this._locale;
}
/**
* Sets the `locale` of the date-picker.
* Expects a valid BCP 47 language tag.
*/
set locale(value) {
this._locale = value;
// if value is invalid, set it back to _localeId
try {
getLocaleFirstDayOfWeek(this._locale);
}
catch (e) {
this._locale = this._localeId;
}
}
/**
* Gets the start day of the week.
* Can return a numeric or an enum representation of the week day.
* If not set, defaults to the first day of the week for the application locale.
*/
get weekStart() {
return this._weekStart ?? getLocaleFirstDayOfWeek(this._locale);
}
/**
* Sets the start day of the week.
* Can be assigned to a numeric value or to `WEEKDAYS` enum value.
*/
set weekStart(value) {
this._weekStart = value;
}
/**
* Determines how the picker's input will be styled.
*
* @remarks
* Default is `box`.
*
* @example
* ```html
* <igx-date-picker [type]="'line'"></igx-date-picker>
* ```
*/
set type(val) {
this._type = val;
}
get type() {
return this._type || this._inputGroupType;
}
/**
* Gets the picker's pop-up state.
*
* @example
* ```typescript
* const state = this.picker.collapsed;
* ```
*/
get collapsed() {
return this._collapsed;
}
/** @hidden @internal */
get isDropdown() {
return this.mode === PickerInteractionMode.DropDown;
}
/**
* Returns if there's focus within the picker's element OR popup container
* @hidden @internal
*/
get isFocused() {
const document = this.element.nativeElement?.getRootNode();
if (!document?.activeElement)
return false;
return this.element.nativeElement.contains(document.activeElement)
|| !this.collapsed && this.toggleContainer.contains(document.activeElement);
}
constructor() {
this.element = inject(ElementRef);
this._localeId = inject(LOCALE_ID);
this._inputGroupType = inject(IGX_INPUT_GROUP_TYPE, { optional: true });
/**
* Sets the `placeholder` of the picker's input.
*
* @example
* ```html
* <igx-date-picker [placeholder]="'Choose your date'"></igx-date-picker>
* ```
*/
this.placeholder = '';
/**
* Can be `dropdown` with editable input field or `dialog` with readonly input field.
*
* @remarks
* Default mode is `dropdown`
*
* @example
* ```html
* <igx-date-picker mode="dialog"></igx-date-picker>
* ```
*/
this.mode = PickerInteractionMode.DropDown;
/**
* Gets/Sets the orientation of the `IgxDatePickerComponent` header.
*
* @example
* ```html
* <igx-date-picker headerOrientation="vertical"></igx-date-picker>
* ```
*/
this.headerOrientation = PickerHeaderOrientation.Horizontal;
/**
* Gets/Sets whether the header is hidden in dialog mode.
*
* @example
* ```html
* <igx-date-picker mode="dialog" [hideHeader]="true"></igx-date-picker>
* ```
*/
this.hideHeader = false;
/**
* Enables or disables the picker.
*
* @example
* ```html
* <igx-date-picker [disabled]="'true'"></igx-date-picker>
* ```
*/
this.disabled = false;
/**
* Emitted when the calendar has started opening, cancelable.
*
* @example
* ```html
* <igx-date-picker (opening)="handleOpening($event)"></igx-date-picker>
* ```
*/
this.opening = new EventEmitter();
/**
* Emitted after the calendar has opened.
*
* @example
* ```html
* <igx-date-picker (opened)="handleOpened($event)"></igx-date-picker>
* ```
*/
this.opened = new EventEmitter();
/**
* Emitted when the calendar has started closing, cancelable.
*
* @example
* ```html
* <igx-date-picker (closing)="handleClosing($event)"></igx-date-picker>
* ```
*/
this.closing = new EventEmitter();
/**
* Emitted after the calendar has closed.
*
* @example
* ```html
* <igx-date-picker (closed)="handleClosed($event)"></igx-date-picker>
* ```
*/
this.closed = new EventEmitter();
this._collapsed = true;
this._destroy$ = new Subject();
this.locale = this.locale || this._localeId;
}
/** @hidden @internal */
ngAfterViewInit() {
this.subToIconsClicked(this.toggleComponents, () => this.toggle());
this.subToIconsClicked(this.clearComponents, () => this.clear());
}
/** @hidden @internal */
ngAfterContentChecked() {
if (this.inputGroup && this.prefixes?.length > 0) {
this.inputGroup.prefixes = this.prefixes;
}
if (this.inputGroup && this.suffixes?.length > 0) {
this.inputGroup.suffixes = this.suffixes;
}
}
/** @hidden @internal */
ngOnDestroy() {
this._destroy$.next();
this._destroy$.complete();
}
/** Subscribes to the click events of toggle/clear icons in a query */
subToIconsClicked(components, handler) {
const subscribeToClick = componentList => {
componentList.forEach(component => {
component.clicked
.pipe(takeUntil(merge(componentList.changes, this._destroy$)))
.subscribe(handler);
});
};
subscribeToClick(components);
components.changes.pipe(takeUntil(this._destroy$))
.subscribe(() => subscribeToClick(components));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: PickerBaseDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "21.0.2", type: PickerBaseDirective, isStandalone: true, inputs: { inputFormat: "inputFormat", displayFormat: "displayFormat", placeholder: "placeholder", mode: "mode", headerOrientation: "headerOrientation", hideHeader: ["hideHeader", "hideHeader", booleanAttribute], overlaySettings: "overlaySettings", disabled: ["disabled", "disabled", booleanAttribute], locale: "locale", weekStart: "weekStart", outlet: "outlet", type: "type", tabIndex: "tabIndex" }, outputs: { opening: "opening", opened: "opened", closing: "closing", closed: "closed" }, queries: [{ propertyName: "toggleComponents", predicate: IgxPickerToggleComponent, descendants: true }, { propertyName: "clearComponents", predicate: IgxPickerClearComponent, descendants: true }, { propertyName: "prefixes", predicate: IgxPrefixDirective, descendants: true }, { propertyName: "suffixes", predicate: IgxSuffixDirective, descendants: true }], viewQueries: [{ propertyName: "inputGroup", first: true, predicate: IgxInputGroupComponent, descendants: true }], ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: PickerBaseDirective, decorators: [{
type: Directive
}], ctorParameters: () => [], propDecorators: { inputFormat: [{
type: Input
}], displayFormat: [{
type: Input
}], placeholder: [{
type: Input
}], mode: [{
type: Input
}], headerOrientation: [{
type: Input
}], hideHeader: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], overlaySettings: [{
type: Input
}], disabled: [{
type: Input,
args: [{ transform: booleanAttribute }]
}], locale: [{
type: Input
}], weekStart: [{
type: Input
}], outlet: [{
type: Input
}], type: [{
type: Input
}], tabIndex: [{
type: Input
}], opening: [{
type: Output
}], opened: [{
type: Output
}], closing: [{
type: Output
}], closed: [{
type: Output
}], toggleComponents: [{
type: ContentChildren,
args: [IgxPickerToggleComponent, { descendants: true }]
}], clearComponents: [{
type: ContentChildren,
args: [IgxPickerClearComponent, { descendants: true }]
}], prefixes: [{
type: ContentChildren,
args: [IgxPrefixDirective, { descendants: true }]
}], suffixes: [{
type: ContentChildren,
args: [IgxSuffixDirective, { descendants: true }]
}], inputGroup: [{
type: ViewChild,
args: [IgxInputGroupComponent]
}] } });
class IgxPredefinedRangesAreaComponent {
constructor() {
this.usePredefinedRanges = false;
this.customRanges = [];
this.resourceStrings = DateRangePickerResourceStringsEN;
this.rangeSelect = new EventEmitter();
this.trackByLabel = (i, r) => r.label;
}
get ranges() {
const base = this.usePredefinedRanges ? this.getPredefinedRanges() : [];
return [...base, ...(this.customRanges ?? [])];
}
onSelect(range) {
this.rangeSelect.emit(range);
}
getLabel(rs, shortKey, prefixedKey, fallback) {
return rs?.[shortKey] ?? rs?.[prefixedKey] ?? fallback;
}
getPredefinedRanges() {
const today = CalendarDay.today;
const rs = this.resourceStrings ?? {};
const labels = {
last7Days: this.getLabel(rs, 'last7Days', 'igx_date_range_picker_last7Days', 'Last 7 Days'),
currentMonth: this.getLabel(rs, 'currentMonth', 'igx_date_range_picker_currentMonth', 'Current Month'),
last30Days: this.getLabel(rs, 'last30Days', 'igx_date_range_picker_last30Days', 'Last 30 Days'),
yearToDate: this.getLabel(rs, 'yearToDate', 'igx_date_range_picker_yearToDate', 'Year to Date')
};
const startOfMonth = new Date(today.native.getFullYear(), today.native.getMonth(), 1);
const endOfMonth = new Date(today.native.getFullYear(), today.native.getMonth() + 1, 0);
const startOfYear = new Date(today.native.getFullYear(), 0, 1);
const predefinedRanges = [
{ key: 'last7Days', get: () => ({ start: today.add('day', -7).native, end: today.native }) },
{ key: 'currentMonth', get: () => ({ start: startOfMonth, end: endOfMonth }) },
{ key: 'last30Days', get: () => ({ start: today.add('day', -29).native, end: today.native }) },
{ key: 'yearToDate', get: () => ({ start: startOfYear, end: today.native }) },
];
return predefinedRanges.map(range => ({
label: labels[range.key],
dateRange: range.get()
}));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxPredefinedRangesAreaComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: IgxPredefinedRangesAreaComponent, isStandalone: true, selector: "igx-predefined-ranges-area", inputs: { usePredefinedRanges: "usePredefinedRanges", customRanges: "customRanges", resourceStrings: "resourceStrings" }, outputs: { rangeSelect: "rangeSelect" }, ngImport: i0, template: "<div class=\"igx-predefined-ranges\" role=\"group\" aria-label=\"Predefined ranges\">\n @for (r of ranges; track r.label) {\n <igx-chip (click)=\"onSelect(r.dateRange)\">\n {{ r.label }}\n </igx-chip>\n }\n</div>\n", styles: [":host{display:block}.igx-predefined-ranges{display:flex;flex-wrap:wrap;gap:.5rem;padding:.5rem .75rem}\n"], dependencies: [{ kind: "component", type: IgxChipComponent, selector: "igx-chip", inputs: ["variant", "id", "tabIndex", "data", "draggable", "animateOnRelease", "hideBaseOnDrag", "removable", "removeIcon", "selectable", "selectIcon", "class", "disabled", "selected", "color", "resourceStrings"], outputs: ["selectedChange", "moveStart", "moveEnd", "remove", "chipClick", "selectedChanging", "selectedChanged", "keyDown", "dragEnter", "dragLeave", "dragOver", "dragDrop"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxPredefinedRangesAreaComponent, decorators: [{
type: Component,
args: [{ selector: 'igx-predefined-ranges-area', standalone: true, imports: [IgxChipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"igx-predefined-ranges\" role=\"group\" aria-label=\"Predefined ranges\">\n @for (r of ranges; track r.label) {\n <igx-chip (click)=\"onSelect(r.dateRange)\">\n {{ r.label }}\n </igx-chip>\n }\n</div>\n", styles: [":host{display:block}.igx-predefined-ranges{display:flex;flex-wrap:wrap;gap:.5rem;padding:.5rem .75rem}\n"] }]
}], propDecorators: { usePredefinedRanges: [{
type: Input
}], customRanges: [{
type: Input
}], resourceStrings: [{
type: Input
}], rangeSelect: [{
type: Output
}] } });
/** @hidden */
class IgxCalendarContainerComponent {
constructor() {
this.calendarClose = new EventEmitter();
this.calendarCancel = new EventEmitter();
this.todaySelection = new EventEmitter();
this.rangeSelected = new EventEmitter();
this.styleClass = 'igx-date-picker';
this.usePredefinedRanges = false;
this.customRanges = [];
this.vertical = false;
this.mode = PickerInteractionMode.DropDown;
}
get dropdownCSS() {
return this.mode === PickerInteractionMode.DropDown;
}
onEscape(event) {
event.preventDefault();
this.calendarClose.emit();
}
get isReadonly() {
return this.mode === PickerInteractionMode.Dialog;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxCalendarContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: IgxCalendarContainerComponent, isStandalone: true, selector: "igx-calendar-container", outputs: { calendarClose: "calendarClose", calendarCancel: "calendarCancel", todaySelection: "todaySelection", rangeSelected: "rangeSelected" }, host: { listeners: { "keydown.alt.arrowup": "onEscape($event)" }, properties: { "class.igx-date-picker": "this.styleClass", "class.igx-date-picker--dropdown": "this.dropdownCSS" } }, viewQueries: [{ propertyName: "calendar", first: true, predicate: IgxCalendarComponent, descendants: true, static: true }], ngImport: i0, template: "<ng-template #defaultPickerActions>\n @if (closeButtonLabel || cancelButtonLabel || todayButtonLabel) {\n <div class=\"igx-date-picker__buttons\">\n @if (cancelButtonLabel) {\n <button\n #cancelButton type=\"button\"\n igxButton=\"flat\" igxRipple\n (click)=\"calendarCancel.emit({ owner: this})\"\n >\n {{ cancelButtonLabel }}\n </button>\n }\n @if (closeButtonLabel) {\n <button\n #closeButton\n type=\"button\"\n igxButton=\"flat\"\n igxRipple\n (click)=\"calendarClose.emit({ owner: this})\"\n >\n {{ closeButtonLabel }}\n </button>\n }\n @if (todayButtonLabel) {\n <button\n #todayButton\n type=\"button\"\n igxButton=\"flat\"\n igxRipple\n (click)=\"todaySelection.emit({ owner: this })\"\n >\n {{ todayButtonLabel }}\n </button>\n }\n </div>\n }\n</ng-template>\n\n<igx-calendar></igx-calendar>\n @if( usePredefinedRanges || (customRanges?.length || 0) > 0 ){\n <igx-predefined-ranges-area\n [usePredefinedRanges]=\"usePredefinedRanges\"\n [customRanges]=\"customRanges\"\n [resourceStrings]=\"resourceStrings\"\n (rangeSelect)=\"rangeSelected.emit($event)\">\n </igx-predefined-ranges-area>\n }\n@if (pickerActions?.template || (closeButtonLabel || todayButtonLabel)) {\n <igx-divider></igx-divider>\n}\n@if (pickerActions?.template || (closeButtonLabel || cancelButtonLabel || todayButtonLabel)) {\n <div class=\"igx-date-picker__actions\">\n <ng-container\n *ngTemplateOutlet=\"\n pickerActions?.template || defaultPickerActions;\n context: { $implicit: calendar }\n \"\n >\n </ng-container>\n </div>\n}\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "directive", type: IgxButtonDirective, selector: "[igxButton]", inputs: ["selected", "igxButton", "igxLabel"], outputs: ["buttonSelected"] }, { kind: "directive", type: IgxRippleDirective, selector: "[igxRipple]", inputs: ["igxRippleTarget", "igxRipple", "igxRippleDuration", "igxRippleCentered", "igxRippleDisabled"] }, { kind: "component", type: IgxCalendarComponent, selector: "igx-calendar", inputs: ["id", "hasHeader", "vertical", "orientation", "headerOrientation", "monthsViewNumber", "showWeekNumbers"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: IgxDividerDirective, selector: "igx-divider", inputs: ["id", "role", "type", "middle", "vertical", "inset"] }, { kind: "component", type: IgxPredefinedRangesAreaComponent, selector: "igx-predefined-ranges-area", inputs: ["usePredefinedRanges", "customRanges", "resourceStrings"], outputs: ["rangeSelect"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxCalendarContainerComponent, decorators: [{
type: Component,
args: [{ selector: 'igx-calendar-container', imports: [
IgxButtonDirective,
IgxRippleDirective,
IgxCalendarComponent,
NgTemplateOutlet,
IgxDividerDirective,
IgxPredefinedRangesAreaComponent
], template: "<ng-template #defaultPickerActions>\n @if (closeButtonLabel || cancelButtonLabel || todayButtonLabel) {\n <div class=\"igx-date-picker__buttons\">\n @if (cancelButtonLabel) {\n <button\n #cancelButton type=\"button\"\n igxButton=\"flat\" igxRipple\n (click)=\"calendarCancel.emit({ owner: this})\"\n >\n {{ cancelButtonLabel }}\n </button>\n }\n @if (closeButtonLabel) {\n <button\n #closeButton\n type=\"button\"\n igxButton=\"flat\"\n igxRipple\n (click)=\"calendarClose.emit({ owner: this})\"\n >\n {{ closeButtonLabel }}\n </button>\n }\n @if (todayButtonLabel) {\n <button\n #todayButton\n type=\"button\"\n igxButton=\"flat\"\n igxRipple\n (click)=\"todaySelection.emit({ owner: this })\"\n >\n {{ todayButtonLabel }}\n </button>\n }\n </div>\n }\n</ng-template>\n\n<igx-calendar></igx-calendar>\n @if( usePredefinedRanges || (customRanges?.length || 0) > 0 ){\n <igx-predefined-ranges-area\n [usePredefinedRanges]=\"usePredefinedRanges\"\n [customRanges]=\"customRanges\"\n [resourceStrings]=\"resourceStrings\"\n (rangeSelect)=\"rangeSelected.emit($event)\">\n </igx-predefined-ranges-area>\n }\n@if (pickerActions?.template || (closeButtonLabel || todayButtonLabel)) {\n <igx-divider></igx-divider>\n}\n@if (pickerActions?.template || (closeButtonLabel || cancelButtonLabel || todayButtonLabel)) {\n <div class=\"igx-date-picker__actions\">\n <ng-container\n *ngTemplateOutlet=\"\n pickerActions?.template || defaultPickerActions;\n context: { $implicit: calendar }\n \"\n >\n </ng-container>\n </div>\n}\n", styles: [":host{display:block}\n"] }]
}], propDecorators: { calendar: [{
type: ViewChild,
args: [IgxCalendarComponent, { static: true }]
}], calendarClose: [{
type: Output
}], calendarCancel: [{
type: Output
}], todaySelection: [{
type: Output
}], rangeSelected: [{
type: Output
}], styleClass: [{
type: HostBinding,
args: ['class.igx-date-picker']
}], dropdownCSS: [{
type: HostBinding,
args: ['class.igx-date-picker--dropdown']
}], onEscape: [{
type: HostListener,
args: ['keydown.alt.arrowup', ['$event']]
}] } });
let NEXT_ID = 0;
/**
* Date Picker displays a popup calendar that lets users select a single date.
*
* @igxModule IgxDatePickerModule
* @igxTheme igx-calendar-theme, igx-icon-theme
* @igxGroup Scheduling
* @igxKeywords datepicker, calendar, schedule, date
* @example
* ```html
* <igx-date-picker [(ngModel)]="selectedDate"></igx-date-picker>
* ```
*/
class IgxDatePickerComponent extends PickerBaseDirective {
/**
* Gets/Sets the date which is shown in the calendar picker and is highlighted.
* By default it is the current date, or the value of the picker, if set.
*/
get activeDate() {
const today = new Date(new Date().setHours(0, 0, 0, 0));
const dateValue = DateTimeUtil.isValidDate(this._dateValue) ? new Date(this._dateValue.setHours(0, 0, 0, 0)) : null;
return this._activeDate ?? dateValue ?? this._calendar?.activeDate ?? today;
}
set activeDate(value) {
this._activeDate = value;
}
/**
* Gets/Sets the disabled dates descriptors.
*
* @example
* ```typescript
* let disabledDates = this.datepicker.disabledDates;
* this.datePicker.disabledDates = [ {type: DateRangeType.Weekends}, ...];
* ```
*/
get disabledDates() {
return this._disabledDates;
}
set disabledDates(value) {
this._disabledDates = value;
this._onValidatorChange();
}
/**
* Gets/Sets the special dates descriptors.
*
* @example
* ```typescript
* let specialDates = this.datepicker.specialDates;
* this.datePicker.specialDates = [ {type: DateRangeType.Weekends}, ... ];
* ```
*/
get specialDates() {
return this._specialDates;
}
set specialDates(value) {
this._specialDates = value;
}
//#endregion
/**
* Gets/Sets the selected date.
*
* @example
* ```html
* <igx-date-picker [value]="date"></igx-date-picker>
* ```
*/
get value() {
return this._value;
}
set value(date) {
this._value = date;
this.setDateValue(date);
if (this.dateTimeEditor.value !== date) {
this.dateTimeEditor.value = this._dateValue;
}
this.valueChange.emit(this.dateValue);
this._onChangeCallback(this.dateValue);
}
/**
* The minimum value the picker will accept.
*
* @example
* <igx-date-picker [minValue]="minDate"></igx-date-picker>
*/
set minValue(value) {
this._minValue = value;
this._onValidatorChange();
}
get minValue() {
return this._minValue;
}
/**
* The maximum value the picker will accept.
*
* @example
* <igx-date-picker [maxValue]="maxDate"></igx-date-picker>
*/
set maxValue(value) {
this._maxValue = value;
this._onValidatorChange();
}
get maxValue() {
return this._maxValue;
}
get dialogOverlaySettings() {
return Object.assign({}, this._dialogOverlaySettings, this.overlaySettings);
}
get dropDownOverlaySettings() {
return Object.assign({}, this._dropDownOverlaySettings, this.overlaySettings);
}
get inputGroupElement() {
return this.inputGroup?.element.nativeElement;
}
get dateValue() {
return this._dateValue;
}
get pickerFormatViews() {
return Object.assign({}, this._defFormatViews, this.formatViews);
}
get pickerCalendarFormat() {
return Object.assign({}, this._calendarFormat, this.calendarFormat);
}
constructor() {
super();
this._overlayService = inject(IgxOverlayService);
this._injector = inject(Injector);
this._renderer = inject(Renderer2);
this.platform = inject(PlatformUtil);
this.cdr = inject(ChangeDetectorRef);
/**
* Gets/Sets the number of month views displayed.
*
* @remarks
* Default value is `1`.
*
* @example
* ```html
* <igx-date-picker [displayMonthsCount]="2"></igx-date-picker>
* ```
* @example
* ```typescript
* let monthViewsDisplayed = this.datePicker.displayMonthsCount;
* ```
*/
this.displayMonthsCount = 1;
/**
* Gets/Sets the orientation of the multiple months displayed in the picker's calendar's days view.
*
* @example
* <igx-date-picker orientation="vertical"></igx-date-picker>
*/
this.orientation = PickerCalendarOrientation.Horizontal;
/**
* Specify if the currently spun date segment should loop over.
*
* @example
* ```html
* <igx-date-picker [spinLoop]="false"></igx-date-picker>
* ```
*/
this.spinLoop = true;
/**
* Gets/Sets the value of `id` attribute.
*
* @remarks If not provided it will be automatically generated.
* @example
* ```html
* <igx-date-picker [id]="'igx-date-picker-3'" cancelButtonLabel="cancel" todayButtonLabel="today"></igx-date-picker>
* ```
*/
this.id = `igx-date-picker-${NEXT_ID++}`;
/** @hidden @internal */
this.readOnly = false;
/**
* Emitted when the picker's value changes.
*
* @remarks
* Used for `two-way` bindings.
*
* @example
* ```html
* <igx-date-picker [(value)]="date"></igx-date-picker>
* ```
*/
this.valueChange = new EventEmitter();
/**
* Emitted when the user types/spins invalid date in the date-picker editor.
*
* @example
* ```html
* <igx-date-picker (validationFailed)="onValidationFailed($event)"></igx-date-picker>
* ```
*/
this.validationFailed = new EventEmitter();
/** @hidden @internal */
this.displayValue = { transform: (date) => this.formatter(date) };
this._resourceStrings = getCurrentResourceStrings(DatePickerResourceStringsEN);
this._ngControl = null;
this._specialDates = null;
this._disabledDates = null;
this._activeDate = null;
this._overlaySubFilter = [
filter(x => x.id === this._overlayId),
takeUntil(this._destroy$)
];
this._dropDownOverlaySettings = {
target: this.inputGroupElement,
closeOnOutsideClick: true,
modal: false,
closeOnEscape: true,
scrollStrategy: new AbsoluteScrollStrategy(),
positionStrategy: new AutoPositionStrategy({
openAnimation: fadeIn,
closeAnimation: fadeOut
})
};
this._dialogOverlaySettings = {
closeOnOutsideClick: true,
modal: true,
closeOnEscape: true
};
this._calendarFormat = {
day: 'numeric',
month: 'short',
weekday: 'short',
year: 'numeric'
};
this._defFormatViews = {
day: false,
month: true,
year: false
};
this._onChangeCallback = noop;
this._onTouchedCallback = noop;
this._onValidatorChange = noop;
this.onStatusChanged = () => {
this.disabled = this._ngControl.disabled;
this.updateValidity();
this.inputGroup.isRequired = this.required;
};
this.locale = this.locale || this._localeId;
}
/** @hidden @internal */
get required() {
if (this._ngControl && this._ngControl.control && this._ngControl.control.validator) {
// Run the validation with empty object to check if required is enabled.
const error = this._ngControl.control.validator({});
return error && error.required;
}
return false;
}
/** @hidden @internal */
get pickerResourceStrings() {
return Object.assign({}, this._resourceStrings, this.resourceStrings);
}
get toggleContainer() {
return this._calendarContainer;
}
/** @hidden @internal */
onKeyDown(event) {
switch (event.key) {
case this.platform.KEYMAP.ARROW_UP:
if (event.altKey) {
this.close();
}
break;
case this.platform.KEYMAP.ARROW_DOWN:
if (event.altKey) {
this.open();
}
break;
case this.platform.KEYMAP.SPACE:
event.preventDefault();
this.open();
break;
}
}
/**
* Opens the picker's dropdown or dialog.
*
* @example
* ```html
* <igx-date-picker #picker></igx-date-picker>
*
* <button type="button" igxButton (click)="picker.open()">Open Dialog</button>
* ```
*/
open(settings) {
if (!this.collapsed || this.disabled || this.readOnly) {
return;
}
const overlaySettings = Object.assign({}, this.isDropdown
? this.dropDownOverlaySettings
: this.dialogOverlaySettings, settings);
if (this.isDropdown && this.inputGroupElement) {
overlaySettings.target = this.inputGroupElement;
}
if (this.outlet) {
overlaySettings.outlet = this.outlet;
}
this._overlayId = this._overlayService
.attach(IgxCalendarContainerComponent, this.viewContainerRef, overlaySettings);
this._overlayService.show(this._overlayId);
}
/**
* Toggles the picker's dropdown or dialog
*
* @example
* ```html
* <igx-date-picker #picker></igx-date-picker>
*
* <button type="button" igxButton (click)="picker.toggle()">Toggle Dialog</button>
* ```
*/
toggle(settings) {
if (this.collapsed) {
this.open(settings);
}
else {
this.close();
}
}
/**
* Closes the picker's dropdown or dialog.
*
* @example
* ```html
* <igx-date-picker #picker></igx-date-picker>
*
* <button type="button" igxButton (click)="picker.close()">Close Dialog</button>
* ```
*/
close() {
if (!this.collapsed) {
this._overlayService.hide(this._overlayId);
}
}
/**
* Selects a date.
*
* @remarks Updates the value in the input field.
*
* @example
* ```typescript
* this.datePicker.select(date);
* ```
* @param date passed date that has to be set to the calendar.
*/
select(value) {
this.value = value;
}
/**
* Selects today's date and closes the picker.
*
* @example
* ```html
* <igx-date-picker #picker></igx-date-picker>
*
* <button type="button" igxButton (click)="picker.selectToday()">Select Today</button>
* ```
* */
selectToday() {
const today = new Date();
today.setHours(0);
today.setMinutes(0);
today.setSeconds(0);
today.setMilliseconds(0);
this.select(today);
this.close();
}
/**
* Clears the input field and the picker's value.
*
* @example
* ```typescript
* this.datePicker.clear();
* ```
*/
clear() {
if (!this.disabled || !this.readOnly) {
this._calendar?.deselectDate();
this.dateTimeEditor.clear();
}
}
/**
* Increment a specified `DatePart`.
*
* @param datePart The optional DatePart to increment. Defaults to Date.
* @param delta The optional delta to increment by. Overrides `spinDelta`.
* @example
* ```typescript
* this.datePicker.increment(DatePart.Date);
* ```
*/
increment(datePart, delta) {
this.dateTimeEditor.increment(datePart, delta);
}
/**
* Decrement a specified `DatePart`
*
* @param datePart The optional DatePart to decrement. Defaults to Date.
* @param delta The optional delta to decrement by. Overrides `spinDelta`.
* @example
* ```typescript
* this.datePicker.decrement(DatePart.Date);
* ```
*/
decrement(datePart, delta) {
this.dateTimeEditor.decrement(datePart, delta);
}
//#region Control Value Accessor
/** @hidden @internal */
writeValue(value) {
this._value = value;
this.setDateValue(value);
if (this.dateTimeEditor.value !== value) {
this.dateTimeEditor.value = this._dateValue;
}
}
/** @hidden @internal */
registerOnChange(fn) {
this._onChangeCallback = fn;
}
/** @hidden @internal */
registerOnTouched(fn) {
this._onTouchedCallback = fn;
}
/** @hidden @internal */
setDisabledState(isDisabled) {
this.disabled = isDisabled;
}
//#endregion
//#region Validator
/** @hidden @internal */
registerOnValidatorChange(fn) {
this._onValidatorChange = fn;
}
/** @hidden @internal */
validate(control) {
if (!control.value) {
return null;
}
// InvalidDate handling
if (isDate(control.value) && !DateTimeUtil.isValidDate(control.value)) {
return { value: true };
}
const errors = {};
const value = DateTimeUtil.isValidDate(control.value) ? control.value : DateTimeUtil.parseIsoDate(control.value);
if (value && this.disabledDates && isDateInRanges(value, this.disabledDates)) {
Object.assign(errors, { dateIsDisabled: true });
}
Object.assign(errors, DateTimeUtil.validateMinMax(value, this.minValue, this.maxValue, false));
return Object.keys(errors).length > 0 ? errors : null;
}
//#endregion
/** @hidden @internal */
ngOnInit() {
this._ngControl = this._injector.get(NgControl, null);
this.locale = this.locale || this._localeId;
}
/** @hidden @internal */
ngAfterViewInit() {
super.ngAfterViewInit();
this.subscribeToClick();
this.subscribeToOverlayEvents();
this.subscribeToDateEditorEvents();
this._dropDownOverlaySettings.excludeFromOutsideClick = [this.inputGroup.element.nativeElement];
fromEvent(this.inputDirective.nativeElement, 'blur')
.pipe(takeUntil(this._destroy$))
.subscribe(() => {
if (this.collapsed) {
this._onTouchedCallback();
this.updateValidity();
}
});
if (this._ngControl) {
this._statusChanges$ =
this._ngControl.statusChanges.subscribe(this.onStatusChanged.bind(this));
if (this._ngControl.control.validator) {
this.inputGroup.isRequired = this.required;
this.cdr.detectChanges();
}
}
}
/** @hidden @internal */
ngAfterViewChecked() {
if (this.labelDirective) {
this._renderer.setAttribute(this.inputDirective.nativeElement, 'aria-labelledby', this.labelDirective.id);
}
}
/** @hidden @internal */
ngOnDestroy() {
super.ngOnDestroy();
if (this._statusChanges$) {
this._statusChanges$.unsubscribe();
}
if (this._overlayId) {
this._overlayService.detach(this._overlayId);
delete this._overlayId;
}
}
/** @hidden @internal */
getEditElement() {
return this.inputDirective.nativeElement;
}
subscribeToClick() {
fromEvent(this.getEditElement(), 'click')
.pipe(takeUntil(this._destroy$))
.subscribe(() => {
if (!this.isDropdown) {
this.toggle();
}
});
}
setDateValue(value) {
if (isDate(value) && isNaN(value.getTime())) {
this._dateValue = value;
return;
}
this._dateValue = DateTimeUtil.isValidDate(value) ? value : DateTimeUtil.parseIsoDate(value);
if (this._calendar) {
this._calendar.selectDate(this._dateValue);
this._calendar.activeDate = this.activeDate;
this._calendar.viewDate = this.activeDate;
this.cdr.detectChanges();
}
}
updateValidity() {
// B.P. 18 May 2021: IgxDatePicker does not reset its state upon resetForm #9526
if (this._ngControl && !this.disabled && this.isTouchedOrDirty) {
if (this.hasValidators && this.inputGroup.isFocused) {
this.inputDirective.valid = this._ngControl.valid ? IgxInputState.VALID : IgxInputState.INVALID;
}
else {
this.inputDirective.valid = this._ngControl.valid ? IgxInputState.INITIAL : IgxInputState.INVALID;
}
}
else {
this.inputDirective.valid = IgxInputState.INITIAL;
}
}
get isTouchedOrDirty() {
return (this._ngControl.control.touched || this._ngControl.control.dirty);
}
get hasValidators() {
return (!!this._ngControl.control.validator || !!this._ngControl.control.asyncValidator);
}
handleSelection(date) {
if (this.dateValue && DateTimeUtil.isValidDate(this.dateValue)) {
date.setHours(this.dateValue.getHours());
date.setMinutes(this.dateValue.getMinutes());
date.setSeconds(this.dateValue.getSeconds());
date.setMilliseconds(this.dateValue.getMilliseconds());
}
this.value = date;
if (this._calendar) {
this._calendar.activeDate = this.activeDate;
this._calendar.viewDate = this.activeDate;
}
this.close();
}
subscribeToDateEditorEvents() {
this.dateTimeEditor.valueChange.pipe(takeUntil(this._destroy$)).subscribe(val => {
this.value = val;
});
this.dateTimeEditor.validationFailed.pipe(takeUntil(this._destroy$)).subscribe((event) => {
this.validationFailed.emit({
owner: this,
prevValue: event.oldValue,
currentValue: this.value
});
});
}
subscribeToOverlayEvents() {
this._overlayService.opening.pipe(...this._overlaySubFilter).subscribe((e) => {
const args = { owner: this, event: e.event, cancel: e.cancel };
this.opening.emit(args);
e.cancel = args.cancel;
if (args.cancel) {
this._overlayService.detach(this._overlayId);
return;
}
this._initializeCalendarContainer(e.componentRef.instance);
this._calendarContainer = e.componentRef.location.nativeElement;
this._collapsed = false;
});
this._overlayService.opened.pipe(...this._overlaySubFilter).subscribe(() => {
this.opened.emit({ owner: this });
this._calendar.wrapper?.nativeElement?.focus();
});
this._overlayService.closing.pipe(...this._overlaySubFilter).subscribe((e) => {
const args = { owner: this, event: e.event, cancel: e.cancel };
this.closing.emit(args);
e.cancel = args.cancel;
if (args.cancel) {
return;
}
// do not focus the input if clicking outside in dropdown mode
const outsideEvent = args.event && args.event.key !== this.platform.KEYMAP.ESCAPE;
if (this.getEditElement() && !(outsideEvent && this.isDropdown)) {
this.inputDirective.focus();
}
else {
this._onTouchedCallback();
this.updateValidity();
}
});
this._overlayService.closed.pipe(...this._overlaySubFilter).subscribe(() => {
this.closed.emit({ owner: this });
this._overlayService.detach(this._overlayId);
this._collapsed = true;
this._overlayId = null;
this._calendar = null;
this._calendarContainer = undefined;
});
}
getMinMaxDates() {
const minValue = DateTimeUtil.isValidDate(this.minValue) ? this.minValue : DateTimeUtil.parseIsoDate(this.minValue);
const maxValue = DateTimeUtil.isValidDate(this.maxValue) ? this.maxValue : DateTimeUtil.parseIsoDate(this.maxValue);
return { minValue, maxValue };
}
setDisabledDates() {
this._calendar.disabledDates = this.disabledDates ? [...this.disabledDates] : [];
const { minValue, maxValue } = this.getMinMaxDates();
if (minValue) {
this._calendar.disabledDates.push({ type: DateRangeType.Before, dateRange: [minValue] });
}
if (maxValue) {
this._calendar.disabledDates.push({ type: DateRangeType.After, dateRange: [maxValue] });
}
}
_initializeCalendarContainer(componentInstance) {
this._calendar = componentInstance.calendar;
this._calendar.hasHeader = !this.isDropdown && !this.hideHeader;
this._calendar.formatOptions = this.pickerCalendarFormat;
this._calendar.formatViews = this.pickerFormatViews;
this._calendar.locale = this.locale;
this._calendar.weekStart = this.weekStart;
this._calendar.specialDates = this.specialDates;
this._calendar.headerTitleTemplate = this.headerTitleTemplate;
this._calendar.headerTemplate = this.headerTemplate;
this._calendar.subheaderTemplate = this.subheaderTemplate;
this._calendar.headerOrientation = this.headerOrientation;
this._calendar.hideOutsideDays = this.hideOutsideDays;
this._calendar.monthsViewNumber = this.displayMonthsCount;
this._calendar.showWeekNumbers = this.showWeekNumbers;
this._calendar.orientation = this.orientation;
this._calendar.selected.pipe(takeUntil(this._destroy$)).subscribe((ev) => this.handleSelection(ev));
this.setDisabledDates();
if (DateTimeUtil.isValidDate(this.dateValue)) {
// calendar will throw if the picker's value is InvalidDate #9208
this._calendar.value = this.dateValue;
}
this._calendar.activeDate = this.activeDate;
this._calendar.viewDate = this.activeDate;
componentInstance.mode = this.mode;
componentInstance.closeButtonLabel = this.cancelButtonLabel;
componentInstance.todayButtonLabel = this.todayButtonLabel;
componentInstance.pickerActions = this.pickerActions;
componentInstance.calendarClose.pipe(takeUntil(this._destroy$)).subscribe(() => this.close());
componentInstance.todaySelection.pipe(takeUntil(this._destroy$)).subscribe(() => this.selectToday());
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.2", ngImport: i0, type: IgxDatePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.2", type: IgxDatePickerComponent, isStandalone: true, selector: "igx-date-picker", inputs: { hideOutsideDays: ["hideOutsideDays", "hideOutsideDays", booleanAttribute], displayMonthsCount: "displayMonthsCount", orientation: "orientation", showWeekNumbers: ["showWeekNumbers", "showWeekNumbers", booleanAttribute], activeDate: "activeDate", formatter: "formatter", todayButtonLabel: "todayButtonLabel", cancelButtonLabel: "cancelButtonLabel", spinLoop: ["spinLoop", "spinLoop", booleanAttribute], spinDelta: "spinDelta", outlet: "outlet", id: "id", formatViews: "formatViews", disabledDates: "disabledDates", specialDates: "specialDates", calendarFormat: "calendarFormat", value: "value", minValue: "minValue", maxValue: "maxValue", resourceStrings: "resourceStrings", readOnly: ["readOnly", "readOnly", booleanAttribute] }, outputs: { valueChange: "valueChange", validationFailed: "validationFailed" }, host: { listeners: { "keydown": "onKeyDown($event)" }, properties: { "attr.id": "this.id" } }, providers: [
{ provide: NG_VALUE_ACCESSOR, useExisting: IgxDatePickerComponent, multi: true },
{ provide: NG_VALIDATORS, useExisting: IgxDatePickerComponent, multi: true }
], queries: [{ propertyName: "label", first: true, predicate: IgxLabelDirective, descendants: true }, { propertyName: "headerTitleTemplate", first: true, predicate: IgxCalendarHeaderTitleTemplateDirective, descendants: true }, { propertyName: "headerTemplate", first: true, predicate: IgxCalendarHeaderTemplateDirective, descendants: true }, { propertyName: "subheaderTemplate", first: true, predicate: IgxCalendarSubheaderTemplateDirective, descendants: true }, { propertyName: "pickerActions", first: true, predicate: IgxPickerActionsDirective, descendants: true }], viewQueries: [{ propertyName: "dateTimeEditor", first: true, predicate: IgxDateTimeEditorDirective, descendants: true, static: true }, { propertyName: "viewContainerRef", first: true, predicate: IgxInputGroupComponent, descendants: true, read: ViewContainerRef }, { propertyName: "labelDirective", first: true, predicate: IgxLabelDirective, descendants: true }, { propertyName: "inputDirective", first: true, predicate: IgxInputDirective, descendants: true }], usesInheritance: true, ngImport: i0, template: "