@asoftwareworld/form-builder-pro
Version:
ASW Form Builder Pro helps you with rapid development and designed web forms which includes several controls. The key feature of Form Builder is to make your content attractive and effective. We can customize our control at run time and preview the same b
839 lines (831 loc) • 205 kB
JavaScript
import * as i2$1 from '@angular/cdk/a11y';
import { A11yModule } from '@angular/cdk/a11y';
import * as i4$1 from '@angular/cdk/overlay';
import { Overlay, OverlayConfig, OverlayModule } from '@angular/cdk/overlay';
import { ComponentPortal, PortalModule } from '@angular/cdk/portal';
import * as i2 from '@angular/common';
import { DOCUMENT, CommonModule } from '@angular/common';
import * as i0 from '@angular/core';
import { EventEmitter, Output, Input, ChangeDetectionStrategy, ViewEncapsulation, Component, Optional, Inject, Injectable, Directive, ElementRef, ViewChild, InjectionToken, inject, forwardRef, ContentChild, Attribute, NgModule } from '@angular/core';
import * as i4 from '@angular/material/button';
import { MatButtonModule } from '@angular/material/button';
import { UP_ARROW, DOWN_ARROW, ENTER, PAGE_DOWN, PAGE_UP, END, HOME, RIGHT_ARROW, LEFT_ARROW, hasModifierKey, ESCAPE } from '@angular/cdk/keycodes';
import { coerceNumberProperty, coerceBooleanProperty, coerceStringArray } from '@angular/cdk/coercion';
import { first, take, filter } from 'rxjs/operators';
import * as i1 from '@asoftwareworld/form-builder-pro/common';
import { ASW_DATETIME_FORMATS, Icons, AswPipeModule } from '@asoftwareworld/form-builder-pro/common';
import { trigger, transition, animate, keyframes, style, state } from '@angular/animations';
import { Subject, Subscription, merge, of } from 'rxjs';
import { normalizePassiveListenerOptions, _getFocusedElementPierceShadowDom } from '@angular/cdk/platform';
import { mixinColor } from '@angular/material/core';
import * as i6 from '@angular/cdk/bidi';
import { NG_VALUE_ACCESSOR, NG_VALIDATORS, Validators } from '@angular/forms';
import { MAT_INPUT_VALUE_ACCESSOR } from '@angular/material/input';
import * as i2$2 from '@angular/material/form-field';
/**
* Animations used by the Material datepicker.
* @docs-private
*/
const aswDatetimepickerAnimations = {
/** Transforms the height of the datepicker's calendar. */
transformPanel: trigger('transformPanel', [
transition('void => enter-dropdown', animate('120ms cubic-bezier(0, 0, 0.2, 1)', keyframes([style({ opacity: 0, transform: 'scale(1, 0.8)' }), style({ opacity: 1, transform: 'scale(1, 1)' })]))),
transition('void => enter-dialog', animate('150ms cubic-bezier(0, 0, 0.2, 1)', keyframes([style({ opacity: 0, transform: 'scale(0.7)' }), style({ transform: 'none', opacity: 1 })]))),
transition('* => void', animate('100ms linear', style({ opacity: 0 })))
]),
/** Fades in the content of the calendar. */
fadeInCalendar: trigger('fadeInCalendar', [
state('void', style({ opacity: 0 })),
state('enter', style({ opacity: 1 })),
// TODO(crisbeto): this animation should be removed since it isn't quite on spec, but we
// need to keep it until #12440 gets in, otherwise the exit animation will look glitchy.
transition('void => *', animate('120ms 100ms cubic-bezier(0.55, 0, 0.55, 0.2)'))
]),
slideCalendar: trigger('slideCalendar', [
transition('* => left', [animate(180, keyframes([style({ transform: 'translateX(100%)', offset: 0.5 }), style({ transform: 'translateX(-100%)', offset: 0.51 }), style({ transform: 'translateX(0)', offset: 1 })]))]),
transition('* => right', [animate(180, keyframes([style({ transform: 'translateX(-100%)', offset: 0.5 }), style({ transform: 'translateX(100%)', offset: 0.51 }), style({ transform: 'translateX(0)', offset: 1 })]))])
])
};
/** @docs-private */
function createMissingDateImplError(provider) {
return Error(`AswDatetimepicker: No provider found for ${provider}. You must import one of the following ` + `modules at your application root: AswNativeDatetimeModule, AswMomentDatetimeModule, or provide a ` + `custom implementation.`);
}
var AswDatetimepickerFilterType;
(function (AswDatetimepickerFilterType) {
AswDatetimepickerFilterType[AswDatetimepickerFilterType["DATE"] = 0] = "DATE";
AswDatetimepickerFilterType[AswDatetimepickerFilterType["HOUR"] = 1] = "HOUR";
AswDatetimepickerFilterType[AswDatetimepickerFilterType["MINUTE"] = 2] = "MINUTE";
})(AswDatetimepickerFilterType || (AswDatetimepickerFilterType = {}));
/**
* An internal class that represents the data corresponding to a single calendar cell.
* @docs-private
*/
class AswCalendarCell {
value;
displayValue;
ariaLabel;
enabled;
constructor(value, displayValue, ariaLabel, enabled) {
this.value = value;
this.displayValue = displayValue;
this.ariaLabel = ariaLabel;
this.enabled = enabled;
}
}
/**
* An internal component used to display calendar data in a table.
* @docs-private
*/
class AswCalendarBody {
/** The label for the table. (e.g. "Jan 2017"). */
label;
/** The cells to display in the table. */
rows;
/** The value in the table that corresponds to today. */
todayValue;
/** The value in the table that is currently selected. */
selectedValue;
/** The minimum number of free cells needed to fit the label in the first row. */
labelMinRequiredCells;
/** The number of columns in the table. */
numCols = 7;
/** Whether to allow selection of disabled cells. */
allowDisabledSelection = false;
/** The cell number of the active cell in the table. */
activeCell = 0;
/** Emits when a new value is selected. */
selectedValueChange = new EventEmitter();
/** The number of blank cells to put at the beginning for the first row. */
get _firstRowOffset() {
return this.rows && this.rows.length && this.rows[0].length ? this.numCols - this.rows[0].length : 0;
}
_cellClicked(cell) {
if (!this.allowDisabledSelection && !cell.enabled) {
return;
}
this.selectedValueChange.emit(cell.value);
}
_isActiveCell(rowIndex, colIndex) {
let cellNumber = rowIndex * this.numCols + colIndex;
// Account for the fact that the first row may not have as many cells.
if (rowIndex) {
cellNumber -= this._firstRowOffset;
}
return cellNumber === this.activeCell;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: AswCalendarBody, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.8", type: AswCalendarBody, selector: "[asw-calendar-body]", inputs: { label: "label", rows: "rows", todayValue: "todayValue", selectedValue: "selectedValue", labelMinRequiredCells: "labelMinRequiredCells", numCols: "numCols", allowDisabledSelection: "allowDisabledSelection", activeCell: "activeCell" }, outputs: { selectedValueChange: "selectedValueChange" }, host: { classAttribute: "asw-calendar-body" }, exportAs: ["aswCalendarBody"], ngImport: i0, template: "<!--\r\n If there's not enough space in the first row, create a separate label row. We mark this row as\r\n aria-hidden because we don't want it to be read out as one of the weeks in the month.\r\n-->\r\n<tr *ngIf=\"_firstRowOffset < labelMinRequiredCells\" aria-hidden=\"true\">\r\n <td class=\"asw-calendar-body-label\" [attr.colspan]=\"numCols\">{{ label }}</td>\r\n</tr>\r\n\r\n<!-- Create the first row separately so we can include a special spacer cell. -->\r\n<tr *ngFor=\"let row of rows; let rowIndex = index\" role=\"row\">\r\n <!--\r\n We mark this cell as aria-hidden so it doesn't get read out as one of the days in the week.\r\n -->\r\n <td *ngIf=\"rowIndex === 0 && _firstRowOffset\"\r\n class=\"asw-calendar-body-label\" [attr.colspan]=\"_firstRowOffset\" aria-hidden=\"true\">\r\n {{ _firstRowOffset >= labelMinRequiredCells ? label : '' }}\r\n </td>\r\n <td *ngFor=\"let item of row; let colIndex = index\"\r\n role=\"gridcell\"\r\n class=\"asw-calendar-body-cell\"\r\n [class.asw-calendar-body-active]=\"_isActiveCell(rowIndex, colIndex)\"\r\n [class.asw-calendar-body-disabled]=\"!item.enabled\"\r\n [tabindex]=\"_isActiveCell(rowIndex, colIndex) ? 0 : -1\"\r\n [attr.data-mat-row]=\"rowIndex\"\r\n [attr.data-mat-col]=\"colIndex\"\r\n [attr.aria-label]=\"item.ariaLabel\"\r\n [attr.aria-disabled]=\"!item.enabled || null\"\r\n (click)=\"_cellClicked(item)\">\r\n <div class=\"asw-calendar-body-cell-content\"\r\n [class.asw-calendar-body-selected]=\"selectedValue === item.value\"\r\n [class.asw-calendar-body-today]=\"todayValue === item.value\"\r\n [attr.aria-selected]=\"selectedValue === item.value\">\r\n {{ item.displayValue }}\r\n </div>\r\n </td>\r\n</tr>\r\n", styles: [".asw-calendar-body{min-width:224px}.asw-calendar-body-label{height:0;line-height:0;text-align:left;padding:7.1428571429% 4.7142857143%}[dir=rtl] .asw-calendar-body-label{text-align:right}.asw-calendar-body-cell{position:relative;width:14.2857142857%;height:0;line-height:0;padding:7.1428571429% 0;text-align:center;outline:none;cursor:pointer}.asw-calendar-body-disabled{cursor:default;pointer-events:none}.asw-calendar-body-cell-content{position:absolute;top:5%;left:5%;display:flex;align-items:center;justify-content:center;box-sizing:border-box;width:90%;height:90%;border:1px solid transparent;border-radius:999px}\n"], dependencies: [{ kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: AswCalendarBody, decorators: [{
type: Component,
args: [{ selector: '[asw-calendar-body]', host: {
class: 'asw-calendar-body'
}, exportAs: 'aswCalendarBody', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\r\n If there's not enough space in the first row, create a separate label row. We mark this row as\r\n aria-hidden because we don't want it to be read out as one of the weeks in the month.\r\n-->\r\n<tr *ngIf=\"_firstRowOffset < labelMinRequiredCells\" aria-hidden=\"true\">\r\n <td class=\"asw-calendar-body-label\" [attr.colspan]=\"numCols\">{{ label }}</td>\r\n</tr>\r\n\r\n<!-- Create the first row separately so we can include a special spacer cell. -->\r\n<tr *ngFor=\"let row of rows; let rowIndex = index\" role=\"row\">\r\n <!--\r\n We mark this cell as aria-hidden so it doesn't get read out as one of the days in the week.\r\n -->\r\n <td *ngIf=\"rowIndex === 0 && _firstRowOffset\"\r\n class=\"asw-calendar-body-label\" [attr.colspan]=\"_firstRowOffset\" aria-hidden=\"true\">\r\n {{ _firstRowOffset >= labelMinRequiredCells ? label : '' }}\r\n </td>\r\n <td *ngFor=\"let item of row; let colIndex = index\"\r\n role=\"gridcell\"\r\n class=\"asw-calendar-body-cell\"\r\n [class.asw-calendar-body-active]=\"_isActiveCell(rowIndex, colIndex)\"\r\n [class.asw-calendar-body-disabled]=\"!item.enabled\"\r\n [tabindex]=\"_isActiveCell(rowIndex, colIndex) ? 0 : -1\"\r\n [attr.data-mat-row]=\"rowIndex\"\r\n [attr.data-mat-col]=\"colIndex\"\r\n [attr.aria-label]=\"item.ariaLabel\"\r\n [attr.aria-disabled]=\"!item.enabled || null\"\r\n (click)=\"_cellClicked(item)\">\r\n <div class=\"asw-calendar-body-cell-content\"\r\n [class.asw-calendar-body-selected]=\"selectedValue === item.value\"\r\n [class.asw-calendar-body-today]=\"todayValue === item.value\"\r\n [attr.aria-selected]=\"selectedValue === item.value\">\r\n {{ item.displayValue }}\r\n </div>\r\n </td>\r\n</tr>\r\n", styles: [".asw-calendar-body{min-width:224px}.asw-calendar-body-label{height:0;line-height:0;text-align:left;padding:7.1428571429% 4.7142857143%}[dir=rtl] .asw-calendar-body-label{text-align:right}.asw-calendar-body-cell{position:relative;width:14.2857142857%;height:0;line-height:0;padding:7.1428571429% 0;text-align:center;outline:none;cursor:pointer}.asw-calendar-body-disabled{cursor:default;pointer-events:none}.asw-calendar-body-cell-content{position:absolute;top:5%;left:5%;display:flex;align-items:center;justify-content:center;box-sizing:border-box;width:90%;height:90%;border:1px solid transparent;border-radius:999px}\n"] }]
}], propDecorators: { label: [{
type: Input
}], rows: [{
type: Input
}], todayValue: [{
type: Input
}], selectedValue: [{
type: Input
}], labelMinRequiredCells: [{
type: Input
}], numCols: [{
type: Input
}], allowDisabledSelection: [{
type: Input
}], activeCell: [{
type: Input
}], selectedValueChange: [{
type: Output
}] } });
const yearsPerPage = 24;
const yearsPerRow = 4;
/**
* An internal component used to display multiple years in the datetimepicker.
* @docs-private
*/
class AswMultiYearView {
_adapter;
_dateFormats;
type = 'date';
/** A function used to filter which dates are selectable. */
dateFilter;
/** Emits when a new month is selected. */
selectedChange = new EventEmitter();
/** Emits when any date is selected. */
_userSelection = new EventEmitter();
/** Grid of calendar cells representing the years in the range. */
_years;
/** The label for this year range (e.g. "2000-2020"). */
_yearLabel;
/** The year in this range that today falls on. Null if today is in a different range. */
_todayYear;
/**
* The year in this range that the selected Date falls on.
* Null if the selected Date is in a different range.
*/
_selectedYear;
_calendarState;
constructor(_adapter, _dateFormats) {
this._adapter = _adapter;
this._dateFormats = _dateFormats;
if (!this._adapter) {
throw createMissingDateImplError('DatetimeAdapter');
}
if (!this._dateFormats) {
throw createMissingDateImplError('ASW_DATETIME_FORMATS');
}
this._activeDate = this._adapter.today();
}
/** The date to display in this multi year view */
get activeDate() {
return this._activeDate;
}
set activeDate(value) {
const oldActiveDate = this._activeDate;
this._activeDate = value || this._adapter.today();
if (oldActiveDate && this._activeDate && !isSameMultiYearView(this._adapter, oldActiveDate, this._activeDate, this.minDate, this.maxDate)) {
this._init();
}
}
_activeDate;
/** The currently selected date. */
get selected() {
return this._selected;
}
set selected(value) {
this._selected = value;
this._selectedYear = this._selected && this._adapter.getYear(this._selected);
}
_selected;
/** The minimum selectable date. */
get minDate() {
return this._minDate;
}
set minDate(value) {
this._minDate = this._getValidDateOrNull(this._adapter.deserialize(value));
}
_minDate;
/** The maximum selectable date. */
get maxDate() {
return this._maxDate;
}
set maxDate(value) {
this._maxDate = this._getValidDateOrNull(this._adapter.deserialize(value));
}
_maxDate;
ngAfterContentInit() {
this._init();
}
/** Handles when a new year is selected. */
_yearSelected(year) {
const month = this._adapter.getMonth(this.activeDate);
const normalizedDate = this._adapter.createDatetime(year, month, 1, 0, 0);
this.selectedChange.emit(this._adapter.createDatetime(year, month, Math.min(this._adapter.getDate(this.activeDate), this._adapter.getNumDaysInMonth(normalizedDate)), this._adapter.getHour(this.activeDate), this._adapter.getMinute(this.activeDate)));
if (this.type === 'year') {
this._userSelection.emit();
}
}
_getActiveCell() {
return getActiveOffset(this._adapter, this.activeDate, this.minDate, this.maxDate);
}
_calendarStateDone() {
this._calendarState = '';
}
/** Initializes this year view. */
_init() {
this._todayYear = this._adapter.getYear(this._adapter.today());
this._yearLabel = this._adapter.getYearName(this.activeDate);
const activeYear = this._adapter.getYear(this.activeDate);
const minYearOfPage = activeYear - getActiveOffset(this._adapter, this.activeDate, this.minDate, this.maxDate);
this._years = [];
for (let i = 0, row = []; i < yearsPerPage; i++) {
row.push(minYearOfPage + i);
if (row.length === yearsPerRow) {
this._years.push(row.map((year) => this._createCellForYear(year)));
row = [];
}
}
}
/** Creates an AswCalendarCell for the given year. */
_createCellForYear(year) {
const yearName = this._adapter.getYearName(this._adapter.createDate(year, 0, 1));
return new AswCalendarCell(year, yearName, yearName, this._shouldEnableYear(year));
}
/** Whether the given year is enabled. */
_shouldEnableYear(year) {
// disable if the year is greater than maxDate lower than minDate
if (year === undefined || year === null || (this.maxDate && year > this._adapter.getYear(this.maxDate)) || (this.minDate && year < this._adapter.getYear(this.minDate))) {
return false;
}
// enable if it reaches here and there's no filter defined
if (!this.dateFilter) {
return true;
}
const firstOfYear = this._adapter.createDate(year, 0, 1);
// If any date in the year is enabled count the year as enabled.
for (let date = firstOfYear; this._adapter.getYear(date) === year; date = this._adapter.addCalendarDays(date, 1)) {
if (this.dateFilter(date)) {
return true;
}
}
return false;
}
/**
* Gets the year in this years range that the given Date falls on.
* Returns null if the given Date is not in this range.
*/
_getYearInCurrentRange(date) {
const year = this._adapter.getYear(date);
return this._isInRange(year) ? year : null;
}
/**
* Validate if the current year is in the current range
* Returns true if is in range else returns false
*/
_isInRange(year) {
return true;
}
/**
* @param obj The object to check.
* @returns The given object if it is both a date instance and valid, otherwise null.
*/
_getValidDateOrNull(obj) {
return this._adapter.isDateInstance(obj) && this._adapter.isValid(obj) ? obj : null;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: AswMultiYearView, deps: [{ token: i1.DatetimeAdapter, optional: true }, { token: ASW_DATETIME_FORMATS, optional: true }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.8", type: AswMultiYearView, selector: "asw-multi-year-view", inputs: { type: "type", dateFilter: "dateFilter", activeDate: "activeDate", selected: "selected", minDate: "minDate", maxDate: "maxDate" }, outputs: { selectedChange: "selectedChange", _userSelection: "_userSelection" }, exportAs: ["aswMultiYearView"], ngImport: i0, template: "<table class=\"asw-calendar-table\" role=\"grid\">\r\n <thead class=\"asw-calendar-table-header\"></thead>\r\n <tbody asw-calendar-body\r\n (@slideCalendar.done)=\"_calendarStateDone()\"\r\n [@slideCalendar]=\"_calendarState\"\r\n [todayValue]=\"_todayYear\"\r\n [rows]=\"_years\"\r\n [numCols]=\"4\"\r\n [activeCell]=\"_getActiveCell()\"\r\n [allowDisabledSelection]=\"true\"\r\n [selectedValue]=\"_selectedYear!\"\r\n (selectedValueChange)=\"_yearSelected($event)\">\r\n </tbody>\r\n</table>\r\n", dependencies: [{ kind: "component", type: AswCalendarBody, selector: "[asw-calendar-body]", inputs: ["label", "rows", "todayValue", "selectedValue", "labelMinRequiredCells", "numCols", "allowDisabledSelection", "activeCell"], outputs: ["selectedValueChange"], exportAs: ["aswCalendarBody"] }], animations: [aswDatetimepickerAnimations.slideCalendar], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: AswMultiYearView, decorators: [{
type: Component,
args: [{ selector: 'asw-multi-year-view', exportAs: 'aswMultiYearView', animations: [aswDatetimepickerAnimations.slideCalendar], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<table class=\"asw-calendar-table\" role=\"grid\">\r\n <thead class=\"asw-calendar-table-header\"></thead>\r\n <tbody asw-calendar-body\r\n (@slideCalendar.done)=\"_calendarStateDone()\"\r\n [@slideCalendar]=\"_calendarState\"\r\n [todayValue]=\"_todayYear\"\r\n [rows]=\"_years\"\r\n [numCols]=\"4\"\r\n [activeCell]=\"_getActiveCell()\"\r\n [allowDisabledSelection]=\"true\"\r\n [selectedValue]=\"_selectedYear!\"\r\n (selectedValueChange)=\"_yearSelected($event)\">\r\n </tbody>\r\n</table>\r\n" }]
}], ctorParameters: () => [{ type: i1.DatetimeAdapter, decorators: [{
type: Optional
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [ASW_DATETIME_FORMATS]
}] }], propDecorators: { type: [{
type: Input
}], dateFilter: [{
type: Input
}], selectedChange: [{
type: Output
}], _userSelection: [{
type: Output
}], activeDate: [{
type: Input
}], selected: [{
type: Input
}], minDate: [{
type: Input
}], maxDate: [{
type: Input
}] } });
function isSameMultiYearView(dateAdapter, date1, date2, minDate, maxDate) {
const year1 = dateAdapter.getYear(date1);
const year2 = dateAdapter.getYear(date2);
const startingYear = getStartingYear(dateAdapter, minDate, maxDate);
return Math.floor((year1 - startingYear) / yearsPerPage) === Math.floor((year2 - startingYear) / yearsPerPage);
}
/**
* When the multi-year view is first opened, the active year will be in view.
* So we compute how many years are between the active year and the *slot* where our
* "startingYear" will render when paged into view.
*/
function getActiveOffset(dateAdapter, activeDate, minDate, maxDate) {
const activeYear = dateAdapter.getYear(activeDate);
return euclideanModulo(activeYear - getStartingYear(dateAdapter, minDate, maxDate), yearsPerPage);
}
/**
* We pick a "starting" year such that either the maximum year would be at the end
* or the minimum year would be at the beginning of a page.
*/
function getStartingYear(dateAdapter, minDate, maxDate) {
let startingYear = 0;
if (maxDate) {
const maxYear = dateAdapter.getYear(maxDate);
startingYear = maxYear - yearsPerPage + 1;
}
else if (minDate) {
startingYear = dateAdapter.getYear(minDate);
}
return startingYear;
}
/** Gets remainder that is non-negative, even if first number is negative */
function euclideanModulo(a, b) {
return ((a % b) + b) % b;
}
class AswDatetimepickerIntl {
/**
* Stream to emit from when labels are changed. Use this to notify components when the labels have
* changed after initialization.
*/
changes = new Subject();
/** A label for the calendar popup (used by screen readers). */
calendarLabel = 'Calendar';
/** A label for the button used to open the calendar popup (used by screen readers). */
openCalendarLabel = 'Open calendar';
/** Label for the button used to close the calendar popup. */
closeCalendarLabel = 'Close calendar';
/** A label for the previous month button (used by screen readers). */
prevMonthLabel = 'Previous month';
/** A label for the next month button (used by screen readers). */
nextMonthLabel = 'Next month';
/** A label for the previous year button (used by screen readers). */
prevYearLabel = 'Previous year';
/** A label for the next year button (used by screen readers). */
nextYearLabel = 'Next year';
/** A label for the previous multi-year button (used by screen readers). */
prevMultiYearLabel = 'Previous 24 years';
/** A label for the next multi-year button (used by screen readers). */
nextMultiYearLabel = 'Next 24 years';
/** A label for the 'switch to month view' button (used by screen readers). */
switchToMonthViewLabel = 'Choose date';
/** A label for the 'switch to year view' button (used by screen readers). */
switchToYearViewLabel = 'Choose month';
/** A label for the 'switch to multi-year view' button (used by screen readers). */
switchToMultiYearViewLabel = 'Choose month and year';
/** A label for the first date of a range of dates (used by screen readers). */
startDateLabel = 'Start date';
/** A label for the last date of a range of dates (used by screen readers). */
endDateLabel = 'End date';
/** Formats a range of years (used for visuals). */
formatYearRange(start, end) {
return `${start} \u2013 ${end}`;
}
/** Formats a label for a range of years (used by screen readers). */
formatYearRangeLabel(start, end) {
return `${start} to ${end}`;
}
/** A label for the 'switch to clock hour view' button (used by screen readers). */
switchToClockHourViewLabel = 'Choose hour';
/** A label for the 'switch to clock minute view' button (used by screen readers). */
switchToClockMinuteViewLabel = 'Choose minute';
/** Label used for ok button within the manual time input. */
okLabel = 'OK';
/** Label used for cancel button within the manual time input. */
cancelLabel = 'Cancel';
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: AswDatetimepickerIntl, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: AswDatetimepickerIntl, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: AswDatetimepickerIntl, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}] });
const activeEventOptions = normalizePassiveListenerOptions({ passive: false });
const CLOCK_RADIUS = 50;
const CLOCK_INNER_RADIUS = 27.5;
const CLOCK_OUTER_RADIUS = 41.25;
const CLOCK_TICK_RADIUS = 7.0833;
/**
* A clock that is used as part of the datetimepicker.
* @docs-private
*/
class AswClock {
_elementRef;
_adapter;
_changeDetectorRef;
_document;
/** A function used to filter which dates are selectable. */
dateFilter;
/** Step over minutes. */
interval = 1;
/** Whether the clock uses 12 hour format. */
twelvehour = false;
/** Whether the time is now in AM or PM. */
AMPM = 'AM';
/** Emits when the currently selected date changes. */
selectedChange = new EventEmitter();
/** Emits when any date is activated. */
activeDateChange = new EventEmitter();
/** Emits when any date is selected. */
_userSelection = new EventEmitter();
/** Whether the clock is in hour view. */
_hourView = true;
_hours = [];
_minutes = [];
_selectedHour;
_selectedMinute;
_timeChanged = false;
constructor(_elementRef, _adapter, _changeDetectorRef, _document) {
this._elementRef = _elementRef;
this._adapter = _adapter;
this._changeDetectorRef = _changeDetectorRef;
this._document = _document;
}
/**
* The date to display in this clock view.
*/
get activeDate() {
return this._activeDate;
}
set activeDate(value) {
const oldActiveDate = this._activeDate;
this._activeDate = this._adapter.clampDate(value, this.minDate, this.maxDate);
if (!this._adapter.sameMinute(oldActiveDate, this._activeDate)) {
this._init();
}
}
_activeDate;
/** The currently selected date. */
get selected() {
return this._selected;
}
set selected(value) {
this._selected = this._adapter.getValidDateOrNull(this._adapter.deserialize(value));
if (this._selected) {
this.activeDate = this._selected;
}
}
_selected;
/** The minimum selectable date. */
get minDate() {
return this._minDate;
}
set minDate(value) {
this._minDate = this._adapter.getValidDateOrNull(this._adapter.deserialize(value));
}
_minDate;
/** The maximum selectable date. */
get maxDate() {
return this._maxDate;
}
set maxDate(value) {
this._maxDate = this._adapter.getValidDateOrNull(this._adapter.deserialize(value));
}
_maxDate;
/** Whether the clock should be started in hour or minute view. */
set startView(value) {
this._hourView = value !== 'minute';
}
get _hand() {
const hour = this._adapter.getHour(this.activeDate);
this._selectedHour = hour;
this._selectedMinute = this._adapter.getMinute(this.activeDate);
let deg = 0;
let radius = CLOCK_OUTER_RADIUS;
if (this._hourView) {
const outer = this._selectedHour > 0 && this._selectedHour < 13;
radius = outer ? CLOCK_OUTER_RADIUS : CLOCK_INNER_RADIUS;
if (this.twelvehour) {
radius = CLOCK_OUTER_RADIUS;
}
deg = Math.round(this._selectedHour * (360 / (24 / 2)));
}
else {
deg = Math.round(this._selectedMinute * (360 / 60));
}
return {
height: `${radius}%`,
marginTop: `${50 - radius}%`,
transform: `rotate(${deg}deg)`
};
}
ngAfterContentInit() {
this.activeDate = this._activeDate || this._adapter.today();
this._init();
}
ngOnDestroy() {
this._removeGlobalEvents();
}
ngOnChanges() {
this._init();
}
/** Called when the user has put their pointer down on the clock. */
_pointerDown = (event) => {
this._timeChanged = false;
this.setTime(event);
this._bindGlobalEvents(event);
};
/**
* Called when the user has moved their pointer after
* starting to drag. Bound on the document level.
*/
_pointerMove = (event) => {
if (event.cancelable) {
event.preventDefault();
}
this.setTime(event);
};
/** Called when the user has lifted their pointer. Bound on the document level. */
_pointerUp = (event) => {
if (event.cancelable) {
event.preventDefault();
}
this._removeGlobalEvents();
if (this._timeChanged) {
this.selectedChange.emit(this.activeDate);
if (!this._hourView) {
this._userSelection.emit();
}
}
};
/** Binds our global move and end events. */
_bindGlobalEvents(triggerEvent) {
// Note that we bind the events to the `document`, because it allows us to capture
// drag cancel events where the user's pointer is outside the browser window.
const document = this._document;
const isTouch = isTouchEvent(triggerEvent);
const moveEventName = isTouch ? 'touchmove' : 'mousemove';
const endEventName = isTouch ? 'touchend' : 'mouseup';
document.addEventListener(moveEventName, this._pointerMove, activeEventOptions);
document.addEventListener(endEventName, this._pointerUp, activeEventOptions);
if (isTouch) {
document.addEventListener('touchcancel', this._pointerUp, activeEventOptions);
}
}
/** Removes any global event listeners that we may have added. */
_removeGlobalEvents() {
const document = this._document;
document.removeEventListener('mousemove', this._pointerMove, activeEventOptions);
document.removeEventListener('mouseup', this._pointerUp, activeEventOptions);
document.removeEventListener('touchmove', this._pointerMove, activeEventOptions);
document.removeEventListener('touchend', this._pointerUp, activeEventOptions);
document.removeEventListener('touchcancel', this._pointerUp, activeEventOptions);
}
/** Initializes this clock view. */
_init() {
this._hours.length = 0;
this._minutes.length = 0;
const hourNames = this._adapter.getHourNames();
const minuteNames = this._adapter.getMinuteNames();
if (this.twelvehour) {
const hours = [];
for (let i = 0; i < hourNames.length; i++) {
const radian = (i / 6) * Math.PI;
const radius = CLOCK_OUTER_RADIUS;
const hour = i;
const date = this._adapter.createDatetime(this._adapter.getYear(this.activeDate), this._adapter.getMonth(this.activeDate), this._adapter.getDate(this.activeDate), hour, 0);
// Check if the date is enabled, no need to respect the minute setting here
const enabled = (!this.minDate || this._adapter.compareDatetime(date, this.minDate, false) >= 0) &&
(!this.maxDate || this._adapter.compareDatetime(date, this.maxDate, false) <= 0) &&
(!this.dateFilter || this.dateFilter(date, AswDatetimepickerFilterType.HOUR));
// display value for twelvehour clock should be from 1-12 not including 0 and not above 12
hours.push({
value: i,
displayValue: i % 12 === 0 ? '12' : hourNames[i % 12],
enabled,
top: CLOCK_RADIUS - Math.cos(radian) * radius - CLOCK_TICK_RADIUS,
left: CLOCK_RADIUS + Math.sin(radian) * radius - CLOCK_TICK_RADIUS
});
}
// filter out AM or PM hours based on AMPM
if (this.AMPM === 'AM') {
this._hours = hours.filter((x) => x.value < 12);
}
else {
this._hours = hours.filter((x) => x.value >= 12);
}
}
else {
for (let i = 0; i < hourNames.length; i++) {
const radian = (i / 6) * Math.PI;
const outer = i > 0 && i < 13;
const radius = outer ? CLOCK_OUTER_RADIUS : CLOCK_INNER_RADIUS;
const date = this._adapter.createDatetime(this._adapter.getYear(this.activeDate), this._adapter.getMonth(this.activeDate), this._adapter.getDate(this.activeDate), i, 0);
// Check if the date is enabled, no need to respect the minute setting here
const enabled = (!this.minDate || this._adapter.compareDatetime(date, this.minDate, false) >= 0) &&
(!this.maxDate || this._adapter.compareDatetime(date, this.maxDate, false) <= 0) &&
(!this.dateFilter || this.dateFilter(date, AswDatetimepickerFilterType.HOUR));
this._hours.push({
value: i,
displayValue: i === 0 ? '00' : hourNames[i],
enabled,
top: CLOCK_RADIUS - Math.cos(radian) * radius - CLOCK_TICK_RADIUS,
left: CLOCK_RADIUS + Math.sin(radian) * radius - CLOCK_TICK_RADIUS,
fontSize: i > 0 && i < 13 ? '' : '80%'
});
}
}
for (let i = 0; i < minuteNames.length; i += 5) {
const radian = (i / 30) * Math.PI;
const date = this._adapter.createDatetime(this._adapter.getYear(this.activeDate), this._adapter.getMonth(this.activeDate), this._adapter.getDate(this.activeDate), this._adapter.getHour(this.activeDate), i);
const enabled = (!this.minDate || this._adapter.compareDatetime(date, this.minDate) >= 0) &&
(!this.maxDate || this._adapter.compareDatetime(date, this.maxDate) <= 0) &&
(!this.dateFilter || this.dateFilter(date, AswDatetimepickerFilterType.MINUTE));
this._minutes.push({
value: i,
displayValue: i === 0 ? '00' : minuteNames[i],
enabled,
top: CLOCK_RADIUS - Math.cos(radian) * CLOCK_OUTER_RADIUS - CLOCK_TICK_RADIUS,
left: CLOCK_RADIUS + Math.sin(radian) * CLOCK_OUTER_RADIUS - CLOCK_TICK_RADIUS
});
}
}
/**
* Set Time
* @param event
*/
setTime(event) {
const trigger = this._elementRef.nativeElement;
const triggerRect = trigger.getBoundingClientRect();
const width = trigger.offsetWidth;
const height = trigger.offsetHeight;
const { pageX, pageY } = getPointerPositionOnPage(event);
const x = width / 2 - (pageX - triggerRect.left - window.pageXOffset);
const y = height / 2 - (pageY - triggerRect.top - window.pageYOffset);
let radian = Math.atan2(-x, y);
const unit = Math.PI / (this._hourView ? 6 : this.interval ? 30 / this.interval : 30);
const z = Math.sqrt(x * x + y * y);
const outer = this._hourView && z > (width * (CLOCK_OUTER_RADIUS / 100) + width * (CLOCK_INNER_RADIUS / 100)) / 2;
if (radian < 0) {
radian = Math.PI * 2 + radian;
}
let value = Math.round(radian / unit);
let date;
if (this._hourView) {
if (this.twelvehour) {
if (this.AMPM === 'AM') {
value = value === 0 ? 12 : value;
}
else {
// if we chosen 12 in PM, the value should be 0 for 0:00,
// else we can safely add 12 to the final value
value = value === 12 ? 0 : value + 12;
}
}
else {
if (value === 12) {
value = 0;
}
value = outer ? (value === 0 ? 12 : value) : value === 0 ? 0 : value + 12;
}
date = this._adapter.createDatetime(this._adapter.getYear(this.activeDate), this._adapter.getMonth(this.activeDate), this._adapter.getDate(this.activeDate), value, this._adapter.getMinute(this.activeDate));
}
else {
if (this.interval) {
value *= this.interval;
}
if (value === 60) {
value = 0;
}
date = this._adapter.createDatetime(this._adapter.getYear(this.activeDate), this._adapter.getMonth(this.activeDate), this._adapter.getDate(this.activeDate), this._adapter.getHour(this.activeDate), value);
}
this._timeChanged = true;
this.activeDate = date;
this._changeDetectorRef.markForCheck();
this.activeDateChange.emit(this.activeDate);
}
static ngAcceptInputType_twelvehour;
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: AswClock, deps: [{ token: i0.ElementRef }, { token: i1.DatetimeAdapter }, { token: i0.ChangeDetectorRef }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.8", type: AswClock, selector: "asw-clock", inputs: { dateFilter: "dateFilter", interval: "interval", twelvehour: "twelvehour", AMPM: "AMPM", activeDate: "activeDate", selected: "selected", minDate: "minDate", maxDate: "maxDate", startView: "startView" }, outputs: { selectedChange: "selectedChange", activeDateChange: "activeDateChange", _userSelection: "_userSelection" }, host: { attributes: { "role": "clock" }, listeners: { "mousedown": "_pointerDown($event)", "touchstart": "_pointerDown($event)" }, classAttribute: "asw-clock" }, exportAs: ["aswClock"], usesOnChanges: true, ngImport: i0, template: "<div class=\"asw-clock-wrapper\">\r\n <div class=\"asw-clock-center\"></div>\r\n <div class=\"asw-clock-hand\" [ngStyle]=\"_hand\"></div>\r\n <div class=\"asw-clock-hours\" [class.active]=\"_hourView\">\r\n <div *ngFor=\"let item of _hours\"\r\n class=\"asw-clock-cell\"\r\n [class.asw-clock-cell-disabled]=\"!item.enabled\"\r\n [class.asw-clock-cell-selected]=\"_selectedHour === item.value\"\r\n [style.fontSize]=\"item.fontSize\"\r\n [style.left]=\"item.left+'%'\"\r\n [style.top]=\"item.top+'%'\">{{ item.displayValue }}\r\n </div>\r\n </div>\r\n <div class=\"asw-clock-minutes\" [class.active]=\"!_hourView\">\r\n <div *ngFor=\"let item of _minutes\"\r\n class=\"asw-clock-cell\"\r\n [class.asw-clock-cell-disabled]=\"!item.enabled\"\r\n [class.asw-clock-cell-selected]=\"_selectedMinute === item.value\"\r\n [style.left]=\"item.left+'%'\"\r\n [style.top]=\"item.top+'%'\">{{ item.displayValue }}\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".asw-clock{position:relative;display:block;min-width:224px;margin:12px;box-sizing:border-box;-webkit-user-select:none;user-select:none;touch-action:none}.asw-clock-wrapper{position:relative;width:100%;height:0;padding-top:100%;border-radius:50%}.asw-clock-center{position:absolute;top:50%;left:50%;width:2%;height:2%;margin:-1%;border-radius:50%}.asw-clock-hand{position:absolute;inset:0;width:1px;margin:0 auto;transform-origin:bottom}.asw-clock-hand:before{content:\"\";position:absolute;top:-4px;left:-4px;width:8px;height:8px;border-radius:50%}.asw-clock-hours,.asw-clock-minutes{position:absolute;top:0;left:0;width:100%;height:100%;opacity:0;visibility:hidden;transition:.35s;transform:scale(1.2)}.asw-clock-hours.active,.asw-clock-minutes.active{opacity:1;visibility:visible;transform:scale(1)}.asw-clock-minutes{transform:scale(.8)}.asw-clock-cell{position:absolute;display:flex;width:14.1666%;height:14.1666%;justify-content:center;box-sizing:border-box;border-radius:50%;align-items:center;cursor:pointer}.asw-clock-cell.asw-clock-cell-disabled{pointer-events:none}\n"], dependencies: [{ kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.8", ngImport: i0, type: AswClock, decorators: [{
type: Component,
args: [{ selector: 'asw-clock', host: {
role: 'clock',
class: 'asw-clock',
'(mousedown)': '_pointerDown($event)',
'(touchstart)': '_pointerDown($event)'
}, exportAs: 'aswClock', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"asw-clock-wrapper\">\r\n <div class=\"asw-clock-center\"></div>\r\n <div class=\"asw-clock-hand\" [ngStyle]=\"_hand\"></div>\r\n <div class=\"asw-clock-hours\" [class.active]=\"_hourView\">\r\n <div *ngFor=\"let item of _hours\"\r\n class=\"asw-clock-cell\"\r\n [class.asw-clock-cell-disabled]=\"!item.enabled\"\r\n [class.asw-clock-cell-selected]=\"_selectedHour === item.value\"\r\n [style.fontSize]=\"item.fontSize\"\r\n [style.left]=\"item.left+'%'\"\r\n [style.top]=\"item.top+'%'\">{{ item.displayValue }}\r\n </div>\r\n </div>\r\n <div class=\"asw-clock-minutes\" [class.active]=\"!_hourView\">\r\n <div *ngFor=\"let item of _minutes\"\r\n class=\"asw-clock-cell\"\r\n [class.asw-clock-cell-disabled]=\"!item.enabled\"\r\n [class.asw-clock-cell-selected]=\"_selectedMinute === item.value\"\r\n [style.left]=\"item.left+'%'\"\r\n [style.top]=\"item.top+'%'\">{{ item.displayValue }}\r\n </div>\r\n </div>\r\n</div>\r\n", styles: [".asw-clock{position:relative;display:block;min-width:224px;margin:12px;box-sizing:border-box;-webkit-user-select:none;user-select:none;touch-action:none}.asw-clock-wrapper{position:relative;width:100%;height:0;padding-top:100%;border-radius:50%}.asw-clock-center{position:absolute;top:50%;left:50%;width:2%;height:2%;margin:-1%;border-radius:50%}.asw-clock-hand{position:absolute;inset:0;width:1px;margin:0 auto;transform-origin:bottom}.asw-clock-hand:before{content:\"\";position:absolute;top:-4px;left:-4px;width:8px;height:8px;border-radius:50%}.asw-clock-hours,.asw-clock-minutes{position:absolute;top:0;left:0;width:100%;height:100%;opacity:0;visibility:hidden;transition:.35s;transform:scale(1.2)}.asw-clock-hours.active,.asw-clock-minutes.active{opacity:1;visibility:visible;transform:scale(1)}.asw-clock-minutes{transform:scale(.8)}.asw-clock-cell{position:absolute;display:flex;width:14.1666%;height:14.1666%;justify-content:center;box-sizing:border-box;border-radius:50%;align-items:center;cursor:pointer}.asw-clock-cell.asw-clock-cell-disabled{pointer-events:none}\n"] }]
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i1.DatetimeAdapter }, { type: i0.ChangeDetectorRef }, { type: undefined, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }], propDecorators: { dateFilter: [{
type: Input
}], interval: [{
type: Input
}], twelvehour: [{
type: Input
}], AMPM: [{
type: Input
}], selectedChange: [{
type: Output
}], activeDateChange: [{
type: Output
}], _userSelection: [{
type: Output
}], activeDate: [{
type: Input
}], selected: [{
type: Input
}], minDate: [{
type: Input
}], maxDate: [{
type: Input
}], startView: [{
type: Input
}] } });
/** Returns whether an event is a touch event. */
function isTouchEvent(event) {
// This function is called for every pixel that the user has dragged so we need it to be
// as fast as possible. Since we only bind mouse events and touch events, we can assume
// that if the event's name starts with `t`, it's a touch event.
return event.type[0] === 't';
}
/** Gets the coordinates of a touch or mouse event relative to the document. */
function getPointerPositionOnPage(event) {
let point;
if (isTouchEvent(event)) {
// `touches` will be empty for start/end events so we have to fall back to `changedTouches`.
point = event.touches[0] || event.changedTouches[0];
}
else {
point = event;
}
return point;
}
function pad(num, size) {
num = String(num);
while (num.length < size)
num = '0' + num;
return num;
}
class AswTimeInput {
element;
cdr;
set timeInterval(value) {
this._interval = coerceNumberProperty(value);
}
_interval = 1;
set timeMin(value) {
this._min = coerceNumberProperty(value);
}
_min = 0;
set timeMax(value) {
this._max = coerceNumberProperty(value);
}
_max = Infinity;
set timeValue(value) {
this._value = coerceNumberProperty(value);
if (!this.hasFocus) {
this.writeValue(this._value);
}
this.writePlaceholder(this._value);
}
timeValueChanged = new EventEmitter();
_value;
keyDownListener = this.keyDownHandler.bind(this);
keyPressListener = this.keyPressHandler.bind(this);
inputEventListener = this.inputChangedHandler.bind(this);
constructor(element, cdr) {
this.element = element;
this.cdr = cdr;
this.inputElement.addEventListener('keydown', this.keyDownListener, {
passive: true
});
// Do not passive since we want to be able to preventDefault()
this.inputElement.addEventListener('keypress', this.keyPressListener);
this.inputElement.addEventListener('input', this.inputEventListener, {
passive: true
});
}
get hasFocus() {
return this.element.nativeElement && this.element?.nativeElement === document?.activeElement;
}
get inputElement() {
return this.element.nativeElement;
}
// We look here at the placeholder value, because we write '' into the value on focus
// placeholder should always be up to date with "currentValue"
get valid() {
// At the start _value is undefined therefore this would result in not valid and
// make a ugly warning border afterwards we can safely check
if (this._value) {
const currentValue = String(this.inputElement.value);
// It can be that currentValue is empty due to we removing the value on focus,
// if that is the case we should check previous value which should be