UNPKG

@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

1 lines 252 kB
{"version":3,"file":"asoftwareworld-form-builder-pro-datetimepicker.mjs","sources":["../../src/components/datetimepicker/datetimepicker-animations.ts","../../src/components/datetimepicker/datetimepicker-errors.ts","../../src/components/datetimepicker/datetimepicker-filtertype.ts","../../src/components/datetimepicker/calendar-body/calendar-body.ts","../../src/components/datetimepicker/calendar-body/calendar-body.html","../../src/components/datetimepicker/multi-year-view/multi-year-view.ts","../../src/components/datetimepicker/multi-year-view/multi-year-view.html","../../src/components/datetimepicker/datetimepicker-intl.ts","../../src/components/datetimepicker/clock/clock.ts","../../src/components/datetimepicker/clock/clock.html","../../src/components/datetimepicker/time/time.ts","../../src/components/datetimepicker/time/time.html","../../src/components/datetimepicker/month-view/month-view.ts","../../src/components/datetimepicker/month-view/month-view.html","../../src/components/datetimepicker/year-view/year-view.ts","../../src/components/datetimepicker/year-view/year-view.html","../../src/components/datetimepicker/calendar/calendar.ts","../../src/components/datetimepicker/calendar/calendar.html","../../src/components/datetimepicker/datetimepicker.ts","../../src/components/datetimepicker/datetimepicker-content.html","../../src/components/datetimepicker/datetimepicker-input.ts","../../src/components/datetimepicker/datetimepicker-toggle/datetimepicker-toggle.ts","../../src/components/datetimepicker/datetimepicker-toggle/datetimepicker-toggle.html","../../src/components/datetimepicker/datetimepicker-module.ts","../../src/components/datetimepicker/asoftwareworld-form-builder-pro-datetimepicker.ts"],"sourcesContent":["import { animate, AnimationTriggerMetadata, keyframes, state, style, transition, trigger } from '@angular/animations';\n\n/**\n * Animations used by the Material datepicker.\n * @docs-private\n */\nexport const aswDatetimepickerAnimations: {\n readonly transformPanel: AnimationTriggerMetadata;\n readonly fadeInCalendar: AnimationTriggerMetadata;\n readonly slideCalendar: AnimationTriggerMetadata;\n} = {\n /** Transforms the height of the datepicker's calendar. */\n transformPanel: trigger('transformPanel', [\n 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)' })]))),\n 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 })]))),\n transition('* => void', animate('100ms linear', style({ opacity: 0 })))\n ]),\n\n /** Fades in the content of the calendar. */\n fadeInCalendar: trigger('fadeInCalendar', [\n state('void', style({ opacity: 0 })),\n state('enter', style({ opacity: 1 })),\n\n // TODO(crisbeto): this animation should be removed since it isn't quite on spec, but we\n // need to keep it until #12440 gets in, otherwise the exit animation will look glitchy.\n transition('void => *', animate('120ms 100ms cubic-bezier(0.55, 0, 0.55, 0.2)'))\n ]),\n\n slideCalendar: trigger('slideCalendar', [\n 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 })]))]),\n 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 })]))])\n ])\n};\n","/** @docs-private */\nexport function createMissingDateImplError(provider: string) {\n 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.`);\n}\n","export enum AswDatetimepickerFilterType {\n DATE,\n HOUR,\n MINUTE\n}\n","import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';\n\n/**\n * An internal class that represents the data corresponding to a single calendar cell.\n * @docs-private\n */\nexport class AswCalendarCell {\n constructor(\n public value: number,\n public displayValue: string,\n public ariaLabel: string,\n public enabled: boolean\n ) {}\n}\n\n/**\n * An internal component used to display calendar data in a table.\n * @docs-private\n */\n@Component({\n selector: '[asw-calendar-body]',\n templateUrl: 'calendar-body.html',\n styleUrls: ['calendar-body.scss'],\n host: {\n class: 'asw-calendar-body'\n },\n exportAs: 'aswCalendarBody',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class AswCalendarBody {\n /** The label for the table. (e.g. \"Jan 2017\"). */\n @Input() label!: string;\n\n /** The cells to display in the table. */\n @Input() rows!: AswCalendarCell[][];\n\n /** The value in the table that corresponds to today. */\n @Input() todayValue!: number;\n\n /** The value in the table that is currently selected. */\n @Input() selectedValue!: number;\n\n /** The minimum number of free cells needed to fit the label in the first row. */\n @Input() labelMinRequiredCells!: number;\n\n /** The number of columns in the table. */\n @Input() numCols = 7;\n\n /** Whether to allow selection of disabled cells. */\n @Input() allowDisabledSelection = false;\n\n /** The cell number of the active cell in the table. */\n @Input() activeCell = 0;\n\n /** Emits when a new value is selected. */\n @Output() selectedValueChange = new EventEmitter<number>();\n\n /** The number of blank cells to put at the beginning for the first row. */\n get _firstRowOffset(): number {\n return this.rows && this.rows.length && this.rows[0].length ? this.numCols - this.rows[0].length : 0;\n }\n\n _cellClicked(cell: AswCalendarCell): void {\n if (!this.allowDisabledSelection && !cell.enabled) {\n return;\n }\n this.selectedValueChange.emit(cell.value);\n }\n\n _isActiveCell(rowIndex: number, colIndex: number): boolean {\n let cellNumber = rowIndex * this.numCols + colIndex;\n\n // Account for the fact that the first row may not have as many cells.\n if (rowIndex) {\n cellNumber -= this._firstRowOffset;\n }\n\n return cellNumber === this.activeCell;\n }\n}\n","<!--\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","import { AfterContentInit, ChangeDetectionStrategy, Component, EventEmitter, Inject, Input, Optional, Output, ViewEncapsulation } from '@angular/core';\nimport { createMissingDateImplError } from '../datetimepicker-errors';\nimport { AswCalendarCell } from '../calendar-body/calendar-body';\nimport { aswDatetimepickerAnimations } from '../datetimepicker-animations';\nimport { ASW_DATETIME_FORMATS, AswDatetimeFormats, DatetimeAdapter } from '@asoftwareworld/form-builder-pro/common';\nimport { AswDatetimepickerType } from '../datetimepicker-types';\n\nexport const yearsPerPage = 24;\n\nexport const yearsPerRow = 4;\n\n/**\n * An internal component used to display multiple years in the datetimepicker.\n * @docs-private\n */\n@Component({\n selector: 'asw-multi-year-view',\n templateUrl: 'multi-year-view.html',\n exportAs: 'aswMultiYearView',\n animations: [aswDatetimepickerAnimations.slideCalendar],\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class AswMultiYearView<D> implements AfterContentInit {\n @Input() type: AswDatetimepickerType = 'date';\n\n /** A function used to filter which dates are selectable. */\n @Input() dateFilter!: (date: D) => boolean;\n\n /** Emits when a new month is selected. */\n @Output() selectedChange = new EventEmitter<D>();\n\n /** Emits when any date is selected. */\n @Output() readonly _userSelection = new EventEmitter<void>();\n\n /** Grid of calendar cells representing the years in the range. */\n _years!: AswCalendarCell[][];\n\n /** The label for this year range (e.g. \"2000-2020\"). */\n _yearLabel!: string;\n\n /** The year in this range that today falls on. Null if today is in a different range. */\n _todayYear!: number;\n\n /**\n * The year in this range that the selected Date falls on.\n * Null if the selected Date is in a different range.\n */\n _selectedYear!: number | null;\n\n _calendarState!: string;\n\n constructor(\n @Optional() public _adapter: DatetimeAdapter<D>,\n @Optional() @Inject(ASW_DATETIME_FORMATS) private _dateFormats: AswDatetimeFormats\n ) {\n if (!this._adapter) {\n throw createMissingDateImplError('DatetimeAdapter');\n }\n\n if (!this._dateFormats) {\n throw createMissingDateImplError('ASW_DATETIME_FORMATS');\n }\n\n this._activeDate = this._adapter.today();\n }\n\n /** The date to display in this multi year view */\n @Input()\n get activeDate(): D {\n return this._activeDate;\n }\n set activeDate(value: D) {\n const oldActiveDate = this._activeDate;\n this._activeDate = value || this._adapter.today();\n if (oldActiveDate && this._activeDate && !isSameMultiYearView(this._adapter, oldActiveDate, this._activeDate, this.minDate, this.maxDate)) {\n this._init();\n }\n }\n private _activeDate: D;\n\n /** The currently selected date. */\n @Input()\n get selected(): D {\n return this._selected;\n }\n set selected(value: D) {\n this._selected = value;\n this._selectedYear = this._selected && this._adapter.getYear(this._selected);\n }\n private _selected!: D;\n\n /** The minimum selectable date. */\n @Input()\n get minDate(): D | null {\n return this._minDate;\n }\n set minDate(value: D | null) {\n this._minDate = this._getValidDateOrNull(this._adapter.deserialize(value));\n }\n private _minDate!: D | null;\n\n /** The maximum selectable date. */\n @Input()\n get maxDate(): D | null {\n return this._maxDate;\n }\n set maxDate(value: D | null) {\n this._maxDate = this._getValidDateOrNull(this._adapter.deserialize(value));\n }\n private _maxDate!: D | null;\n\n ngAfterContentInit() {\n this._init();\n }\n\n /** Handles when a new year is selected. */\n _yearSelected(year: number) {\n const month = this._adapter.getMonth(this.activeDate);\n const normalizedDate = this._adapter.createDatetime(year, month, 1, 0, 0);\n\n this.selectedChange.emit(\n 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))\n );\n\n if (this.type === 'year') {\n this._userSelection.emit();\n }\n }\n\n _getActiveCell(): number {\n return getActiveOffset(this._adapter, this.activeDate, this.minDate, this.maxDate);\n }\n\n _calendarStateDone() {\n this._calendarState = '';\n }\n\n /** Initializes this year view. */\n private _init() {\n this._todayYear = this._adapter.getYear(this._adapter.today());\n this._yearLabel = this._adapter.getYearName(this.activeDate);\n\n const activeYear = this._adapter.getYear(this.activeDate);\n\n const minYearOfPage = activeYear - getActiveOffset(this._adapter, this.activeDate, this.minDate, this.maxDate);\n\n this._years = [];\n for (let i = 0, row: number[] = []; i < yearsPerPage; i++) {\n row.push(minYearOfPage + i);\n if (row.length === yearsPerRow) {\n this._years.push(row.map((year) => this._createCellForYear(year)));\n row = [];\n }\n }\n }\n\n /** Creates an AswCalendarCell for the given year. */\n private _createCellForYear(year: number) {\n const yearName = this._adapter.getYearName(this._adapter.createDate(year, 0, 1));\n return new AswCalendarCell(year, yearName, yearName, this._shouldEnableYear(year));\n }\n\n /** Whether the given year is enabled. */\n private _shouldEnableYear(year: number) {\n // disable if the year is greater than maxDate lower than minDate\n if (year === undefined || year === null || (this.maxDate && year > this._adapter.getYear(this.maxDate)) || (this.minDate && year < this._adapter.getYear(this.minDate))) {\n return false;\n }\n\n // enable if it reaches here and there's no filter defined\n if (!this.dateFilter) {\n return true;\n }\n\n const firstOfYear = this._adapter.createDate(year, 0, 1);\n\n // If any date in the year is enabled count the year as enabled.\n for (let date = firstOfYear; this._adapter.getYear(date) === year; date = this._adapter.addCalendarDays(date, 1)) {\n if (this.dateFilter(date)) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Gets the year in this years range that the given Date falls on.\n * Returns null if the given Date is not in this range.\n */\n private _getYearInCurrentRange(date: D) {\n const year = this._adapter.getYear(date);\n return this._isInRange(year) ? year : null;\n }\n\n /**\n * Validate if the current year is in the current range\n * Returns true if is in range else returns false\n */\n private _isInRange(year: number): boolean {\n return true;\n }\n\n /**\n * @param obj The object to check.\n * @returns The given object if it is both a date instance and valid, otherwise null.\n */\n private _getValidDateOrNull(obj: any): D | null {\n return this._adapter.isDateInstance(obj) && this._adapter.isValid(obj) ? obj : null;\n }\n}\n\nexport function isSameMultiYearView<D>(dateAdapter: DatetimeAdapter<D>, date1: D, date2: D, minDate: D | null, maxDate: D | null): boolean {\n const year1 = dateAdapter.getYear(date1);\n const year2 = dateAdapter.getYear(date2);\n const startingYear = getStartingYear(dateAdapter, minDate, maxDate);\n return Math.floor((year1 - startingYear) / yearsPerPage) === Math.floor((year2 - startingYear) / yearsPerPage);\n}\n\n/**\n * When the multi-year view is first opened, the active year will be in view.\n * So we compute how many years are between the active year and the *slot* where our\n * \"startingYear\" will render when paged into view.\n */\nexport function getActiveOffset<D>(dateAdapter: DatetimeAdapter<D>, activeDate: D, minDate: D | null, maxDate: D | null): number {\n const activeYear = dateAdapter.getYear(activeDate);\n return euclideanModulo(activeYear - getStartingYear(dateAdapter, minDate, maxDate), yearsPerPage);\n}\n\n/**\n * We pick a \"starting\" year such that either the maximum year would be at the end\n * or the minimum year would be at the beginning of a page.\n */\nfunction getStartingYear<D>(dateAdapter: DatetimeAdapter<D>, minDate: D | null, maxDate: D | null): number {\n let startingYear = 0;\n if (maxDate) {\n const maxYear = dateAdapter.getYear(maxDate);\n startingYear = maxYear - yearsPerPage + 1;\n } else if (minDate) {\n startingYear = dateAdapter.getYear(minDate);\n }\n return startingYear;\n}\n\n/** Gets remainder that is non-negative, even if first number is negative */\nfunction euclideanModulo(a: number, b: number): number {\n return ((a % b) + b) % b;\n}\n","<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","import { Injectable } from '@angular/core';\nimport { Subject } from 'rxjs';\n\n@Injectable({ providedIn: 'root' })\nexport class AswDatetimepickerIntl {\n /**\n * Stream to emit from when labels are changed. Use this to notify components when the labels have\n * changed after initialization.\n */\n readonly changes = new Subject<void>();\n\n /** A label for the calendar popup (used by screen readers). */\n calendarLabel = 'Calendar';\n\n /** A label for the button used to open the calendar popup (used by screen readers). */\n openCalendarLabel = 'Open calendar';\n\n /** Label for the button used to close the calendar popup. */\n closeCalendarLabel = 'Close calendar';\n\n /** A label for the previous month button (used by screen readers). */\n prevMonthLabel = 'Previous month';\n\n /** A label for the next month button (used by screen readers). */\n nextMonthLabel = 'Next month';\n\n /** A label for the previous year button (used by screen readers). */\n prevYearLabel = 'Previous year';\n\n /** A label for the next year button (used by screen readers). */\n nextYearLabel = 'Next year';\n\n /** A label for the previous multi-year button (used by screen readers). */\n prevMultiYearLabel = 'Previous 24 years';\n\n /** A label for the next multi-year button (used by screen readers). */\n nextMultiYearLabel = 'Next 24 years';\n\n /** A label for the 'switch to month view' button (used by screen readers). */\n switchToMonthViewLabel = 'Choose date';\n\n /** A label for the 'switch to year view' button (used by screen readers). */\n switchToYearViewLabel = 'Choose month';\n\n /** A label for the 'switch to multi-year view' button (used by screen readers). */\n switchToMultiYearViewLabel = 'Choose month and year';\n\n /** A label for the first date of a range of dates (used by screen readers). */\n startDateLabel = 'Start date';\n\n /** A label for the last date of a range of dates (used by screen readers). */\n endDateLabel = 'End date';\n\n /** Formats a range of years (used for visuals). */\n formatYearRange(start: string, end: string): string {\n return `${start} \\u2013 ${end}`;\n }\n\n /** Formats a label for a range of years (used by screen readers). */\n formatYearRangeLabel(start: string, end: string): string {\n return `${start} to ${end}`;\n }\n\n /** A label for the 'switch to clock hour view' button (used by screen readers). */\n switchToClockHourViewLabel = 'Choose hour';\n\n /** A label for the 'switch to clock minute view' button (used by screen readers). */\n switchToClockMinuteViewLabel = 'Choose minute';\n\n /** Label used for ok button within the manual time input. */\n okLabel = 'OK';\n\n /** Label used for cancel button within the manual time input. */\n cancelLabel = 'Cancel';\n}\n","import { BooleanInput } from '@angular/cdk/coercion';\nimport { normalizePassiveListenerOptions } from '@angular/cdk/platform';\nimport { DOCUMENT } from '@angular/common';\nimport { AfterContentInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, Inject, Input, OnChanges, OnDestroy, Output, SimpleChanges, ViewEncapsulation } from '@angular/core';\nimport { DatetimeAdapter } from '@asoftwareworld/form-builder-pro/common';\nimport { AswDatetimepickerFilterType } from '../datetimepicker-filtertype';\nimport { AswAMPM } from '../datetimepicker-types';\n\nconst activeEventOptions = normalizePassiveListenerOptions({ passive: false });\n\nexport const CLOCK_RADIUS = 50;\nexport const CLOCK_INNER_RADIUS = 27.5;\nexport const CLOCK_OUTER_RADIUS = 41.25;\nexport const CLOCK_TICK_RADIUS = 7.0833;\n\n/** Possible views for datetimepicker clock. */\nexport type AswClockView = 'hour' | 'minute';\n\n/**\n * A clock that is used as part of the datetimepicker.\n * @docs-private\n */\n@Component({\n selector: 'asw-clock',\n templateUrl: 'clock.html',\n styleUrls: ['clock.scss'],\n host: {\n role: 'clock',\n class: 'asw-clock',\n '(mousedown)': '_pointerDown($event)',\n '(touchstart)': '_pointerDown($event)'\n },\n exportAs: 'aswClock',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class AswClock<D> implements AfterContentInit, OnDestroy, OnChanges {\n /** A function used to filter which dates are selectable. */\n @Input() dateFilter!: (date: D, type: AswDatetimepickerFilterType) => boolean;\n\n /** Step over minutes. */\n @Input() interval: number = 1;\n\n /** Whether the clock uses 12 hour format. */\n @Input() twelvehour: boolean = false;\n\n /** Whether the time is now in AM or PM. */\n @Input() AMPM: AswAMPM = 'AM';\n\n /** Emits when the currently selected date changes. */\n @Output() selectedChange = new EventEmitter<D>();\n\n /** Emits when any date is activated. */\n @Output() activeDateChange = new EventEmitter<D>();\n\n /** Emits when any date is selected. */\n @Output() readonly _userSelection = new EventEmitter<void>();\n\n /** Whether the clock is in hour view. */\n _hourView: boolean = true;\n\n _hours: any[] = [];\n\n _minutes: any[] = [];\n\n _selectedHour!: number;\n\n _selectedMinute!: number;\n\n private _timeChanged = false;\n\n constructor(\n private _elementRef: ElementRef,\n private _adapter: DatetimeAdapter<D>,\n private _changeDetectorRef: ChangeDetectorRef,\n @Inject(DOCUMENT) private _document: any\n ) {}\n\n /**\n * The date to display in this clock view.\n */\n @Input()\n get activeDate(): D {\n return this._activeDate;\n }\n set activeDate(value: D) {\n const oldActiveDate = this._activeDate;\n this._activeDate = this._adapter.clampDate(value, this.minDate, this.maxDate);\n if (!this._adapter.sameMinute(oldActiveDate, this._activeDate)) {\n this._init();\n }\n }\n private _activeDate!: D;\n\n /** The currently selected date. */\n @Input()\n get selected(): D | null {\n return this._selected;\n }\n set selected(value: D | null) {\n this._selected = this._adapter.getValidDateOrNull(this._adapter.deserialize(value));\n if (this._selected) {\n this.activeDate = this._selected;\n }\n }\n private _selected!: D | null;\n\n /** The minimum selectable date. */\n @Input()\n get minDate(): D | null {\n return this._minDate;\n }\n set minDate(value: D | null) {\n this._minDate = this._adapter.getValidDateOrNull(this._adapter.deserialize(value));\n }\n private _minDate!: D | null;\n\n /** The maximum selectable date. */\n @Input()\n get maxDate(): D | null {\n return this._maxDate;\n }\n set maxDate(value: D | null) {\n this._maxDate = this._adapter.getValidDateOrNull(this._adapter.deserialize(value));\n }\n private _maxDate!: D | null;\n\n /** Whether the clock should be started in hour or minute view. */\n @Input()\n set startView(value: AswClockView) {\n this._hourView = value !== 'minute';\n }\n\n get _hand() {\n const hour = this._adapter.getHour(this.activeDate);\n this._selectedHour = hour;\n this._selectedMinute = this._adapter.getMinute(this.activeDate);\n let deg = 0;\n let radius = CLOCK_OUTER_RADIUS;\n if (this._hourView) {\n const outer = this._selectedHour > 0 && this._selectedHour < 13;\n radius = outer ? CLOCK_OUTER_RADIUS : CLOCK_INNER_RADIUS;\n if (this.twelvehour) {\n radius = CLOCK_OUTER_RADIUS;\n }\n deg = Math.round(this._selectedHour * (360 / (24 / 2)));\n } else {\n deg = Math.round(this._selectedMinute * (360 / 60));\n }\n return {\n height: `${radius}%`,\n marginTop: `${50 - radius}%`,\n transform: `rotate(${deg}deg)`\n };\n }\n\n ngAfterContentInit() {\n this.activeDate = this._activeDate || this._adapter.today();\n this._init();\n }\n\n ngOnDestroy() {\n this._removeGlobalEvents();\n }\n\n ngOnChanges(): void {\n this._init();\n }\n\n /** Called when the user has put their pointer down on the clock. */\n private _pointerDown = (event: TouchEvent | MouseEvent) => {\n this._timeChanged = false;\n this.setTime(event);\n this._bindGlobalEvents(event);\n };\n\n /**\n * Called when the user has moved their pointer after\n * starting to drag. Bound on the document level.\n */\n private _pointerMove = (event: TouchEvent | MouseEvent) => {\n if (event.cancelable) {\n event.preventDefault();\n }\n this.setTime(event);\n };\n\n /** Called when the user has lifted their pointer. Bound on the document level. */\n private _pointerUp = (event: TouchEvent | MouseEvent) => {\n if (event.cancelable) {\n event.preventDefault();\n }\n this._removeGlobalEvents();\n\n if (this._timeChanged) {\n this.selectedChange.emit(this.activeDate);\n if (!this._hourView) {\n this._userSelection.emit();\n }\n }\n };\n\n /** Binds our global move and end events. */\n private _bindGlobalEvents(triggerEvent: TouchEvent | MouseEvent) {\n // Note that we bind the events to the `document`, because it allows us to capture\n // drag cancel events where the user's pointer is outside the browser window.\n const document = this._document;\n const isTouch = isTouchEvent(triggerEvent);\n const moveEventName = isTouch ? 'touchmove' : 'mousemove';\n const endEventName = isTouch ? 'touchend' : 'mouseup';\n document.addEventListener(moveEventName, this._pointerMove, activeEventOptions);\n document.addEventListener(endEventName, this._pointerUp, activeEventOptions);\n\n if (isTouch) {\n document.addEventListener('touchcancel', this._pointerUp, activeEventOptions);\n }\n }\n\n /** Removes any global event listeners that we may have added. */\n private _removeGlobalEvents() {\n const document = this._document;\n document.removeEventListener('mousemove', this._pointerMove, activeEventOptions);\n document.removeEventListener('mouseup', this._pointerUp, activeEventOptions);\n document.removeEventListener('touchmove', this._pointerMove, activeEventOptions);\n document.removeEventListener('touchend', this._pointerUp, activeEventOptions);\n document.removeEventListener('touchcancel', this._pointerUp, activeEventOptions);\n }\n\n /** Initializes this clock view. */\n private _init() {\n this._hours.length = 0;\n this._minutes.length = 0;\n\n const hourNames = this._adapter.getHourNames();\n const minuteNames = this._adapter.getMinuteNames();\n if (this.twelvehour) {\n const hours = [];\n for (let i = 0; i < hourNames.length; i++) {\n const radian = (i / 6) * Math.PI;\n const radius = CLOCK_OUTER_RADIUS;\n\n const hour = i;\n const date = this._adapter.createDatetime(this._adapter.getYear(this.activeDate), this._adapter.getMonth(this.activeDate), this._adapter.getDate(this.activeDate), hour, 0);\n\n // Check if the date is enabled, no need to respect the minute setting here\n const enabled =\n (!this.minDate || (this._adapter.compareDatetime(date, this.minDate, false) as number) >= 0) &&\n (!this.maxDate || (this._adapter.compareDatetime(date, this.maxDate, false) as number) <= 0) &&\n (!this.dateFilter || this.dateFilter(date, AswDatetimepickerFilterType.HOUR));\n\n // display value for twelvehour clock should be from 1-12 not including 0 and not above 12\n hours.push({\n value: i,\n displayValue: i % 12 === 0 ? '12' : hourNames[i % 12],\n enabled,\n top: CLOCK_RADIUS - Math.cos(radian) * radius - CLOCK_TICK_RADIUS,\n left: CLOCK_RADIUS + Math.sin(radian) * radius - CLOCK_TICK_RADIUS\n });\n }\n\n // filter out AM or PM hours based on AMPM\n if (this.AMPM === 'AM') {\n this._hours = hours.filter((x) => x.value < 12);\n } else {\n this._hours = hours.filter((x) => x.value >= 12);\n }\n } else {\n for (let i = 0; i < hourNames.length; i++) {\n const radian = (i / 6) * Math.PI;\n const outer = i > 0 && i < 13;\n const radius = outer ? CLOCK_OUTER_RADIUS : CLOCK_INNER_RADIUS;\n const date = this._adapter.createDatetime(this._adapter.getYear(this.activeDate), this._adapter.getMonth(this.activeDate), this._adapter.getDate(this.activeDate), i, 0);\n\n // Check if the date is enabled, no need to respect the minute setting here\n const enabled =\n (!this.minDate || (this._adapter.compareDatetime(date, this.minDate, false) as number) >= 0) &&\n (!this.maxDate || (this._adapter.compareDatetime(date, this.maxDate, false) as number) <= 0) &&\n (!this.dateFilter || this.dateFilter(date, AswDatetimepickerFilterType.HOUR));\n\n this._hours.push({\n value: i,\n displayValue: i === 0 ? '00' : hourNames[i],\n enabled,\n top: CLOCK_RADIUS - Math.cos(radian) * radius - CLOCK_TICK_RADIUS,\n left: CLOCK_RADIUS + Math.sin(radian) * radius - CLOCK_TICK_RADIUS,\n fontSize: i > 0 && i < 13 ? '' : '80%'\n });\n }\n }\n\n for (let i = 0; i < minuteNames.length; i += 5) {\n const radian = (i / 30) * Math.PI;\n 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);\n const enabled =\n (!this.minDate || (this._adapter.compareDatetime(date, this.minDate) as number) >= 0) &&\n (!this.maxDate || (this._adapter.compareDatetime(date, this.maxDate) as number) <= 0) &&\n (!this.dateFilter || this.dateFilter(date, AswDatetimepickerFilterType.MINUTE));\n this._minutes.push({\n value: i,\n displayValue: i === 0 ? '00' : minuteNames[i],\n enabled,\n top: CLOCK_RADIUS - Math.cos(radian) * CLOCK_OUTER_RADIUS - CLOCK_TICK_RADIUS,\n left: CLOCK_RADIUS + Math.sin(radian) * CLOCK_OUTER_RADIUS - CLOCK_TICK_RADIUS\n });\n }\n }\n\n /**\n * Set Time\n * @param event\n */\n private setTime(event: TouchEvent | MouseEvent) {\n const trigger = this._elementRef.nativeElement;\n const triggerRect = trigger.getBoundingClientRect();\n const width = trigger.offsetWidth;\n const height = trigger.offsetHeight;\n const { pageX, pageY } = getPointerPositionOnPage(event);\n const x = width / 2 - (pageX - triggerRect.left - window.pageXOffset);\n const y = height / 2 - (pageY - triggerRect.top - window.pageYOffset);\n\n let radian = Math.atan2(-x, y);\n const unit = Math.PI / (this._hourView ? 6 : this.interval ? 30 / this.interval : 30);\n const z = Math.sqrt(x * x + y * y);\n const outer = this._hourView && z > (width * (CLOCK_OUTER_RADIUS / 100) + width * (CLOCK_INNER_RADIUS / 100)) / 2;\n\n if (radian < 0) {\n radian = Math.PI * 2 + radian;\n }\n let value = Math.round(radian / unit);\n\n let date;\n if (this._hourView) {\n if (this.twelvehour) {\n if (this.AMPM === 'AM') {\n value = value === 0 ? 12 : value;\n } else {\n // if we chosen 12 in PM, the value should be 0 for 0:00,\n // else we can safely add 12 to the final value\n value = value === 12 ? 0 : value + 12;\n }\n } else {\n if (value === 12) {\n value = 0;\n }\n value = outer ? (value === 0 ? 12 : value) : value === 0 ? 0 : value + 12;\n }\n\n 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));\n } else {\n if (this.interval) {\n value *= this.interval;\n }\n if (value === 60) {\n value = 0;\n }\n 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);\n }\n\n this._timeChanged = true;\n this.activeDate = date;\n this._changeDetectorRef.markForCheck();\n this.activeDateChange.emit(this.activeDate);\n }\n\n static ngAcceptInputType_twelvehour: BooleanInput;\n}\n\n/** Returns whether an event is a touch event. */\nfunction isTouchEvent(event: MouseEvent | TouchEvent): event is TouchEvent {\n // This function is called for every pixel that the user has dragged so we need it to be\n // as fast as possible. Since we only bind mouse events and touch events, we can assume\n // that if the event's name starts with `t`, it's a touch event.\n return event.type[0] === 't';\n}\n\n/** Gets the coordinates of a touch or mouse event relative to the document. */\nfunction getPointerPositionOnPage(event: MouseEvent | TouchEvent) {\n let point: { pageX: number; pageY: number };\n\n if (isTouchEvent(event)) {\n // `touches` will be empty for start/end events so we have to fall back to `changedTouches`.\n point = event.touches[0] || event.changedTouches[0];\n } else {\n point = event;\n }\n\n return point;\n}\n","<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","import { BooleanInput, coerceBooleanProperty, coerceNumberProperty, NumberInput } from '@angular/cdk/coercion';\nimport { DOWN_ARROW, UP_ARROW } from '@angular/cdk/keycodes';\nimport { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, Directive, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, Output, SimpleChanges, ViewChild, ViewEncapsulation } from '@angular/core';\nimport { DatetimeAdapter } from '@asoftwareworld/form-builder-pro/common';\nimport { SubscriptionLike } from 'rxjs';\nimport { AswClockView } from '../clock/clock';\nimport { AswDatetimepickerFilterType } from '../datetimepicker-filtertype';\nimport { AswDatetimepickerIntl } from '../datetimepicker-intl';\nimport { AswAMPM } from '../datetimepicker-types';\n\nfunction pad(num: NumberInput, size: number) {\n num = String(num);\n while (num.length < size) num = '0' + num;\n return num;\n}\n\n@Directive({\n selector: 'input.asw-time-input',\n host: {\n '(blur)': 'blur($event)',\n '(focus)': 'focus($event)'\n },\n exportAs: 'aswTimeInput'\n})\nexport class AswTimeInput implements OnDestroy {\n @Input('timeInterval')\n set timeInterval(value: NumberInput) {\n this._interval = coerceNumberProperty(value);\n }\n private _interval: number = 1;\n\n @Input('timeMin')\n set timeMin(value: NumberInput) {\n this._min = coerceNumberProperty(value);\n }\n private _min = 0;\n\n @Input('timeMax')\n set timeMax(value: NumberInput) {\n this._max = coerceNumberProperty(value);\n }\n private _max = Infinity;\n\n @Input('timeValue')\n set timeValue(value: NumberInput) {\n this._value = coerceNumberProperty(value);\n if (!this.hasFocus) {\n this.writeValue(this._value);\n }\n this.writePlaceholder(this._value);\n }\n\n @Output() timeValueChanged = new EventEmitter<NumberInput>();\n\n private _value: NumberInput;\n\n private keyDownListener = this.keyDownHandler.bind(this);\n private keyPressListener = this.keyPressHandler.bind(this);\n private inputEventListener = this.inputChangedHandler.bind(this);\n\n constructor(\n private element: ElementRef,\n private cdr: ChangeDetectorRef\n ) {\n this.inputElement.addEventListener('keydown', this.keyDownListener, {\n passive: true\n });\n\n // Do not passive since we want to be able to preventDefault()\n this.inputElement.addEventListener('keypress', this.keyPressListener);\n this.inputElement.addEventListener('input', this.inputEventListener, {\n passive: true\n });\n }\n\n get hasFocus() {\n return this.element.nativeElement && this.element?.nativeElement === document?.activeElement;\n }\n\n get inputElement() {\n return this.element.nativeElement as HTMLInputElement;\n }\n\n // We look here at the placeholder value, because we write '' into the value on focus\n // placeholder should always be up to date with \"currentValue\"\n get valid() {\n // At the start _value is undefined therefore this would result in not valid and\n // make a ugly warning border afterwards we can safely check\n if (this._value) {\n const currentValue = String(this.inputElement.value);\n\n // It can be that currentValue is empty due to we removing the value on focus,\n // if that is the case we should check previous value which should be in the placeholder\n if (currentValue.length) {\n return this._value == this.inputElement.value;\n } else {\n return this._value == this.inputElement.placeholder;\n }\n }\n return true;\n }\n\n get invalid() {\n return !this.valid;\n }\n\n blur() {\n this.writeValue(this._value);\n this.writePlaceholder(this._value);\n this.timeValueChanged.emit(this._value);\n }\n\n focus() {\n this.writeValue('');\n }\n\n /**\n * Write value to inputElement\n * @param value NumberInput\n */\n writeValue(value: NumberInput) {\n if (value !== '') {\n this.inputElement.value = pad(value, 2);\n } else {\n this.inputElement.value = '';\n }\n this.cdr.markForCheck();\n }\n\n /**\n * Writes value to placeholder\n * @param value NumberInput\n */\n writePlaceholder(value: NumberInput) {\n this.inputElement.placeholder = pad(value, 2);\n this.cdr.markForCheck();\n }\n\n keyDownHandler(event: KeyboardEvent) {\n if (String(this.inputElement.value).length > 0) {\n let value: number | null = null;\n if (event.keyCode === UP_ARROW) {\n value = coerceNumberProperty(this._value);\n value += this._interval;\n event.stopPropagation();\n } else if (event.keyCode === DOWN_ARROW) {\n value = coerceNumberProperty(this._value);\n value -= this._interval;\n event.stopPropagation();\n }\n\n // if value has changed\n if (typeof value === 'number') {\n this.writeValue(value);\n this.writePlaceholder(value);\n this.clampInputValue();\n this.timeValueChanged.emit(this._value);\n }\n }\n }\n\n /**\n * Prevent non number inputs in the inputElement with the exception of Enter/BackSpace\n * @param event KeyboardEvent\n */\n keyPressHandler(event: KeyboardEvent) {\n const key = event?.key ?? null;\n if (isNaN(Number(key)) && key !== 'Enter') {\n event.preventDefault();\n }\n }\n\n inputChangedHandler() {\n this.clampInputValue();\n this.timeValueChanged.emit(this._value);\n }\n\n clampInputValue() {\n if (this.inputElement?.value === '') {\n return;\n }\n\n const value = coerceNumberProperty(this.inputElement?.value ?? null);\n // if this._min === 0, we should allow 0\n if (value || (this._min === 0 && value === 0)) {\n const clampedValue = Math.min(Math.max(value, this._min), this._max);\n if (clampedValue !== value) {\n this.writeValue(clampedValue);\n this.writePlaceholder(clampedValue);\n }\n this._value = clampedValue;\n }\n }\n\n /**\n * Remove event listeners on destruction\n */\n ngOnDestroy(): void {\n this.inputElement.removeEventListener('keydown', this.keyDownListener);\n this.inputElement.removeEventListener('keypress', this.keyPressListener);\n this.inputElement.removeEventListener('input', this.inputEventListener);\n }\n}\n\n@Component({\n selector: 'asw-time',\n templateUrl: 'time.html',\n styleUrls: ['time.scss'],\n exportAs: 'aswTime',\n host: {\n class: 'asw-time'\n },\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class AswTime<D> implements OnChanges, AfterViewInit, OnDestroy {\n /** Emits when the currently selected date changes. */\n @Output() readonly selectedChange = new EventEmitter<D>();\n\n /** Emits when any date changes. */\n @Output() readonly activeDateChange = new EventEmitter<D>();\n\n /** Emits when any date is selected. */\n @Output() readonly _userSelection = new EventEmitter<void>();\n\n /** Emits when AM/PM button are clicked. */\n @Output() readonly ampmChange = new EventEmitter<AswAMPM>();\n\n /** Emits when AM/PM button are clicked. */\n @Output() readonly clockViewChange = new EventEmitter<AswClockView>();\n\n /** A function used to filter which dates are selectable. */\n @Input() dateFilter!: (date: D, type: AswDatetimepickerFilterType) => boolean;\n\n /** Step over minutes. */\n @Input() interval: number = 1;\n\n @ViewChild('hourInput', { read: ElementRef<HTMLInputElement> })\n protected hourInputElement: ElementRef<HTMLInputElement> | undefined;\n\n @ViewChild('hourInput', { read: AswTimeInput })\n protected hourInputDirective: AswTimeInput | undefined;\n\n @ViewChild('minuteInput', { read: ElementRef<HTMLInputElement> })\n protected minuteInputElement: ElementRef<HTMLInputElement> | undefined;\n\n @ViewChild('minuteInput', { read: AswTimeInput })\n protected minuteInputDirective: AswTimeInput | undefined;\n\n datetimepickerIntlChangesSubscription: SubscriptionLike;\n\n /** Whether the clock uses 12 hour format. */\n @Input()\n get twelvehour(): boolean {\n return this._twelvehour;\n }\n set twelvehour(value: boolean) {\n this._twelvehour = coerceBooleanProperty(value);\n }\n private _twelvehour = false;\n\n /** Whether the time is now in AM or PM. */\n @Input() AMPM: AswAMPM = 'AM';\n\n /**\n * The date to display in this clock view.\n */\n @Input()\n get activeDate(): D {\n return this._activeDate;\n }\n set activeDate(value: D) {\n this._activeDate = this._adapter.clampDate(value, this.minDate, this.maxDate);\n }\n private _activeDate!: D;\n\n /** The currently selected date. */\n @Input()\n get selected(): D | null {\n return this._selected;\n }\n set selected(value: D | null) {\n this._selected = this._adapter.getValidDateOrNull(this._adapter.deserialize(value));\n if (this._selected) {\n this.activeDate = this._selected;\n }\n }\n private _selected!: D | null;\n\n /** The minimum selectable date. */\n @Input()\n get minDate(): D | null {\n return this._minDate;\n }\n\n set minDate(value: D | null) {\n this._minDate = this._adapter.getValidDateOrNull(this._adapter.deserialize(value));\n }\n private _minDate!: D | null;\n\n /** The maximum selectable date. */\n @Input()\n get maxDate(): D | null {\n return this._maxDate;\n }\n set maxDate(value: D | null) {\n this._maxDate = this._adapter.getValidDateOrNull(this._adapter.deserialize(value));\n }\n private _maxDate!: D | null;\n\n /** Whether the clock should be started in hour or minute view. */\n @Input()\n get clockView() {\n return this._clockView;\n }\n set clockView(value: AswClockView) {\n this._clockView = value;\n }\n /** Whether the clock is in hour view. */\n private _clockView: AswClockView = 'hour';\n\n get isHourView() {\n return this._clockView === 'hour';\n }\n\n get isMinuteView() {\n return this._clockView === 'hour';\n }\n\n get hour() {\n if (!this.activeDate) {\n if (this.twelvehour) {\n return '12';\n } else {\n return '00';\n }\n }\n\n const hour = Number(this._adapter.getHour(this.activeDate));\n if (!this.twelvehour) {\n return this.prefixWithZero(hour);\n }\n\n if (hour === 0) {\n return '12';\n } else {\n return this.prefixWithZero(hour > 12 ? hour - 12 : hour);\n }\n }\n\n get minute() {\n if (this.activeDate) {\n return this.prefixWithZero(this._adapter.getMinute(this.activeDate));\n }\n\n return '00';\n }\n\n prefixWithZero(value: number) {\n if (value < 10) {\n return '0' + String(value);\n }\n\n return String(value);\n }\n\n constructor(\n private _adapter: DatetimeAdapter<D>,\n private _changeDetectorRef: ChangeDetectorRef,\n protected _datetimepickerIntl: AswDatetimepickerIntl\n ) {\n this.datetimepickerI