UNPKG

@qeydar/datepicker

Version:

A comprehensive Date and Time Picker for Angular with Jalali calendar support

1,270 lines (1,262 loc) 257 kB
import * as i0 from '@angular/core'; import { InjectionToken, Injectable, ElementRef, Directive, Input, HostListener, EventEmitter, forwardRef, Component, ChangeDetectionStrategy, Output, ViewChild, ViewEncapsulation, Inject, Optional, ViewChildren, ContentChildren, NgModule } from '@angular/core'; import * as i1$1 from '@angular/forms'; import { NG_VALUE_ACCESSOR, ReactiveFormsModule, FormsModule } from '@angular/forms'; import { trigger, state, style, transition, animate } from '@angular/animations'; import { isValid, parse, format, addDays, addMonths, addYears, addHours, startOfWeek, endOfMonth, isSameDay, isSameMonth, isSameYear, isAfter, isBefore, startOfMonth, max, setYear, getDaysInMonth } from 'date-fns-jalali'; import { parseISO, isEqual, startOfDay, addMinutes, isValid as isValid$1, parse as parse$1, format as format$1, addDays as addDays$1, addMonths as addMonths$1, addYears as addYears$1, addHours as addHours$1, startOfWeek as startOfWeek$1, isSameDay as isSameDay$1, isSameMonth as isSameMonth$1, isSameYear as isSameYear$1, isAfter as isAfter$1, isBefore as isBefore$1, startOfMonth as startOfMonth$1, endOfMonth as endOfMonth$1, max as max$1, setYear as setYear$1, getDaysInMonth as getDaysInMonth$1 } from 'date-fns'; import { __decorate, __awaiter } from 'tslib'; import * as i1 from '@angular/cdk/overlay'; import { CdkOverlayOrigin, ConnectionPositionPair, OverlayModule } from '@angular/cdk/overlay'; import { BehaviorSubject, Subject, takeUntil as takeUntil$1, fromEvent } from 'rxjs'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { takeUntil } from 'rxjs/operators'; import { NgIf, NgFor, NgTemplateOutlet, DOCUMENT } from '@angular/common'; /** * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://github.com/NG-ZORRO/../blob/master/LICENSE */ class AnimationDuration { } AnimationDuration.SLOW = '0.3s'; // Modal AnimationDuration.BASE = '0.2s'; AnimationDuration.FAST = '0.1s'; // Tooltip class AnimationCurves { } AnimationCurves.EASE_BASE_OUT = 'cubic-bezier(0.7, 0.3, 0.1, 1)'; AnimationCurves.EASE_BASE_IN = 'cubic-bezier(0.9, 0, 0.3, 0.7)'; AnimationCurves.EASE_OUT = 'cubic-bezier(0.215, 0.61, 0.355, 1)'; AnimationCurves.EASE_IN = 'cubic-bezier(0.55, 0.055, 0.675, 0.19)'; AnimationCurves.EASE_IN_OUT = 'cubic-bezier(0.645, 0.045, 0.355, 1)'; AnimationCurves.EASE_OUT_BACK = 'cubic-bezier(0.12, 0.4, 0.29, 1.46)'; AnimationCurves.EASE_IN_BACK = 'cubic-bezier(0.71, -0.46, 0.88, 0.6)'; AnimationCurves.EASE_IN_OUT_BACK = 'cubic-bezier(0.71, -0.46, 0.29, 1.46)'; AnimationCurves.EASE_OUT_CIRC = 'cubic-bezier(0.08, 0.82, 0.17, 1)'; AnimationCurves.EASE_IN_CIRC = 'cubic-bezier(0.6, 0.04, 0.98, 0.34)'; AnimationCurves.EASE_IN_OUT_CIRC = 'cubic-bezier(0.78, 0.14, 0.15, 0.86)'; AnimationCurves.EASE_OUT_QUINT = 'cubic-bezier(0.23, 1, 0.32, 1)'; AnimationCurves.EASE_IN_QUINT = 'cubic-bezier(0.755, 0.05, 0.855, 0.06)'; AnimationCurves.EASE_IN_OUT_QUINT = 'cubic-bezier(0.86, 0, 0.07, 1)'; /** * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://github.com/NG-ZORRO/../blob/master/LICENSE */ const ANIMATION_TRANSITION_IN = `${AnimationDuration.BASE} ${AnimationCurves.EASE_OUT_QUINT}`; const ANIMATION_TRANSITION_OUT = `${AnimationDuration.BASE} ${AnimationCurves.EASE_IN_QUINT}`; const slideMotion = trigger('slideMotion', [ state('void', style({ opacity: 0, transform: 'scaleY(0.8)' })), state('enter', style({ opacity: 1, transform: 'scaleY(1)' })), transition('void => *', [animate(ANIMATION_TRANSITION_IN)]), transition('* => void', [animate(ANIMATION_TRANSITION_OUT)]) ]); const slideAlertMotion = trigger('slideAlertMotion', [ transition(':leave', [ style({ opacity: 1, transform: 'scaleY(1)', transformOrigin: '0% 0%' }), animate(`${AnimationDuration.SLOW} ${AnimationCurves.EASE_IN_OUT_CIRC}`, style({ opacity: 0, transform: 'scaleY(0)', transformOrigin: '0% 0%' })) ]) ]); // Injection token for date adapter const DATE_ADAPTER = new InjectionToken('DateAdapter'); // Provider factory for default adapters function provideDateAdapter(calendarType) { return { provide: DATE_ADAPTER, useFactory: (jalali, gregorian) => { return calendarType === 'jalali' ? jalali : gregorian; }, deps: [JalaliDateAdapter, GregorianDateAdapter] }; } class JalaliDateAdapter { today() { return new Date(); } parse(value, formatString) { if (typeof value === 'string') { // Check if it's in ISO 8601 format if (value.includes('T')) { const parsedDate = parseISO(value); return isValid(parsedDate) ? parsedDate : null; } try { const parsedDate = parse(value, formatString, new Date()); return isValid(parsedDate) ? parsedDate : null; } catch (error) { console.error('Error parsing date:', error); return null; } } else if (value instanceof Date) { return isValid(value) ? value : null; } return null; } format(date, formatString) { return format(date, formatString); } addDays(date, amount) { return addDays(date, amount); } addMonths(date, amount) { return addMonths(date, amount); } addYears(date, amount) { return addYears(date, amount); } addHours(date, amount) { return addHours(date, amount); } getYear(date) { return date ? parseInt(format(date, 'yyyy')) : null; } getMonth(date) { // Jalali months are 1-indexed in date-fns-jalali return date ? parseInt(format(date, 'M')) - 1 : null; } getDate(date) { return date ? parseInt(format(date, 'dd')) : null; } getDayOfWeek(date) { return parseInt(format(date, 'i')) - 1; } getMonthNames(style) { const jalaliMonths = [ 'فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند' ]; switch (style) { case 'long': return jalaliMonths; case 'short': return jalaliMonths.map(month => month.substring(0, 3)); case 'narrow': return jalaliMonths.map(month => month.substring(0, 1)); default: return jalaliMonths; } } getDateNames() { return Array.from({ length: 31 }, (_, i) => (i + 1).toString()); } getDayOfWeekNames(style) { const formats = { long: 'EEEE', short: 'EEEEE', narrow: 'EEEEEE' }; return Array.from({ length: 7 }, (_, i) => format(addDays(startOfWeek(new Date()), i), formats[style])); } getFirstDayOfWeek() { return 6; // Saturday is the first day of the week in the Jalali calendar } getNumDaysInMonth(date) { return parseInt(format(endOfMonth(date), 'd')); } clone(date) { return new Date(date.getTime()); } createDate(year, month, date) { // Adjust for 0-indexed months in the interface vs 1-indexed months in date-fns-jalali return parse(`${year}/${month + 1}/${date}`, 'yyyy/M/d', new Date()); } isSameDay(date1, date2) { return isSameDay(date1, date2); } isSameMonth(date1, date2) { return isSameMonth(date1, date2); } isSameYear(date1, date2) { return isSameYear(date1, date2); } isAfter(date1, date2) { return isAfter(date1, date2); } isBefore(date1, date2) { return isBefore(date1, date2); } isEqual(date1, date2) { return isEqual(date1, date2); } startOfMonth(date) { return startOfMonth(date); } endOfMonth(date) { return endOfMonth(date); } startOfWeek(date) { return startOfWeek(date, { weekStartsOn: this.getFirstDayOfWeek() }); } isValidFormat(dateString, formatString) { try { const parsedDate = parse(dateString, formatString, new Date()); if (!isValid(parsedDate)) { return false; } // Check if the formatted parsed date matches the original date string const formattedDate = format(parsedDate, formatString); return formattedDate === dateString; } catch (error) { return false; } } max(dates) { return max(dates); } setYear(date, year) { return setYear(date, year); } startOfDay(date) { return startOfDay(date); } getHours(date) { return date ? parseInt(format(date, 'HH')) : null; } getMinutes(date) { return date ? parseInt(format(date, 'mm')) : null; } getSeconds(date) { return date ? parseInt(format(date, 'ss')) : null; } setHours(date, hours) { const newDate = this.clone(date); newDate.setHours(hours); return newDate; } setMinutes(date, minutes) { const newDate = this.clone(date); newDate.setMinutes(minutes); return newDate; } setSeconds(date, seconds) { const newDate = this.clone(date); newDate.setSeconds(seconds); return newDate; } getDaysInMonth(date) { return getDaysInMonth(date); } addMinutes(date, amount) { return addMinutes(date, amount); } } JalaliDateAdapter.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: JalaliDateAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); JalaliDateAdapter.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: JalaliDateAdapter, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: JalaliDateAdapter, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }] }); class GregorianDateAdapter { today() { return new Date(); } parse(value, formatString) { if (typeof value === 'string') { // Check if it's in ISO 8601 format if (value.includes('T')) { const parsedDate = parseISO(value); return isValid$1(parsedDate) ? parsedDate : null; } try { let parsedDate; if (formatString === "ISO") { parsedDate = parseISO(value); } else { parsedDate = parse$1(value, formatString, new Date()); } return isValid$1(parsedDate) ? parsedDate : null; } catch (error) { console.error('Error parsing date:', error); return null; } } else if (value instanceof Date) { return isValid$1(value) ? value : null; } return null; } format(date, formatString) { return format$1(date, formatString); } addDays(date, amount) { return addDays$1(date, amount); } addMonths(date, amount) { return addMonths$1(date, amount); } addYears(date, amount) { return addYears$1(date, amount); } addHours(date, amount) { return addHours$1(date, amount); } getYear(date) { return date.getFullYear(); } getMonth(date) { return date.getMonth(); } getDate(date) { return date.getDate(); } getDayOfWeek(date) { return date.getDay(); } getMonthNames(style) { const formats = { long: 'MMMM', short: 'MMM', narrow: 'MMMMM' }; return Array.from({ length: 12 }, (_, i) => format$1(new Date(2000, i, 1), formats[style])); } getDateNames() { return Array.from({ length: 31 }, (_, i) => (i + 1).toString()); } getDayOfWeekNames(style) { const formats = { long: 'EEEE', short: 'EEE', narrow: 'EEEEE' }; return Array.from({ length: 7 }, (_, i) => format$1(addDays$1(startOfWeek$1(new Date()), i), formats[style])); } getFirstDayOfWeek() { return 0; // Sunday is the first day of the week in the Gregorian calendar } getNumDaysInMonth(date) { return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate(); } clone(date) { return new Date(date.getTime()); } createDate(year, month, date) { return new Date(year, month, date); } isSameDay(date1, date2) { return isSameDay$1(date1, date2); } isSameMonth(date1, date2) { return isSameMonth$1(date1, date2); } isSameYear(date1, date2) { return isSameYear$1(date1, date2); } isAfter(date1, date2) { return isAfter$1(date1, date2); } isBefore(date1, date2) { return isBefore$1(date1, date2); } isEqual(date1, date2) { return isEqual(date1, date2); } startOfMonth(date) { return startOfMonth$1(date); } endOfMonth(date) { return endOfMonth$1(date); } startOfWeek(date) { return startOfWeek$1(date, { weekStartsOn: this.getFirstDayOfWeek() }); } isValidFormat(dateString, formatString) { try { const parsedDate = parse$1(dateString, formatString, new Date()); if (!isValid$1(parsedDate)) { return false; } // Check if the formatted parsed date matches the original date string const formattedDate = format$1(parsedDate, formatString); return formattedDate === dateString; } catch (error) { return false; } } max(dates) { return max$1(dates); } setYear(date, year) { return setYear$1(date, year); } startOfDay(date) { return startOfDay(date); } getHours(date) { return date ? date.getHours() : null; } getMinutes(date) { return date ? date.getMinutes() : null; } getSeconds(date) { return date ? date.getSeconds() : null; } setHours(date, hours) { const newDate = this.clone(date); newDate.setHours(hours); return newDate; } setMinutes(date, minutes) { const newDate = this.clone(date); newDate.setMinutes(minutes); return newDate; } setSeconds(date, seconds) { const newDate = this.clone(date); newDate.setSeconds(seconds); return newDate; } getDaysInMonth(date) { return getDaysInMonth$1(date); } addMinutes(date, amount) { return addMinutes(date, amount); } } GregorianDateAdapter.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GregorianDateAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); GregorianDateAdapter.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GregorianDateAdapter, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GregorianDateAdapter, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }] }); class lang_Fa { constructor() { this.today = "امروز"; this.lastDay = "آخرین روز"; this.lastWeek = "آخرین هفته"; this.lastMonth = "آخرین ماه"; this.custom = "دلخواه"; this.previousMonth = "ماه قبل"; this.nextMonth = "ماه بعد"; this.previousYear = "سال قبل"; this.nextYear = "سال بعد"; this.selectTime = "انتخاب زمان"; this.selectDate = "انتخاب تاریخ"; this.selectMonth = "انتخاب ماه"; this.selectYear = "انتخاب سال"; this.selectDateRange = "انتخاب محدوده تاریخ"; this.startDate = "از تاریخ"; this.endDate = "تا تاریخ"; this.pm = "ب.ظ"; this.am = "ق.ظ"; this.ok = "تایید"; this.cancel = "لغو"; this.now = "اکنون"; } } lang_Fa.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: lang_Fa, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); lang_Fa.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: lang_Fa, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: lang_Fa, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }] }); class lang_En { constructor() { this.today = "Today"; this.lastDay = "Last Day"; this.lastWeek = "Last Week"; this.lastMonth = "Last Month"; this.custom = "Custom"; this.previousMonth = "Previous Month"; this.nextMonth = "Next Month"; this.previousYear = "Previous Year"; this.nextYear = "Next Year"; this.selectTime = "Select time"; this.selectDate = "Select date"; this.selectMonth = "Select month"; this.selectYear = "Select year"; this.selectDateRange = "Select date range"; this.startDate = "Start date"; this.endDate = "End date"; this.pm = "PM"; this.am = "AM"; this.ok = "Ok"; this.cancel = "Cancel"; this.now = "Now"; } } lang_En.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: lang_En, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); lang_En.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: lang_En, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: lang_En, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }] }); class QeydarDatePickerService { /** * */ constructor(locale_fa, locale_en) { this.locale_fa = locale_fa; this.locale_en = locale_en; this.activeInput$ = new BehaviorSubject(''); } getActiveInputValue() { return this.activeInput$.getValue(); } } QeydarDatePickerService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: QeydarDatePickerService, deps: [{ token: lang_Fa }, { token: lang_En }], target: i0.ɵɵFactoryTarget.Injectable }); QeydarDatePickerService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: QeydarDatePickerService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: QeydarDatePickerService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: lang_Fa }, { type: lang_En }]; } }); class DestroyService extends Subject { ngOnDestroy() { this.next(); this.complete(); } } DestroyService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: DestroyService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); DestroyService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: DestroyService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: DestroyService, decorators: [{ type: Injectable }] }); function propDecoratorFactory(name, fallback) { function propDecorator(target, propName, originalDescriptor) { const privatePropName = `$$__zorroPropDecorator__${propName}`; if (Object.prototype.hasOwnProperty.call(target, privatePropName)) { console.warn(`The prop "${privatePropName}" is already exist, it will be overrided by ${name} decorator.`); } Object.defineProperty(target, privatePropName, { configurable: true, writable: true }); return { get() { return originalDescriptor && originalDescriptor.get ? originalDescriptor.get.bind(this)() : this[privatePropName]; }, set(value) { if (originalDescriptor && originalDescriptor.set) { originalDescriptor.set.bind(this)(fallback(value)); } this[privatePropName] = fallback(value); } }; } return propDecorator; } /** * Input decorator that handle a prop to do get/set automatically with toBoolean * * Why not using @InputBoolean alone without @Input? AOT needs @Input to be visible * * @howToUse * ``` * @Input() @InputBoolean() visible: boolean = false; * * // Act as below: * // @Input() * // get visible() { return this.__visible; } * // set visible(value) { this.__visible = value; } * // __visible = false; * ``` */ function InputBoolean() { return propDecoratorFactory('InputBoolean', toBoolean); } function toBoolean(value) { return coerceBooleanProperty(value); } class NzConnectedOverlayDirective { constructor(cdkConnectedOverlay, nzDestroyService) { this.cdkConnectedOverlay = cdkConnectedOverlay; this.nzDestroyService = nzDestroyService; this.nzArrowPointAtCenter = false; this.cdkConnectedOverlay.backdropClass = 'nz-overlay-transparent-backdrop'; this.cdkConnectedOverlay.positionChange .pipe(takeUntil(this.nzDestroyService)) .subscribe((position) => { if (this.nzArrowPointAtCenter) { this.updateArrowPosition(position); } }); } updateArrowPosition(position) { const originRect = this.getOriginRect(); const placement = getPlacementName(position); let offsetX = 0; let offsetY = 0; if (placement === 'topLeft' || placement === 'bottomLeft') { offsetX = originRect.width / 2 - 14; } else if (placement === 'topRight' || placement === 'bottomRight') { offsetX = -(originRect.width / 2 - 14); } else if (placement === 'leftTop' || placement === 'rightTop') { offsetY = originRect.height / 2 - 10; } else if (placement === 'leftBottom' || placement === 'rightBottom') { offsetY = -(originRect.height / 2 - 10); } if (this.cdkConnectedOverlay.offsetX !== offsetX || this.cdkConnectedOverlay.offsetY !== offsetY) { this.cdkConnectedOverlay.offsetY = offsetY; this.cdkConnectedOverlay.offsetX = offsetX; this.cdkConnectedOverlay.overlayRef.updatePosition(); } } getFlexibleConnectedPositionStrategyOrigin() { if (this.cdkConnectedOverlay.origin instanceof CdkOverlayOrigin) { return this.cdkConnectedOverlay.origin.elementRef; } else { return this.cdkConnectedOverlay.origin; } } getOriginRect() { const origin = this.getFlexibleConnectedPositionStrategyOrigin(); if (origin instanceof ElementRef) { return origin.nativeElement.getBoundingClientRect(); } // Check for Element so SVG elements are also supported. if (origin instanceof Element) { return origin.getBoundingClientRect(); } const width = origin.width || 0; const height = origin.height || 0; // If the origin is a point, return a client rect as if it was a 0x0 element at the point. return { top: origin.y, bottom: origin.y + height, left: origin.x, right: origin.x + width, height, width }; } } NzConnectedOverlayDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: NzConnectedOverlayDirective, deps: [{ token: i1.CdkConnectedOverlay }, { token: DestroyService }], target: i0.ɵɵFactoryTarget.Directive }); NzConnectedOverlayDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.3.0", type: NzConnectedOverlayDirective, isStandalone: true, selector: "[cdkConnectedOverlay][nzConnectedOverlay]", inputs: { nzArrowPointAtCenter: "nzArrowPointAtCenter" }, providers: [DestroyService], exportAs: ["nzConnectedOverlay"], ngImport: i0 }); __decorate([ InputBoolean() ], NzConnectedOverlayDirective.prototype, "nzArrowPointAtCenter", void 0); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: NzConnectedOverlayDirective, decorators: [{ type: Directive, args: [{ selector: '[cdkConnectedOverlay][nzConnectedOverlay]', exportAs: 'nzConnectedOverlay', standalone: true, providers: [DestroyService] }] }], ctorParameters: function () { return [{ type: i1.CdkConnectedOverlay }, { type: DestroyService }]; }, propDecorators: { nzArrowPointAtCenter: [{ type: Input }] } }); //overlay-position.ts: const POSITION_MAP = { top: new ConnectionPositionPair({ originX: 'center', originY: 'top' }, { overlayX: 'center', overlayY: 'bottom' }), topCenter: new ConnectionPositionPair({ originX: 'center', originY: 'top' }, { overlayX: 'center', overlayY: 'bottom' }), topLeft: new ConnectionPositionPair({ originX: 'start', originY: 'top' }, { overlayX: 'start', overlayY: 'bottom' }), topRight: new ConnectionPositionPair({ originX: 'end', originY: 'top' }, { overlayX: 'end', overlayY: 'bottom' }), right: new ConnectionPositionPair({ originX: 'end', originY: 'center' }, { overlayX: 'start', overlayY: 'center' }), rightTop: new ConnectionPositionPair({ originX: 'end', originY: 'top' }, { overlayX: 'start', overlayY: 'top' }), rightBottom: new ConnectionPositionPair({ originX: 'end', originY: 'bottom' }, { overlayX: 'start', overlayY: 'bottom' }), bottom: new ConnectionPositionPair({ originX: 'center', originY: 'bottom' }, { overlayX: 'center', overlayY: 'top' }), bottomCenter: new ConnectionPositionPair({ originX: 'center', originY: 'bottom' }, { overlayX: 'center', overlayY: 'top' }), bottomLeft: new ConnectionPositionPair({ originX: 'start', originY: 'bottom' }, { overlayX: 'start', overlayY: 'top' }), bottomRight: new ConnectionPositionPair({ originX: 'end', originY: 'bottom' }, { overlayX: 'end', overlayY: 'top' }), left: new ConnectionPositionPair({ originX: 'start', originY: 'center' }, { overlayX: 'end', overlayY: 'center' }), leftTop: new ConnectionPositionPair({ originX: 'start', originY: 'top' }, { overlayX: 'end', overlayY: 'top' }), leftBottom: new ConnectionPositionPair({ originX: 'start', originY: 'bottom' }, { overlayX: 'end', overlayY: 'bottom' }) }; const DEFAULT_TOOLTIP_POSITIONS = [POSITION_MAP.top, POSITION_MAP.right, POSITION_MAP.bottom, POSITION_MAP.left]; const DEFAULT_CASCADER_POSITIONS = [ POSITION_MAP.bottomLeft, POSITION_MAP.bottomRight, POSITION_MAP.topLeft, POSITION_MAP.topRight, POSITION_MAP.topCenter, POSITION_MAP.bottomCenter ]; const DEFAULT_MENTION_TOP_POSITIONS = [ new ConnectionPositionPair({ originX: 'start', originY: 'bottom' }, { overlayX: 'start', overlayY: 'bottom' }), new ConnectionPositionPair({ originX: 'start', originY: 'bottom' }, { overlayX: 'end', overlayY: 'bottom' }) ]; const DEFAULT_MENTION_BOTTOM_POSITIONS = [ POSITION_MAP.bottomLeft, new ConnectionPositionPair({ originX: 'start', originY: 'bottom' }, { overlayX: 'end', overlayY: 'top' }) ]; function getPlacementName(position) { for (const placement in POSITION_MAP) { if (position.connectionPair.originX === POSITION_MAP[placement].originX && position.connectionPair.originY === POSITION_MAP[placement].originY && position.connectionPair.overlayX === POSITION_MAP[placement].overlayX && position.connectionPair.overlayY === POSITION_MAP[placement].overlayY) { return placement; } } return undefined; } const DATE_PICKER_POSITION_MAP = { bottomLeft: new ConnectionPositionPair({ originX: 'start', originY: 'bottom' }, { overlayX: 'start', overlayY: 'top' }, undefined, 2), topLeft: new ConnectionPositionPair({ originX: 'start', originY: 'top' }, { overlayX: 'start', overlayY: 'bottom' }, undefined, -2), bottomRight: new ConnectionPositionPair({ originX: 'end', originY: 'bottom' }, { overlayX: 'end', overlayY: 'top' }, undefined, 2), topRight: new ConnectionPositionPair({ originX: 'end', originY: 'top' }, { overlayX: 'end', overlayY: 'bottom' }, undefined, -2) }; const DEFAULT_DATE_PICKER_POSITIONS = [ DATE_PICKER_POSITION_MAP.bottomLeft, DATE_PICKER_POSITION_MAP.topLeft, DATE_PICKER_POSITION_MAP.bottomRight, DATE_PICKER_POSITION_MAP.topRight ]; class DateMaskDirective { constructor(el) { this.el = el; this.dateFormat = 'yyyy/MM/dd'; this.disableInputMask = false; this.delimiters = []; this.parts = []; this.lastValue = ''; } ngOnInit() { this.parseFormat(); } parseFormat() { if (this.disableInputMask) return; this.parts = []; this.delimiters = []; let currentPart = ''; for (let i = 0; i < this.dateFormat.length; i++) { const char = this.dateFormat[i]; if (this.isFormatChar(char)) { currentPart += char; } else { if (currentPart) { this.parts.push(currentPart); currentPart = ''; } this.delimiters.push(char); } } if (currentPart) { this.parts.push(currentPart); } } isFormatChar(char) { return /[yMdHhmsa]/i.test(char); } onInput(event) { if (this.disableInputMask) return; const input = event.target; const cursorPosition = input.selectionStart || 0; let value = input.value.replace(/[^0-9APMapm\s:/\-\.]/g, ''); // Allow backspace/delete if (value.length < this.lastValue.length) { this.lastValue = value; return; } let formattedParts = []; let currentValue = value; let shouldAddDelimiter = false; let totalLength = 0; let newCursorPosition = cursorPosition; for (let i = 0; i < this.parts.length; i++) { const part = this.extractPart(currentValue, this.parts[i]); if (!part && part !== '0') break; const expectedLength = this.getPartLength(this.parts[i]); let formattedPart = part; if (formattedPart.length >= expectedLength) { formattedPart = this.validatePart(formattedPart.slice(0, expectedLength), this.parts[i]); shouldAddDelimiter = true; } formattedParts.push(formattedPart); totalLength += formattedPart.length; if (shouldAddDelimiter && i < this.parts.length - 1) { formattedParts.push(this.delimiters[i] || ''); totalLength += 1; shouldAddDelimiter = false; if (cursorPosition === totalLength - 1) { newCursorPosition = totalLength; } } currentValue = this.removeProcessedPart(currentValue, part); } const formattedValue = formattedParts.join(''); input.value = formattedValue; // Set cursor position newCursorPosition = Math.min(newCursorPosition, totalLength); input.setSelectionRange(newCursorPosition, newCursorPosition); this.lastValue = formattedValue; } extractPart(value, format) { if (!value) return ''; if (format[0].toLowerCase() === 'a') { // Handle AM/PM const match = value.match(/^[AaPp][Mm]?/); return match ? match[0].toUpperCase() : ''; } // Handle numeric parts const match = value.match(/^\d+/); return match ? match[0] : ''; } removeProcessedPart(value, part) { if (!part) return value; // Remove part and following delimiter if exists const remainingValue = value.slice(part.length); return remainingValue.replace(/^[:/\s-]/, ''); } onKeyDown(event) { var _a, _b; if (this.disableInputMask) return; const input = event.target; const cursorPosition = input.selectionStart || 0; // Allow control keys if (event.key === 'Backspace' || event.key === 'Delete' || event.key === 'ArrowLeft' || event.key === 'ArrowRight' || event.key === 'Tab' || event.ctrlKey) { return; } const currentPartIndex = this.getCurrentPartIndex(input.value, cursorPosition); if (currentPartIndex === -1) return; const currentFormat = this.parts[currentPartIndex]; const isTimeDelimiter = event.key === ':' && cursorPosition > 0 && (((_a = this.parts[currentPartIndex - 1]) === null || _a === void 0 ? void 0 : _a.includes('H')) || ((_b = this.parts[currentPartIndex - 1]) === null || _b === void 0 ? void 0 : _b.includes('h'))); // Allow time delimiter after hours if (isTimeDelimiter) { if (this.delimiters[currentPartIndex - 1] === ':') { const parts = input.value.split(/[:/\s-]/); const currentPart = this.validatePart(parts[currentPartIndex - 1], this.parts[currentPartIndex - 1]); parts[currentPartIndex - 1] = currentPart; const newValue = parts.slice(0, currentPartIndex).join(this.delimiters[currentPartIndex - 1]) + ':'; input.value = newValue + parts.slice(currentPartIndex).join(this.delimiters[currentPartIndex]); input.setSelectionRange(newValue.length, newValue.length); event.preventDefault(); } return; } // Handle AM/PM input if (currentFormat[0].toLowerCase() === 'a') { if (!/^[AaPpMm]$/.test(event.key)) { event.preventDefault(); } return; } // Allow only digits for other parts if (!/^\d$/.test(event.key)) { event.preventDefault(); } } validatePart(value, format) { if (value === '') return ''; const type = format[0].toLowerCase(); if (type === 'a') { const upperValue = value.toUpperCase(); if (value.length === 1) { return upperValue === 'A' || upperValue === 'P' ? upperValue : ''; } return ['AM', 'PM'].includes(upperValue) ? upperValue : upperValue[0]; } const numValue = parseInt(value, 10); switch (type) { case 'h': // 12-hour format if (format[0] == 'H') return Math.min(Math.max(numValue, 0), 23).toString().padStart(2, '0'); return Math.min(Math.max(numValue, 1), 12).toString().padStart(2, '0'); case 'm': // month or minute if (format === 'MM') { return Math.min(Math.max(numValue, 1), 12).toString().padStart(2, '0'); } return Math.min(Math.max(numValue, 0), 59).toString().padStart(2, '0'); case 's': // seconds return Math.min(Math.max(numValue, 0), 59).toString().padStart(2, '0'); case 'd': // day return Math.min(Math.max(numValue, 1), 31).toString().padStart(2, '0'); case 'y': // year if (format.length === 2) return value.padStart(2, '0'); return value.padStart(4, '0'); default: return value; } } getPartLength(format) { const type = format[0].toLowerCase(); switch (type) { case 'y': return format.length === 2 ? 2 : 4; case 'a': return format.length === 1 ? 1 : 2; default: return 2; } } getCurrentPartIndex(value, cursorPosition) { const parts = value.split(/[:/\s-]/); let currentIndex = 0; let totalLength = 0; for (let i = 0; i < parts.length; i++) { totalLength += parts[i].length; if (cursorPosition <= totalLength + i) { return i; } totalLength += 1; // Add delimiter length } return parts.length - 1; } } DateMaskDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: DateMaskDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); DateMaskDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.3.0", type: DateMaskDirective, isStandalone: true, selector: "[qeydar-dateMask]", inputs: { dateFormat: ["qeydar-dateMask", "dateFormat"], disableInputMask: "disableInputMask" }, host: { listeners: { "input": "onInput($event)", "keydown": "onKeyDown($event)" } }, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: DateMaskDirective, decorators: [{ type: Directive, args: [{ selector: '[qeydar-dateMask]', standalone: true }] }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { dateFormat: [{ type: Input, args: ['qeydar-dateMask'] }], disableInputMask: [{ type: Input }], onInput: [{ type: HostListener, args: ['input', ['$event']] }], onKeyDown: [{ type: HostListener, args: ['keydown', ['$event']] }] } }); class TimePickerComponent { constructor(fb, elementRef, cdref, datePickerService, jalaliAdapter, gregorianAdapter) { this.fb = fb; this.elementRef = elementRef; this.cdref = cdref; this.datePickerService = datePickerService; this.jalaliAdapter = jalaliAdapter; this.gregorianAdapter = gregorianAdapter; this.rtl = false; this.placement = 'right'; this.valueType = 'string'; this.cssClass = ''; this.showIcon = true; this.inline = false; this.disableInputMask = false; this.disabled = false; this.allowEmpty = true; this.readOnly = false; this.readOnlyInput = false; this.timeChange = new EventEmitter(); this.openChange = new EventEmitter(); this.timeFormat = '12'; this._displayFormat = 'hh:mm a'; this._value = null; this._selectedDate = new Date(); this.onChange = () => { }; this.onTouched = () => { }; this.timeoutId = null; this.showSeconds = false; this.hours = []; this.minutes = Array.from({ length: 60 }, (_, i) => i); this.seconds = Array.from({ length: 60 }, (_, i) => i); this.periods = []; this.selectedTime = { hour: 12, minute: 0, second: 0, period: '' }; this.isOpen = false; this.overlayPositions = [...DEFAULT_DATE_PICKER_POSITIONS]; this.handleDocumentClick = (event) => { if (!this.elementRef.nativeElement.contains(event.target) && this.isOpen) { this.close(); this.handleTimeInput(); } }; this.dateAdapter = this.gregorianAdapter; this.initializeForm(); this.initializeLocale(); } set displayFormat(value) { this._displayFormat = value; this.showSeconds = value.toLowerCase().includes('s'); // Infer time format from display format this.timeFormat = this.getTimeFormatFromDisplayFormat(value); this.updateHourRange(); this.updateTimeDisplay(); } get displayFormat() { return this._displayFormat; } set selectedDate(date) { if (date) { this._selectedDate = date; } } get selectedDate() { return this._selectedDate; } // Lifecycle hooks ngOnInit() { this.updateHourRange(); this.origin = new CdkOverlayOrigin(this.elementRef); this.setupInputSubscription(); this.value = this.selectedDate; // Only add document click listener for non-inline mode if (!this.inline) { document.addEventListener('click', this.handleDocumentClick); } // Auto-open for inline mode if (this.inline) { this.isOpen = true; this.scrollToTime(); } } ngOnDestroy() { this.cleanupTimeouts(); document.removeEventListener('click', this.handleDocumentClick); } ngOnChanges(changes) { if (changes['rtl'] || changes['lang']) { this.updateLocale(); } if (changes['rtl'] && !changes['dateAdapter']) { this.dateAdapter = this.rtl ? this.jalaliAdapter : this.gregorianAdapter; } } // Initialization methods initializeForm() { this.form = this.fb.group({ timeInput: [''] }); } initializeLocale() { this.lang = this.datePickerService.locale_en; this.selectedTime.period = this.lang.am; this.periods = [this.lang.am, this.lang.pm]; } updateLocale() { this.lang = this.rtl ? this.datePickerService.locale_fa : this.datePickerService.locale_en; this.selectedTime.period = this.lang.am; this.periods = [this.lang.am, this.lang.pm]; this.placeholder = this.lang.selectTime; } setupInputSubscription() { var _a; (_a = this.form.get('timeInput')) === null || _a === void 0 ? void 0 : _a.valueChanges.subscribe(value => { if (!value) return; if (!this.isOpen) { this.validateAndUpdateTime(value); } else { this.parseTimeString(value); this.scrollToTime(); } }); } // Time management updateHourRange() { const format = this.getTimeFormatFromDisplayFormat(this._displayFormat); this.hours = format === '12' ? Array.from({ length: 12 }, (_, i) => i + 1) : Array.from({ length: 24 }, (_, i) => i); } formatTime(date) { if (!date && !this.dateAdapter) return ''; const currentDate = date || this.updateDateFromSelection(); return this.dateAdapter.format(currentDate, this._displayFormat); } parseTimeString(value) { if (!this.dateAdapter) return; const date = value instanceof Date ? value : this.dateAdapter.parse(value, this._displayFormat); if (!date) return; const hours = this.dateAdapter.getHours(date); const minutes = this.dateAdapter.getMinutes(date); const seconds = this.dateAdapter.getSeconds(date); if (hours === null || minutes === null || seconds === null) return; this.selectedTime = { hour: hours, minute: minutes, second: seconds, period: hours >= 12 ? this.lang.pm : this.lang.am }; this.cdref.markForCheck(); } // Value accessors and form control get value() { return this._value; } set value(val) { this._value = val; this.updateFromValue(val); } updateFromValue(value) { if (!value) { this.resetSelection(); return; } if (value instanceof Date) { this.updateFromDate(value); } else { this.parseTimeString(value); } } updateFromDate(date) { var _a, _b; if (date && !isNaN(date.getTime()) && this.dateAdapter) { const hours = this.dateAdapter.getHours(date); if (hours === null) return; this.selectedTime = { hour: hours, minute: (_a = this.dateAdapter.getMinutes(date)) !== null && _a !== void 0 ? _a : 0, second: (_b = this.dateAdapter.getSeconds(date)) !== null && _b !== void 0 ? _b : 0, period: hours >= 12 ? this.lang.pm : this.lang.am }; } else { this.resetSelection(); } this.cdref.markForCheck(); } resetSelection() { this.selectedTime = { hour: 0, minute: 0, second: 0, period: this.lang.am }; this.cdref.markForCheck(); } writeValue(value) { if (!value) { this.value = null; return; } if (value instanceof Date) { this.value = value; } else if (value.trim()) { const date = this.selectedDate; this.value = !isNaN(date.getTime()) && this.valueType === 'date' ? date : value; this.parseTimeString(value); } this.updateTimeDisplay(); this.save(false); } registerOnChange(fn) { this.onChange = fn; } registerOnTouched(fn) { this.onTouched = fn; } // UI Event handlers handleKeydown(event) { if (event.key === 'Tab' || event.key === 'Enter') { this.handleTimeInput(); if (event.key === 'Tab') this.close(); } else if (event.key === 'Escape') { this.close(); } } handleTimeInput() { var _a; const currentValue = (_a = this.form.get('timeInput')) === null || _a === void 0 ? void 0 : _a.value; if (currentValue || (!currentValue && !this.allowEmpty)) { this.validateAndUpdateTime(currentValue); } } onFocusInput() { if (!this.isOpen) { this.open(); } } toggleTimePicker(event) { event.stopPropagation(); this.isOpen ? this.close() : this.open(); } // Picker operations open() { if (this.inline || this.disabled || this.readOnly) return; const wasOpen = this.isOpen; this.isOpen = true; this.openChange.emit(true); this.scrollToTime(); if (!wasOpen) { this.cdref.markForCheck(); } } close() { if (this.inline) return; this.cleanupTimeouts(); if (this.isOpen) { this.isOpen = false; this.openChange.emit(false); this.cdref.markForCheck(); } } // Selection methods selectHour(hour) { if (!this.isHourDisabled(hour)) { this.selectedTime.hour = hour; this.updateTimeDisplay(); this.scrollToSelectedItem(`h${hour}`); if (this.inline) this.save(); } } selectMinute(minute) { if (!this.isMinuteDisabled(minute)) { this.selectedTime.minute = minute; this.updateTimeDisplay(); this.scrollToSelectedItem(`m${minute}`); if (this.inline) this.save(); } } selectSecond(second) { if (!this.isSecondDisabled(second)) { this.selectedTime.second = second; this.updateTimeDisplay(); this.scrollToSelectedItem(`s${second}`); if (this.inline) this.save(); } } selectPeriod(period) { this.selectedTime.period = period; this.updateTimeDisplay(); } selectNow() { const now = this.selectedDate; this.selectedTime = { hour: now.getHours(), minute: now.getMinutes(), second: now.getSeconds(), period: now.getHours() >= 12 ?