novo-elements
Version:
994 lines (984 loc) • 79.1 kB
JavaScript
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();