UNPKG

novo-elements

Version:

994 lines (984 loc) 79.1 kB
import { trigger, state, style, transition, animate } from '@angular/animations'; import * as i0 from '@angular/core'; import { forwardRef, EventEmitter, Input, Output, HostBinding, Component, ViewChild, NgModule } from '@angular/core'; import * as i3 from '@angular/forms'; import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms'; import * as i2 from '@angular/platform-browser'; import { isDate, isValid, subDays } from 'date-fns'; import * as i1 from 'novo-elements/services'; import { DateUtil, Helpers, BooleanInput } from 'novo-elements/utils'; import * as i2$1 from '@angular/common'; import { CommonModule } from '@angular/common'; import * as i4 from 'novo-elements/elements/button'; import { NovoButtonModule } from 'novo-elements/elements/button'; import * as i5 from 'novo-elements/elements/calendar'; import { NovoCalendarModule } from 'novo-elements/elements/calendar'; import * as i6 from 'angular-imask'; import { IMaskModule } from 'angular-imask'; import * as i8 from 'novo-elements/pipes'; import { NovoPipesModule } from 'novo-elements/pipes'; import * as i6$2 from 'novo-elements/elements/chips'; import { NovoChipsModule } from 'novo-elements/elements/chips'; import * as i4$1 from 'novo-elements/elements/common'; import { NovoOverlayTemplateComponent, NovoOverlayModule } from 'novo-elements/elements/common'; import * as i6$1 from 'novo-elements/elements/icon'; import { NovoIconModule } from 'novo-elements/elements/icon'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; var __decorate$1 = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata$1 = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; // Value accessor for the component (supports ngModel) const DATE_PICKER_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NovoDatePickerElement), multi: true, }; class NovoDatePickerElement { /** * Number of months to display at once. * @default 1 **/ get numberOfMonths() { return this._numberOfMonths.length; } set numberOfMonths(value) { this._numberOfMonths = Array.from(Array(Number(value)).keys()); } /** * How the date selection should work. * @default single **/ get mode() { return this._mode; } set mode(value) { if (this._mode !== value) { this._mode = value; } } /** * **deprecated** please use `mode="range"`. **/ get range() { return ['range', 'week'].includes(this.mode) || this._range; } set range(value) { console.warn('\'range\' property is deprecated, please use \'mode="range"\'.'); if (this._range !== value) { this._range = value; this.mode = 'range'; } } /** * **deprecated** please use `mode="week"`. **/ get weekRangeSelect() { return this._mode === 'week' || this._weekRangeSelect; } set weekRangeSelect(value) { console.warn('\'weekRangeSelect\' property is deprecated, please use \'mode="week"\'.'); if (this._weekRangeSelect !== value) { this._weekRangeSelect = value; this.mode = 'week'; } } get selection() { return this._selection; } set selection(value) { this._selection = value ? value.filter(isDate).map((d) => DateUtil.startOfDay(d)) : []; } constructor(labels, element, cdr, _sanitizer) { this.labels = labels; this.element = element; this.cdr = cdr; this._sanitizer = _sanitizer; /** * Day of the week the calendar should display first, Sunday=0...Saturday=6 **/ this.weekStart = 0; /** * Certain dates that are already selected. **/ this.preselected = []; /** * Whether the days for the previous and next month should be hidden. **/ this.hideOverflowDays = false; /** * Whether the footer should be hidden - contains `today`/`cancel`/`save` buttons **/ this.hideFooter = false; /** * Whether to hide the `today` button. **/ this.hideToday = false; // Select callback for output this.onSelect = new EventEmitter(false); this._mode = 'single'; this._numberOfMonths = [0]; this._selection = []; this.preview = []; this.rangeSelectMode = 'startDate'; this._onChange = () => { }; this._onTouched = () => { }; } ngOnInit() { // Determine the year array const now = new Date(); // Set labels if (this.model) { this.modelToSelection(this.model); } if (this.dateForInitialView) { this.updateView(this.dateForInitialView); } else if (this.selection && this.selection.length) { this.updateView(this.selection[0]); } } updateView(date) { const value = date ? new Date(date) : new Date(); this.activeDate = new Date(value); } updateSelection(selected, fireEvents = true) { this.selection = selected; this.startDateLabel = this.labels.formatDateWithFormat(this.selection[0], { month: 'short', day: '2-digit', year: 'numeric', }); this.endDateLabel = this.labels.formatDateWithFormat(this.selection[1], { month: 'short', day: '2-digit', year: 'numeric', }); if (fireEvents) { switch (this.mode) { case 'multiple': this.fireSelect(); // Also, update the ngModel this._onChange(this.selection); this.model = this.selection; break; case 'range': case 'week': if (this.selection.filter(Boolean).length === 2) { this.fireRangeSelect(); // Also, update the ngModel const model = { startDate: this.selection[0], endDate: this.selection[1], }; this._onChange(model); this.model = model; } break; case 'single': default: this.fireSelect(); // Also, update the ngModel this._onChange(this.selection[0]); this.model = this.selection[0]; break; } } this.cdr.markForCheck(); } eventData(date) { return { year: date.getFullYear(), month: this.labels.formatDateWithFormat(date, { month: 'long' }), day: this.labels.formatDateWithFormat(date, { weekday: 'long' }), date, }; } fireSelect() { if (this.mode === 'multiple') { this.onSelect.next(this.selection); } else { this.onSelect.next(this.eventData(this.selection[0])); } } fireRangeSelect() { // Make sure the start date is before the end date if (this.selection.filter(Boolean).length === 2) { const [start, end] = this.selection; this.onSelect.next({ startDate: this.eventData(start), endDate: this.eventData(end), }); } } setToday() { const tmp = new Date(); this.updateView(tmp); this.updateSelection(Array.of(tmp)); } toggleRangeSelect(range) { this.rangeSelectMode = range; if (range === 'startDate' && this.selection.length) { this.updateView(this.selection[0]); } if (range === 'endDate' && this.selection.length === 2) { this.updateView(this.selection[1]); } } modelToSelection(model) { switch (this.mode) { case 'multiple': this.selection = model; break; case 'range': case 'week': this.setRangeSelection(); break; case 'single': default: this.selection = [model]; break; } } // ValueAccessor Functions writeValue(model) { this.model = model; if (this.mode === 'multiple') { this.selection = this.model; } if (this.mode === 'range') { this.setRangeSelection(); } if (Helpers.isDate(model)) { this.updateView(model); this.modelToSelection(model); } else if (Helpers.isString(model)) { const date = DateUtil.parse(model); if (isValid(date)) { this.updateView(date); this.modelToSelection(date); } } } setRangeSelection() { if (this.model?.hasOwnProperty('startDate')) { // coming from standalone date picker const range = this.model; this.selection = [range.startDate, range.endDate].filter(Boolean); } else if (this.model?.hasOwnProperty('min')) { // coming from data-table filter where model end date is the beginning of the next day const range = this.model; this.selection = [range.min, subDays(range.max, 1)].filter(Boolean); } } registerOnChange(fn) { this._onChange = fn; } registerOnTouched(fn) { this._onTouched = fn; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoDatePickerElement, deps: [{ token: i1.NovoLabelService }, { token: i0.ElementRef }, { token: i0.ChangeDetectorRef }, { token: i2.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: NovoDatePickerElement, isStandalone: false, selector: "novo-date-picker", inputs: { minYear: "minYear", maxYear: "maxYear", start: "start", end: "end", inline: "inline", weekStart: "weekStart", preselected: "preselected", hideOverflowDays: "hideOverflowDays", hideFooter: "hideFooter", hideToday: "hideToday", disabledDateMessage: "disabledDateMessage", dateForInitialView: "dateForInitialView", numberOfMonths: "numberOfMonths", mode: "mode", range: "range", weekRangeSelect: "weekRangeSelect" }, outputs: { onSelect: "onSelect" }, host: { properties: { "class.hide-overflow-days": "this.hideOverflowDays" } }, providers: [DATE_PICKER_VALUE_ACCESSOR], ngImport: i0, template: ` <div class="date-picker-container"> <div class="date-range-tabs" *ngIf="range" [class.week-select-mode]="weekRangeSelect"> <span class="range-tab" (click)="toggleRangeSelect('startDate')" [@startDateTextState]="rangeSelectMode" data-automation-id="calendar-start-date" >{{ startDateLabel }}</span > <span class="range-tab" (click)="toggleRangeSelect('endDate')" [@endDateTextState]="rangeSelectMode" data-automation-id="calendar-end-date" >{{ endDateLabel }}</span > <i class="indicator" [@indicatorState]="rangeSelectMode"></i> </div> <novo-calendar [activeDate]="activeDate" [(selected)]="selection" (selectedChange)="updateSelection($event)" [mode]="mode" [numberOfMonths]="numberOfMonths" [weekStartsOn]="weekStart" [disabledDateMessage]="disabledDateMessage" [minDate]="start" [maxDate]="end" ></novo-calendar> <div class="calendar-footer" [hidden]="hideFooter"> <novo-button [hidden]="hideToday" (click)="setToday()" class="today" size="small" data-automation-id="calendar-today">{{ labels.today }}</novo-button> <ng-content select=".footer-content"></ng-content> </div> </div> `, isInline: true, styles: [":host{display:block}:host .date-picker-container{border-radius:4px;width:min-content;text-align:center;background:var(--background-main);color:#3a3a3a;-webkit-user-select:none;user-select:none;box-shadow:0 1px 3px #00000026,0 2px 7px #0000001a;z-index:9001;position:relative}:host .date-picker-container .month-view+.month-view{border-collapse:unset;border-left:1px solid #dbdbdb;margin-left:.5rem;padding-left:.5rem}:host .date-picker-container .calendar-top{display:flex;flex-flow:column;background:#4a89dc;color:#fff;font-size:14px;border-top-right-radius:4px;border-top-left-radius:4px}:host .date-picker-container .calendar-top h1{font-weight:600;font-size:4.2em;color:#fff;margin:0;padding:0}:host .date-picker-container .calendar-top h2{font-weight:300;opacity:1;margin:10px auto;padding:0}:host .date-picker-container .calendar-top h3{font-weight:400;opacity:.4;margin:15px auto;padding:0}:host .date-picker-container .calendar-top h4{background:#00000026;font-size:1em;font-weight:300;padding:10px}:host .date-picker-container .date-range-tabs{border-bottom:1px solid #f7f7f7;display:flex;align-items:center;justify-content:space-between;position:relative;height:45px}:host .date-picker-container .date-range-tabs.week-select-mode>span{cursor:default;color:#3d464d;pointer-events:none;opacity:1!important}:host .date-picker-container .date-range-tabs.week-select-mode .indicator{display:none}:host .date-picker-container .date-range-tabs>span{color:#4a89dc;text-align:center;flex:1;cursor:pointer;font-weight:500;transition:opacity .2s ease-in-out;opacity:.6}:host .date-picker-container .date-range-tabs>span:hover{opacity:1!important}:host .date-picker-container .date-range-tabs .indicator{position:absolute;width:50%;height:2px;bottom:0;left:0;background:#4a89dc;transition:transform .2s ease-in-out}:host .date-picker-container .calendar-header{width:100%;display:flex;flex-flow:row nowrap;border-collapse:collapse;padding:14px 0;-webkit-user-select:none;justify-content:space-between;cursor:default;border-bottom:1px solid #f7f7f7}:host .date-picker-container .calendar-header .previous{width:30px;height:15px;display:inline-block;cursor:pointer}:host .date-picker-container .calendar-header .previous:after{content:\"\";border-bottom:4px solid transparent;border-top:4px solid transparent;border-right:4px solid #aaa;display:inline-block;height:0;vertical-align:middle;width:0}:host .date-picker-container .calendar-header .previous:hover:after{border-right:4px solid #4a89dc;cursor:pointer}:host .date-picker-container .calendar-header .heading{flex:1;display:inline-block;vertical-align:middle;color:#4a89dc;font-weight:600}:host .date-picker-container .calendar-header .heading .month{border-radius:2px;padding:3px 8px}:host .date-picker-container .calendar-header .heading .month:hover{background:#4a89dc;color:#fff;cursor:pointer}:host .date-picker-container .calendar-header .heading .year{border-radius:2px;padding:3px 8px}:host .date-picker-container .calendar-header .heading .year:hover{background:#4a89dc;color:#fff;cursor:pointer}:host .date-picker-container .calendar-header .next{width:30px;height:15px;display:inline-block;cursor:pointer}:host .date-picker-container .calendar-header .next:before{content:\"\";border-bottom:4px solid transparent;border-top:4px solid transparent;border-left:4px solid #aaa;display:inline-block;height:0;vertical-align:middle;width:0}:host .date-picker-container .calendar-header .next:hover:before{opacity:1;border-left:4px solid #4a89dc;cursor:pointer}:host .date-picker-container section.calendar-content{display:flex;flex-flow:column}:host .date-picker-container section.calendar-content span{display:block}:host .date-picker-container section.calendar-content.days{flex-flow:row nowrap;height:min-content}:host .date-picker-container .calendar-content{width:100%;height:230px;overflow-y:scroll;position:static;top:0;left:0;transform-origin:209px 26px;transform:scale(1)}:host .date-picker-container .calendar-footer{display:flex;width:100%;padding:1rem .8rem;text-align:left}:host .date-picker-container .calendar-footer .novo-button.today{margin-inline-end:auto}:host ::ng-deep .hide-overflow-days .notinmonth{visibility:hidden}:host .calendar.popup{display:none;position:absolute;z-index:9001}:host .calendar.popup.open{display:block}\n"], dependencies: [{ kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i4.NovoButtonElement, selector: "novo-button,button[theme]", inputs: ["color", "side", "size", "theme", "loading", "icon", "secondIcon", "disabled"] }, { kind: "component", type: i5.NovoCalendarElement, selector: "novo-calendar", inputs: ["minYear", "maxYear", "minDate", "maxDate", "activeView", "layout", "selected", "preview", "overlays", "disabledDateMessage", "activeDate", "weekStartsOn", "numberOfMonths", "mode"], outputs: ["selectedChange", "previewChange", "activeDateChange"] }], animations: [ trigger('startDateTextState', [ state('startDate', style({ opacity: '1.0', })), state('endDate', style({ opacity: '0.6', })), transition('startDate <=> endDate', animate('200ms ease-in')), ]), trigger('endDateTextState', [ state('startDate', style({ opacity: '0.6', })), state('endDate', style({ opacity: '1.0', })), transition('startDate <=> endDate', animate('200ms ease-in')), ]), trigger('indicatorState', [ state('startDate', style({ transform: 'translateX(0%)', })), state('endDate', style({ transform: 'translateX(100%)', })), transition('startDate <=> endDate', animate('200ms ease-in')), ]), ] }); } } __decorate$1([ BooleanInput(), __metadata$1("design:type", Boolean) ], NovoDatePickerElement.prototype, "inline", void 0); __decorate$1([ BooleanInput(), __metadata$1("design:type", Boolean) ], NovoDatePickerElement.prototype, "hideOverflowDays", void 0); __decorate$1([ BooleanInput(), __metadata$1("design:type", Boolean) ], NovoDatePickerElement.prototype, "hideFooter", void 0); __decorate$1([ BooleanInput(), __metadata$1("design:type", Boolean) ], NovoDatePickerElement.prototype, "hideToday", void 0); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoDatePickerElement, decorators: [{ type: Component, args: [{ selector: 'novo-date-picker', providers: [DATE_PICKER_VALUE_ACCESSOR], animations: [ trigger('startDateTextState', [ state('startDate', style({ opacity: '1.0', })), state('endDate', style({ opacity: '0.6', })), transition('startDate <=> endDate', animate('200ms ease-in')), ]), trigger('endDateTextState', [ state('startDate', style({ opacity: '0.6', })), state('endDate', style({ opacity: '1.0', })), transition('startDate <=> endDate', animate('200ms ease-in')), ]), trigger('indicatorState', [ state('startDate', style({ transform: 'translateX(0%)', })), state('endDate', style({ transform: 'translateX(100%)', })), transition('startDate <=> endDate', animate('200ms ease-in')), ]), ], template: ` <div class="date-picker-container"> <div class="date-range-tabs" *ngIf="range" [class.week-select-mode]="weekRangeSelect"> <span class="range-tab" (click)="toggleRangeSelect('startDate')" [@startDateTextState]="rangeSelectMode" data-automation-id="calendar-start-date" >{{ startDateLabel }}</span > <span class="range-tab" (click)="toggleRangeSelect('endDate')" [@endDateTextState]="rangeSelectMode" data-automation-id="calendar-end-date" >{{ endDateLabel }}</span > <i class="indicator" [@indicatorState]="rangeSelectMode"></i> </div> <novo-calendar [activeDate]="activeDate" [(selected)]="selection" (selectedChange)="updateSelection($event)" [mode]="mode" [numberOfMonths]="numberOfMonths" [weekStartsOn]="weekStart" [disabledDateMessage]="disabledDateMessage" [minDate]="start" [maxDate]="end" ></novo-calendar> <div class="calendar-footer" [hidden]="hideFooter"> <novo-button [hidden]="hideToday" (click)="setToday()" class="today" size="small" data-automation-id="calendar-today">{{ labels.today }}</novo-button> <ng-content select=".footer-content"></ng-content> </div> </div> `, standalone: false, styles: [":host{display:block}:host .date-picker-container{border-radius:4px;width:min-content;text-align:center;background:var(--background-main);color:#3a3a3a;-webkit-user-select:none;user-select:none;box-shadow:0 1px 3px #00000026,0 2px 7px #0000001a;z-index:9001;position:relative}:host .date-picker-container .month-view+.month-view{border-collapse:unset;border-left:1px solid #dbdbdb;margin-left:.5rem;padding-left:.5rem}:host .date-picker-container .calendar-top{display:flex;flex-flow:column;background:#4a89dc;color:#fff;font-size:14px;border-top-right-radius:4px;border-top-left-radius:4px}:host .date-picker-container .calendar-top h1{font-weight:600;font-size:4.2em;color:#fff;margin:0;padding:0}:host .date-picker-container .calendar-top h2{font-weight:300;opacity:1;margin:10px auto;padding:0}:host .date-picker-container .calendar-top h3{font-weight:400;opacity:.4;margin:15px auto;padding:0}:host .date-picker-container .calendar-top h4{background:#00000026;font-size:1em;font-weight:300;padding:10px}:host .date-picker-container .date-range-tabs{border-bottom:1px solid #f7f7f7;display:flex;align-items:center;justify-content:space-between;position:relative;height:45px}:host .date-picker-container .date-range-tabs.week-select-mode>span{cursor:default;color:#3d464d;pointer-events:none;opacity:1!important}:host .date-picker-container .date-range-tabs.week-select-mode .indicator{display:none}:host .date-picker-container .date-range-tabs>span{color:#4a89dc;text-align:center;flex:1;cursor:pointer;font-weight:500;transition:opacity .2s ease-in-out;opacity:.6}:host .date-picker-container .date-range-tabs>span:hover{opacity:1!important}:host .date-picker-container .date-range-tabs .indicator{position:absolute;width:50%;height:2px;bottom:0;left:0;background:#4a89dc;transition:transform .2s ease-in-out}:host .date-picker-container .calendar-header{width:100%;display:flex;flex-flow:row nowrap;border-collapse:collapse;padding:14px 0;-webkit-user-select:none;justify-content:space-between;cursor:default;border-bottom:1px solid #f7f7f7}:host .date-picker-container .calendar-header .previous{width:30px;height:15px;display:inline-block;cursor:pointer}:host .date-picker-container .calendar-header .previous:after{content:\"\";border-bottom:4px solid transparent;border-top:4px solid transparent;border-right:4px solid #aaa;display:inline-block;height:0;vertical-align:middle;width:0}:host .date-picker-container .calendar-header .previous:hover:after{border-right:4px solid #4a89dc;cursor:pointer}:host .date-picker-container .calendar-header .heading{flex:1;display:inline-block;vertical-align:middle;color:#4a89dc;font-weight:600}:host .date-picker-container .calendar-header .heading .month{border-radius:2px;padding:3px 8px}:host .date-picker-container .calendar-header .heading .month:hover{background:#4a89dc;color:#fff;cursor:pointer}:host .date-picker-container .calendar-header .heading .year{border-radius:2px;padding:3px 8px}:host .date-picker-container .calendar-header .heading .year:hover{background:#4a89dc;color:#fff;cursor:pointer}:host .date-picker-container .calendar-header .next{width:30px;height:15px;display:inline-block;cursor:pointer}:host .date-picker-container .calendar-header .next:before{content:\"\";border-bottom:4px solid transparent;border-top:4px solid transparent;border-left:4px solid #aaa;display:inline-block;height:0;vertical-align:middle;width:0}:host .date-picker-container .calendar-header .next:hover:before{opacity:1;border-left:4px solid #4a89dc;cursor:pointer}:host .date-picker-container section.calendar-content{display:flex;flex-flow:column}:host .date-picker-container section.calendar-content span{display:block}:host .date-picker-container section.calendar-content.days{flex-flow:row nowrap;height:min-content}:host .date-picker-container .calendar-content{width:100%;height:230px;overflow-y:scroll;position:static;top:0;left:0;transform-origin:209px 26px;transform:scale(1)}:host .date-picker-container .calendar-footer{display:flex;width:100%;padding:1rem .8rem;text-align:left}:host .date-picker-container .calendar-footer .novo-button.today{margin-inline-end:auto}:host ::ng-deep .hide-overflow-days .notinmonth{visibility:hidden}:host .calendar.popup{display:none;position:absolute;z-index:9001}:host .calendar.popup.open{display:block}\n"] }] }], ctorParameters: () => [{ type: i1.NovoLabelService }, { type: i0.ElementRef }, { type: i0.ChangeDetectorRef }, { type: i2.DomSanitizer }], propDecorators: { minYear: [{ type: Input }], maxYear: [{ type: Input }], start: [{ type: Input }], end: [{ type: Input }], inline: [{ type: Input }], weekStart: [{ type: Input }], preselected: [{ type: Input }], hideOverflowDays: [{ type: Input }, { type: HostBinding, args: ['class.hide-overflow-days'] }], hideFooter: [{ type: Input }], hideToday: [{ type: Input }], disabledDateMessage: [{ type: Input }], dateForInitialView: [{ type: Input }], onSelect: [{ type: Output }], numberOfMonths: [{ type: Input }], mode: [{ type: Input }], range: [{ type: Input }], weekRangeSelect: [{ type: Input }] } }); var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; // Value accessor for the component (supports ngModel) const DATE_VALUE_ACCESSOR$1 = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NovoDatePickerInputElement), multi: true, }; class NovoDatePickerInputElement { constructor(element, labels, _changeDetectorRef, dateFormatService, destroyRef) { this.element = element; this.labels = labels; this._changeDetectorRef = _changeDetectorRef; this.dateFormatService = dateFormatService; this.destroyRef = destroyRef; this.formattedValue = ''; this.invalidDateErrorMessage = ''; /** View -> model callback called when value changes */ this._onChange = () => { }; /** View -> model callback called when autocomplete has been touched */ this._onTouched = () => { }; /** * Whether to apply a text mask to the date (see `maskOptions`). Only enabled if allowInvalidDate is false. */ this.textMaskEnabled = true; /** * Whether the input should emit values when the field does not yet constitute a valid date */ this.allowInvalidDate = false; /** * Whether the footer in the date picker which contains `today` button and cancel/save buttons should be hidden. **/ this.hideFooter = false; /** * Whether to hide the 'today' button */ this.hideToday = false; /** * Whether to display the picker together with 'cancel'/'save' buttons */ this.hasButtons = false; /** * Sets the field as to appear disabled, users will not be able to interact with the text field. **/ this.disabled = false; /** * Day of the week the calendar should display first, Sunday=0...Saturday=6 **/ this.weekStart = 0; this.blurEvent = new EventEmitter(); this.focusEvent = new EventEmitter(); this.changeEvent = new EventEmitter(); this.onSave = new EventEmitter(); this.onCancel = new EventEmitter(); this.valueCleared = new EventEmitter(); this.placeholder = this.labels.localizedDatePlaceholder(); } ngOnInit() { this._initFormatOptions(); } ngOnChanges(changes) { if (Object.keys(changes).some((key) => ['format'].includes(key))) { this._initFormatOptions(); } } ngAfterViewInit() { this.overlay.panelClosingActions.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(this._handleOverlayClickout.bind(this)); } _initFormatOptions() { this.userDefinedFormat = this.format ? !this.format.match(/^(DD\/MM\/YYYY|MM\/DD\/YYYY)$/g) : false; if (!this.userDefinedFormat && this.textMaskEnabled && !this.allowInvalidDate) { this.maskOptions = this.maskOptions || this.dateFormatService.getDateMask(); } else { this.maskOptions = undefined; } this.setupInvalidDateErrorMessage(); } /** BEGIN: Convenient Panel Methods. */ openPanel() { if (!this.disabled) { this.overlay.openPanel(); } } closePanel() { this.overlay.closePanel(); } get panelOpen() { return this.overlay?.panelOpen; } get overlayElement() { return this.overlayOnElement || this.element; } /** END: Convenient Panel Methods. */ _handleKeydown(event) { if ((event.key === "Escape" /* Key.Escape */ || event.key === "Enter" /* Key.Enter */ || event.key === "Tab" /* Key.Tab */) && this.panelOpen) { this._handleValueUpdate(event.target.value, true); this.closePanel(); event.stopPropagation(); } } _handleInput(event) { // if maskOptions is enabled, then we do not want to process inputs until the mask has accepted them - so those events will be // handled by the (accept) event. if (document.activeElement === event.target && !this.maskOptions) { this._handleValueUpdate(event.target.value, false); } } _handleBlur(event) { if (!this.overlay.isBlurRecipient(event)) { this.handleInvalidDate(); this.blurEvent.emit(event); } } _handleOverlayClickout() { this.handleInvalidDate(/* fromPanelClose: */ true); this.blurEvent.emit(); } _handleFocus(event) { this.showInvalidDateError = false; this.openPanel(); this.focusEvent.emit(event); } _handleValueUpdate(value, blur) { if (value === '') { this.clearValue(); if (!this.hasButtons) { this.closePanel(); } } else { this.formatDate(value, blur); this.openPanel(); } } handleMaskAccept(maskValue) { this._handleValueUpdate(maskValue, false); } formatDate(value, blur) { try { let dateTimeValue; let isInvalidDate; if (this.format) { [dateTimeValue, , isInvalidDate] = this.dateFormatService.parseCustomDateString(value, this.format); } else { [dateTimeValue, , isInvalidDate] = this.dateFormatService.parseString(value, false, 'date'); } this.isInvalidDate = isInvalidDate; // if we have a full date - set the dateTimeValue if (dateTimeValue?.getFullYear()?.toString().length === 4) { const dt = new Date(dateTimeValue); this.dispatchOnChange(dt, blur); // if we only have a partial date - set the value to null } else if (isNaN(dateTimeValue?.getUTCDate())) { this.dispatchOnChange(null, blur); } } catch (err) { } } writeValue(value) { Promise.resolve(null).then(() => this._setTriggerValue(value)); } registerOnChange(fn) { this._onChange = fn; } registerOnTouched(fn) { this._onTouched = fn; } setDisabledState(disabled) { this.disabled = disabled; } handleInvalidDate(fromPanelClose = false) { if (this.isInvalidDate) { this.showInvalidDateError = true; this.clearValue(); if (!fromPanelClose) { this.closePanel(); } } } setupInvalidDateErrorMessage() { let dateFormat = this.labels.dateFormatString(); if (Helpers.isEmpty(dateFormat)) { // Default to mm/dd/yyyy dateFormat = 'mm/dd/yyyy'; } else { dateFormat = dateFormat.toLowerCase(); } this.invalidDateErrorMessage = `Invalid date field entered. Date format of ${dateFormat} is required.`; } dispatchOnChange(newValue, blur = false, skip = false) { if (newValue !== this.value) { this._onChange(newValue); this.changeEvent.emit(newValue); if (blur) { !skip && this.writeValue(newValue); } else { !skip && this._setCalendarValue(newValue); } } } _setTriggerValue(value) { this._setCalendarValue(value); this._setFormValue(value); this._changeDetectorRef.markForCheck(); } _setCalendarValue(value) { if (value instanceof Date && this.value instanceof Date) { const newDate = new Date(value); newDate.setHours(0, 0, 0, 0); this.value = newDate; return; } this.value = value; } _setFormValue(value) { if (value) { const test = this.formatDateValue(value); this.formattedValue = test; } else { this.formattedValue = ''; } } onSelected(event) { this.setValue(event); if (!this.hasButtons) { this.closePanel(); } } setValue(event) { if (event?.date) { this.showInvalidDateError = false; this.dispatchOnChange(event.date, true); } } /** * This method closes the panel, and if a value is specified, also sets the associated * control to that value. It will also mark the control as dirty if this interaction * stemmed from the user. */ setValueAndClose(event) { this.setValue(event); this.closePanel(); } /** * Respond to clicking the X button within the input */ clearAction() { this.clearValue(); this.valueCleared.emit(); } /** * Clear any previous selected option and emit a selection change event for this option */ clearValue() { this._setFormValue(null); this.dispatchOnChange(null); } formatDateValue(value) { const originalValue = value; try { if (!value) { return ''; } if (this.userDefinedFormat && isValid(value)) { return DateUtil.format(value, this.format); } if (!(value instanceof Date)) { value = new Date(value); } if (!(isNaN(value.valueOf()) && this.allowInvalidDate)) { return this.labels.formatDateWithFormat(value, { month: '2-digit', day: '2-digit', year: 'numeric', }); } else { return originalValue; } } catch (err) { return err; } } get hasValue() { return !Helpers.isEmpty(this.value); } save() { this.onSave.emit(); } cancel() { this.onCancel.emit(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoDatePickerInputElement, deps: [{ token: i0.ElementRef }, { token: i1.NovoLabelService }, { token: i0.ChangeDetectorRef }, { token: i1.DateFormatService }, { token: i0.DestroyRef }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: NovoDatePickerInputElement, isStandalone: false, selector: "novo-date-picker-input", inputs: { name: "name", start: "start", end: "end", placeholder: "placeholder", maskOptions: "maskOptions", format: "format", textMaskEnabled: "textMaskEnabled", allowInvalidDate: "allowInvalidDate", overlayOnElement: "overlayOnElement", hideFooter: "hideFooter", hideToday: "hideToday", hasButtons: "hasButtons", disabled: "disabled", disabledDateMessage: "disabledDateMessage", dateForInitialView: "dateForInitialView", weekStart: "weekStart" }, outputs: { blurEvent: "blurEvent", focusEvent: "focusEvent", changeEvent: "changeEvent", onSave: "onSave", onCancel: "onCancel", valueCleared: "valueCleared" }, host: { properties: { "class.disabled": "this.disabled" } }, providers: [DATE_VALUE_ACCESSOR$1], viewQueries: [{ propertyName: "overlay", first: true, predicate: NovoOverlayTemplateComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: ` <input type="text" [name]="name" [(ngModel)]="formattedValue" [imask]="maskOptions" [placeholder]="placeholder" (focus)="_handleFocus($event)" (keydown)="_handleKeydown($event)" (input)="_handleInput($event)" (blur)="_handleBlur($event)" (accept)="handleMaskAccept($event)" #input data-automation-id="date-input" [disabled]="disabled" /> <span class="error-text" *ngIf="showInvalidDateError">{{ invalidDateErrorMessage }}</span> <i *ngIf="!hasValue" (click)="openPanel()" class="bhi-calendar"></i> <i *ngIf="hasValue" (click)="clearAction()" class="bhi-times"></i> <novo-overlay-template [parent]="overlayElement" position="above-below"> <novo-date-picker [start]="start" [end]="end" inline="true" (onSelect)="onSelected($event)" [disabledDateMessage]="disabledDateMessage" [ngModel]="value" [weekStart]="weekStart" [hideFooter]="hideFooter" [hideToday]="hideToday" [dateForInitialView]="dateForInitialView"> <div *ngIf="hasButtons" class="footer-content"> <novo-button class="cancel-button" data-automation-id="date-picker-cancel" theme="dialogue" size="small" (click)="cancel()">{{ labels.cancel }}</novo-button> <novo-button class="save-button" data-automation-id="date-picker-save" theme="primary" color="primary" size="small" (click)="save()">{{ labels.save }}</novo-button> </div> </novo-date-picker> </novo-overlay-template> `, isInline: true, styles: [":host{flex:1;position:relative;display:block!important}:host.disabled{pointer-events:none;opacity:1}:host input{font-size:1em;border:none;border-bottom:1px solid #dbdbdb;background:transparent!important;border-radius:0;outline:none;height:2rem;width:100%;margin:0;padding:0;box-shadow:none;box-sizing:content-box;transition:all .3s;color:#3d464d}:host input:focus{border-bottom:1px solid #4a89dc}:host span.error-text{color:#da4453;padding-top:10px;flex:1;display:flex}:host>i.bhi-clock,:host>i.bhi-search,:host>i.bhi-times,:host>i.bhi-calendar{position:absolute;right:0;top:0;font-size:1.2rem;cursor:pointer}\n"], dependencies: [{ kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i4.NovoButtonElement, selector: "novo-button,button[theme]", inputs: ["color", "side", "size", "theme", "loading", "icon", "secondIcon", "disabled"] }, { kind: "component", type: i4$1.NovoOverlayTemplateComponent, selector: "novo-overlay-template", inputs: ["position", "scrollStrategy", "width", "minWidth", "height", "closeOnSelect", "hasBackdrop", "parent"], outputs: ["select", "opening", "closing", "backDropClicked"] }, { kind: "directive", type: i6.IMaskDirective, selector: "[imask]", inputs: ["imask", "unmask", "imaskElement"], outputs: ["accept", "complete"], exportAs: ["imask"] }, { kind: "component", type: NovoDatePickerElement, selector: "novo-date-picker", inputs: ["minYear", "maxYear", "start", "end", "inline", "weekStart", "preselected", "hideOverflowDays", "hideFooter", "hideToday", "disabledDateMessage", "dateForInitialView", "numberOfMonths", "mode", "range", "weekRangeSelect"], outputs: ["onSelect"] }] }); } } __decorate([ BooleanInput(), __metadata("design:type", Boolean) ], NovoDatePickerInputElement.prototype, "hideFooter", void 0); __decorate([ BooleanInput(), __metadata("design:type", Boolean) ], NovoDatePickerInputElement.prototype, "hideToday", void 0); __decorate([ BooleanInput(), __metadata("design:type", Boolean) ], NovoDatePickerInputElement.prototype, "hasButtons", void 0); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: NovoDatePickerInputElement, decorators: [{ type: Component, args: [{ selector: 'novo-date-picker-input', providers: [DATE_VALUE_ACCESSOR$1], template: ` <input type="text" [name]="name" [(ngModel)]="formattedValue" [imask]="maskOptions" [placeholder]="placeholder" (focus)="_handleFocus($event)" (keydown)="_handleKeydown($event)" (input)="_handleInput($event)" (blur)="_handleBlur($event)" (accept)="handleMaskAccept($event)" #input data-automation-id="date-input" [disabled]="disabled" /> <span class="error-text" *ngIf="showInvalidDateError">{{ invalidDateErrorMessage }}</span> <i *ngIf="!hasValue" (click)="openPanel()" class="bhi-calendar"></i> <i *ngIf="hasValue" (click)="clearAction()" class="bhi-times"></i> <novo-overlay-template [parent]="overlayElement" position="above-below"> <novo-date-picker [start]="start" [end]="end" inline="true" (onSelect)="onSelected($event)" [disabledDateMessage]="disabledDateMessage" [ngModel]="value" [weekStart]="weekStart" [hideFooter]="hideFooter" [hideToday]="hideToday" [dateForInitialView]="dateForInitialView"> <div *ngIf="hasButtons" class="footer-content"> <novo-button class="cancel-button" data-automation-id="date-picker-cancel" theme="dialogue" size="small" (click)="cancel()">{{ labels.cancel }}</novo-button> <novo-button class="save-button" data-automation-id="date-picker-save" theme="primary" color="primary" size="small" (click)="save()">{{ labels.save }}</novo-button> </div> </novo-date-picker> </novo-overlay-template> `, standalone: false, styles: [":host{flex:1;position:relative;display:block!important}:host.disabled{pointer-events:none;opacity:1}:host input{font-size:1em;border:none;border-bottom:1px solid #dbdbdb;background:transparent!important;border-radius:0;outline:none;height:2rem;width:100%;margin:0;padding:0;box-shadow:none;box-sizing:content-box;transition:all .3s;color:#3d464d}:host input:focus{border-bottom:1px solid #4a89dc}:host span.error-text{color:#da4453;padding-top:10px;flex:1;display:flex}:host>i.bhi-clock,:host>i.bhi-search,:host>i.bhi-times,:host>i.bhi-calendar{position:absolute;right:0;top:0;font-size:1.2rem;cursor:pointer}\n"] }] }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1.NovoLabelService }, { type: i0.ChangeDetectorRef }, { type: i1.DateFormatService }, { type: i0.DestroyRef }], propDecorators: { name: [{ type: Input }], start: [{ type: Input }], end: [{ type: Input }], placeholder: [{ type: Input }], maskOptions: [{ type: Input }], format: [{ type: Input }], textMaskEnabled: [{ type: Input }], allowInvalidDate: [{ type: Input }], overlayOnElement: [{ type: Input }], hideFooter: [{ type: Input }], hideToday: [{ type: Input }], hasButtons: [{ type: Input }], disabled: [{ type: HostBinding, args: ['class.disabled'] }, { type: Input }], disabledDateMessage: [{ type: Input }], dateForInitialView: [{ type: Input }], weekStart: [{ type: Input }], blurEvent: [{ type: Output }], focusEvent: [{ type: Output }], changeEvent: [{ type: Output }], onSave: [{ type: Output }], onCancel: [{ type: Output }], valueCleared: [{ type: Output }], overlay: [{ type: ViewChild, args: [NovoOverlayTemplateComponent] }] } }); // NG // Value accessor for the component (supports ngModel) const DATE_VALUE_ACCESSOR = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NovoDateRangeInputElement), multi: true, }; class NovoDateRangeInputElement { get value() { return this._value; } set value(value) { if (this.value !== value) { this._value = value; this._setFormValue(value); this.onChangeCallback(this._value); } } // Disabled State get disabled() { return this._disabled; } set disabled(value) { this._disabled = !!value; } constructor(element, labels, cdr, dateFormatService) { this.element = element; this.labels = labels; this.cdr = cdr; this.dateFormatService = dateFormatService; this.formattedStartDate = ''; this.formattedEndDate = ''; this.weekRangeSelect = false; this.mode = 'range'; this.textMaskEnabled = true; this.allowInvalidDate = false; this.weekStart = 0; this.blurEvent = new EventEmitter(); this.focusEvent = new EventEmitter(); this.change = new EventEmitter();