@qeydar/datepicker
Version:
A comprehensive Date and Time Picker for Angular with Jalali calendar support
1,272 lines (1,263 loc) • 198 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, ElementRef, Directive, Input, HostListener, EventEmitter, forwardRef, Component, ChangeDetectionStrategy, Output, ViewChild, Inject, 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 { __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';
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';
/**
* 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%'
}))
])
]);
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 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 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 ? this.lang.pm : this.lang.am
};
this.updateTimeDisplay();
this.scrollToTime();
this.save();
}
save(close = true) {
var _a;
const date = this.updateDateFromSelection();
const { isValid, normalizedDate } = this.validateAndNormalizeTime(date);
if (!isValid || !normalizedDate)
return;
const outputValue = this.valueType === 'date'
? normalizedDate