@qeydar/datepicker
Version:
A comprehensive Date and Time Picker for Angular with Jalali calendar support
1 lines • 341 kB
Source Map (JSON)
{"version":3,"file":"qeydar-datepicker.mjs","sources":["../../../projects/qeydar-datepicker/src/utils/animation/animation-consts.ts","../../../projects/qeydar-datepicker/src/utils/animation/slide.ts","../../../projects/qeydar-datepicker/src/date-adapter.ts","../../../projects/qeydar-datepicker/src/utils/models.ts","../../../projects/qeydar-datepicker/src/date-picker.service.ts","../../../projects/qeydar-datepicker/src/utils/overlay/overlay.ts","../../../projects/qeydar-datepicker/src/utils/input-mask.directive.ts","../../../projects/qeydar-datepicker/src/time-picker/time-picker.component.ts","../../../projects/qeydar-datepicker/src/date-picker-popup/components/calendar-header.component.ts","../../../projects/qeydar-datepicker/src/date-picker-popup/components/calendar-sidebar.component.ts","../../../projects/qeydar-datepicker/src/date-picker-popup/components/days-grid.component.ts","../../../projects/qeydar-datepicker/src/date-picker-popup/components/months-grid.component.ts","../../../projects/qeydar-datepicker/src/date-picker-popup/components/years-grid.component.ts","../../../projects/qeydar-datepicker/src/date-picker-popup/components/calendar-footer.component.ts","../../../projects/qeydar-datepicker/src/date-picker-popup/services/calendar-utils.service.ts","../../../projects/qeydar-datepicker/src/date-picker-popup/services/validation-strategy.service.ts","../../../projects/qeydar-datepicker/src/date-picker-popup/services/selection-strategy.service.ts","../../../projects/qeydar-datepicker/src/date-picker-popup/date-picker-popup.component.ts","../../../projects/qeydar-datepicker/src/utils/template.directive.ts","../../../projects/qeydar-datepicker/src/date-picker.component.ts","../../../projects/qeydar-datepicker/src/qeydar-datepicker.module.ts","../../../projects/qeydar-datepicker/src/public-api.ts","../../../projects/qeydar-datepicker/src/qeydar-datepicker.ts"],"sourcesContent":["/**\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/NG-ZORRO/../blob/master/LICENSE\n */\n\nexport class AnimationDuration {\n static SLOW = '0.3s'; // Modal\n static BASE = '0.2s';\n static FAST = '0.1s'; // Tooltip\n}\n\nexport class AnimationCurves {\n static EASE_BASE_OUT = 'cubic-bezier(0.7, 0.3, 0.1, 1)';\n static EASE_BASE_IN = 'cubic-bezier(0.9, 0, 0.3, 0.7)';\n static EASE_OUT = 'cubic-bezier(0.215, 0.61, 0.355, 1)';\n static EASE_IN = 'cubic-bezier(0.55, 0.055, 0.675, 0.19)';\n static EASE_IN_OUT = 'cubic-bezier(0.645, 0.045, 0.355, 1)';\n static EASE_OUT_BACK = 'cubic-bezier(0.12, 0.4, 0.29, 1.46)';\n static EASE_IN_BACK = 'cubic-bezier(0.71, -0.46, 0.88, 0.6)';\n static EASE_IN_OUT_BACK = 'cubic-bezier(0.71, -0.46, 0.29, 1.46)';\n static EASE_OUT_CIRC = 'cubic-bezier(0.08, 0.82, 0.17, 1)';\n static EASE_IN_CIRC = 'cubic-bezier(0.6, 0.04, 0.98, 0.34)';\n static EASE_IN_OUT_CIRC = 'cubic-bezier(0.78, 0.14, 0.15, 0.86)';\n static EASE_OUT_QUINT = 'cubic-bezier(0.23, 1, 0.32, 1)';\n static EASE_IN_QUINT = 'cubic-bezier(0.755, 0.05, 0.855, 0.06)';\n static EASE_IN_OUT_QUINT = 'cubic-bezier(0.86, 0, 0.07, 1)';\n}\n","/**\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/NG-ZORRO/../blob/master/LICENSE\n */\n\nimport { animate, AnimationTriggerMetadata, state, style, transition, trigger } from '@angular/animations';\n\nimport { AnimationCurves, AnimationDuration } from './animation-consts';\n\nconst ANIMATION_TRANSITION_IN = `${AnimationDuration.BASE} ${AnimationCurves.EASE_OUT_QUINT}`;\nconst ANIMATION_TRANSITION_OUT = `${AnimationDuration.BASE} ${AnimationCurves.EASE_IN_QUINT}`;\n\nexport const slideMotion: AnimationTriggerMetadata = trigger('slideMotion', [\n state(\n 'void',\n style({\n opacity: 0,\n transform: 'scaleY(0.8)'\n })\n ),\n state(\n 'enter',\n style({\n opacity: 1,\n transform: 'scaleY(1)'\n })\n ),\n transition('void => *', [animate(ANIMATION_TRANSITION_IN)]),\n transition('* => void', [animate(ANIMATION_TRANSITION_OUT)])\n]);\n\nexport const slideAlertMotion: AnimationTriggerMetadata = trigger('slideAlertMotion', [\n transition(':leave', [\n style({ opacity: 1, transform: 'scaleY(1)', transformOrigin: '0% 0%' }),\n animate(\n `${AnimationDuration.SLOW} ${AnimationCurves.EASE_IN_OUT_CIRC}`,\n style({\n opacity: 0,\n transform: 'scaleY(0)',\n transformOrigin: '0% 0%'\n })\n )\n ])\n]);\n","import {\r\n format as formatJalali,\r\n parse as parseJalali,\r\n addDays as addDaysJalali,\r\n addMonths as addMonthsJalali,\r\n addYears as addYearsJalali,\r\n addHours as addHoursJalali,\r\n startOfWeek as startOfWeekJalali,\r\n startOfMonth as startOfMonthJalali,\r\n endOfMonth as endOfMonthJalali,\r\n isSameDay as isSameDayJalali,\r\n isSameMonth as isSameMonthJalali,\r\n isSameYear as isSameYearJalali,\r\n isAfter as isAfterJalali,\r\n isBefore as isBeforeJalali,\r\n isValid as isValidJalali,\r\n max as maxJalali,\r\n setYear as setYearJalali,\r\n getDaysInMonth as getDaysInMonthJalali\r\n} from 'date-fns-jalali';\r\n\r\nimport {\r\n format as formatGregorian,\r\n parse as parseGregorian,\r\n addDays as addDaysGregorian,\r\n addMonths as addMonthsGregorian,\r\n addYears as addYearsGregorian,\r\n addHours as addHoursGregorian,\r\n startOfWeek as startOfWeekGregorian,\r\n startOfMonth as startOfMonthGregorian,\r\n endOfMonth as endOfMonthGregorian,\r\n isSameDay as isSameDayGregorian,\r\n isSameMonth as isSameMonthGregorian,\r\n isSameYear as isSameYearGregorian,\r\n isAfter as isAfterGregorian,\r\n isBefore as isBeforeGregorian,\r\n isValid as isValidGregorian,\r\n max as maxGregorian,\r\n setYear as setYearGregorian,\r\n getDaysInMonth as getDaysInMonthGregorian,\r\n parseISO,\r\n startOfDay,\r\n isEqual,\r\n addMinutes\r\n} from 'date-fns';\r\nimport { Injectable, InjectionToken, Provider } from '@angular/core';\r\nimport { CalendarType } from './utils/types';\r\n\r\nexport interface DateAdapter<D> {\r\n today(): D;\r\n parse(value: any, formatString: string): D | null;\r\n format(date: D, formatString: string): string;\r\n addDays(date: D, amount: number): D;\r\n addMonths(date: D, amount: number): D;\r\n addYears(date: D, amount: number): D;\r\n addHours(date: D, amount: number): D;\r\n getYear(date: D): number|null;\r\n getMonth(date: D): number|null;\r\n getDate(date: D): number|null;\r\n getDayOfWeek(date: D): number;\r\n getMonthNames(style: 'long' | 'short' | 'narrow'): string[];\r\n getDateNames(): string[];\r\n getDayOfWeekNames(style: 'long' | 'short' | 'narrow'): string[];\r\n getFirstDayOfWeek(): number;\r\n getNumDaysInMonth(date: D): number;\r\n clone(date: D): D;\r\n createDate(year: number, month: number, date: number): D;\r\n isSameDay(date1: D, date2: D): boolean;\r\n isSameMonth(date1: D, date2: D): boolean;\r\n isSameYear(date1: D, date2: D): boolean;\r\n isAfter(date1: D, date2: D): boolean;\r\n isBefore(date1: D, date2: D): boolean;\r\n isEqual(date1: D, date2: D): boolean;\r\n startOfMonth(date: D): D;\r\n endOfMonth(date: D): D;\r\n startOfWeek(date: D): D;\r\n isValidFormat(dateString: string, formatString: string): boolean;\r\n max(dates: D[]): D;\r\n setYear(date: D, year: number): D;\r\n startOfDay (date: D): D;\r\n getHours(date: D): number|null;\r\n getMinutes(date: D): number|null;\r\n getSeconds(date: D): number|null;\r\n setHours(date: D, hours: number): D;\r\n setMinutes(date: D, minutes: number): D;\r\n setSeconds(date: D, seconds: number): D;\r\n getDaysInMonth(date: D): number;\r\n addMinutes(date: D, amount: number): D;\r\n}\r\n\r\n// Injection token for date adapter\r\nexport const DATE_ADAPTER = new InjectionToken<DateAdapter<Date>>('DateAdapter');\r\n\r\n// Provider factory for default adapters\r\nexport function provideDateAdapter(calendarType: CalendarType): Provider {\r\n return {\r\n provide: DATE_ADAPTER,\r\n useFactory: (jalali: JalaliDateAdapter, gregorian: GregorianDateAdapter) => {\r\n return calendarType === 'jalali' ? jalali : gregorian;\r\n },\r\n deps: [JalaliDateAdapter, GregorianDateAdapter]\r\n };\r\n}\r\n\r\n@Injectable({\r\n providedIn: 'root'\r\n})\r\nexport class JalaliDateAdapter implements DateAdapter<Date> {\r\n today(): Date {\r\n return new Date();\r\n }\r\n\r\n parse(value: any, formatString: string): Date | null {\r\n if (typeof value === 'string') {\r\n // Check if it's in ISO 8601 format\r\n if (value.includes('T')) {\r\n const parsedDate = parseISO(value);\r\n return isValidJalali(parsedDate) ? parsedDate : null;\r\n }\r\n\r\n try {\r\n const parsedDate = parseJalali(value, formatString, new Date());\r\n return isValidJalali(parsedDate) ? parsedDate : null;\r\n } catch (error) {\r\n console.error('Error parsing date:', error);\r\n return null;\r\n }\r\n } else if (value instanceof Date) {\r\n return isValidJalali(value) ? value : null;\r\n }\r\n return null;\r\n }\r\n\r\n format(date: Date, formatString: string): string {\r\n return formatJalali(date, formatString);\r\n }\r\n\r\n addDays(date: Date, amount: number): Date {\r\n return addDaysJalali(date, amount);\r\n }\r\n\r\n addMonths(date: Date, amount: number): Date {\r\n return addMonthsJalali(date, amount);\r\n }\r\n\r\n addYears(date: Date, amount: number): Date {\r\n return addYearsJalali(date, amount);\r\n }\r\n\r\n addHours(date: Date, amount: number): Date {\r\n return addHoursJalali(date, amount);\r\n }\r\n\r\n getYear(date: Date): number|null {\r\n return date? parseInt(formatJalali(date, 'yyyy')): null;\r\n }\r\n\r\n getMonth(date: Date): number|null {\r\n // Jalali months are 1-indexed in date-fns-jalali\r\n return date? parseInt(formatJalali(date, 'M')) - 1: null;\r\n }\r\n\r\n getDate(date: Date): number|null {\r\n return date? parseInt(formatJalali(date, 'dd')): null;\r\n }\r\n\r\n getDayOfWeek(date: Date): number {\r\n return parseInt(formatJalali(date, 'i')) - 1;\r\n }\r\n\r\n getMonthNames(style: 'long' | 'short' | 'narrow'): string[] {\r\n const jalaliMonths = [\r\n 'فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور',\r\n 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند'\r\n ];\r\n\r\n switch (style) {\r\n case 'long':\r\n return jalaliMonths;\r\n case 'short':\r\n return jalaliMonths.map(month => month.substring(0, 3));\r\n case 'narrow':\r\n return jalaliMonths.map(month => month.substring(0, 1));\r\n default:\r\n return jalaliMonths;\r\n }\r\n }\r\n\r\n getDateNames(): string[] {\r\n return Array.from({ length: 31 }, (_, i) => (i + 1).toString());\r\n }\r\n\r\n getDayOfWeekNames(style: 'long' | 'short' | 'narrow'): string[] {\r\n const formats = {\r\n long: 'EEEE',\r\n short: 'EEEEE',\r\n narrow: 'EEEEEE'\r\n };\r\n return Array.from({ length: 7 }, (_, i) =>\r\n formatJalali(addDaysJalali(startOfWeekJalali(new Date()), i), formats[style])\r\n );\r\n }\r\n\r\n getFirstDayOfWeek(): number {\r\n return 6; // Saturday is the first day of the week in the Jalali calendar\r\n }\r\n\r\n getNumDaysInMonth(date: Date): number {\r\n return parseInt(formatJalali(endOfMonthJalali(date), 'd'));\r\n }\r\n\r\n clone(date: Date): Date {\r\n return new Date(date.getTime());\r\n }\r\n\r\n createDate(year: number, month: number, date: number): Date {\r\n // Adjust for 0-indexed months in the interface vs 1-indexed months in date-fns-jalali\r\n return parseJalali(`${year}/${month + 1}/${date}`, 'yyyy/M/d', new Date());\r\n }\r\n\r\n isSameDay(date1: Date, date2: Date): boolean {\r\n return isSameDayJalali(date1, date2);\r\n }\r\n\r\n isSameMonth(date1: Date, date2: Date): boolean {\r\n return isSameMonthJalali(date1, date2);\r\n }\r\n\r\n isSameYear(date1: Date, date2: Date): boolean {\r\n return isSameYearJalali(date1, date2);\r\n }\r\n\r\n isAfter(date1: Date, date2: Date): boolean {\r\n return isAfterJalali(date1, date2);\r\n }\r\n\r\n isBefore(date1: Date, date2: Date): boolean {\r\n return isBeforeJalali(date1, date2);\r\n }\r\n\r\n isEqual(date1: Date, date2: Date): boolean {\r\n return isEqual(date1, date2);\r\n }\r\n\r\n startOfMonth(date: Date): Date {\r\n return startOfMonthJalali(date);\r\n }\r\n\r\n endOfMonth(date: Date): Date {\r\n return endOfMonthJalali(date);\r\n }\r\n\r\n startOfWeek(date: Date): Date {\r\n return startOfWeekJalali(date, { weekStartsOn: this.getFirstDayOfWeek() as 0 | 1 | 2 | 3 | 4 | 5 | 6 });\r\n }\r\n\r\n isValidFormat(dateString: string, formatString: string): boolean {\r\n try {\r\n const parsedDate = parseJalali(dateString, formatString, new Date());\r\n if (!isValidJalali(parsedDate)) {\r\n return false;\r\n }\r\n // Check if the formatted parsed date matches the original date string\r\n const formattedDate = formatJalali(parsedDate, formatString);\r\n return formattedDate === dateString;\r\n } catch (error) {\r\n return false;\r\n }\r\n }\r\n\r\n max(dates: Date[]): Date {\r\n return maxJalali(dates);\r\n }\r\n\r\n setYear(date: Date, year: number): Date {\r\n return setYearJalali(date, year)\r\n }\r\n\r\n startOfDay(date: Date): Date {\r\n return startOfDay(date);\r\n }\r\n\r\n getHours(date: Date): number|null {\r\n return date? parseInt(formatJalali(date, 'HH')): null;\r\n }\r\n\r\n getMinutes(date: Date): number|null {\r\n return date? parseInt(formatJalali(date, 'mm')): null;\r\n }\r\n\r\n getSeconds(date: Date): number|null {\r\n return date? parseInt(formatJalali(date, 'ss')): null;\r\n }\r\n\r\n setHours(date: Date, hours: number): Date {\r\n const newDate = this.clone(date);\r\n newDate.setHours(hours);\r\n return newDate;\r\n }\r\n\r\n setMinutes(date: Date, minutes: number): Date {\r\n const newDate = this.clone(date);\r\n newDate.setMinutes(minutes);\r\n return newDate;\r\n }\r\n\r\n setSeconds(date: Date, seconds: number): Date {\r\n const newDate = this.clone(date);\r\n newDate.setSeconds(seconds);\r\n return newDate;\r\n }\r\n\r\n getDaysInMonth(date: Date) {\r\n return getDaysInMonthJalali(date);\r\n }\r\n\r\n addMinutes(date: Date, amount: number) {\r\n return addMinutes(date, amount);\r\n }\r\n}\r\n\r\n@Injectable({\r\n providedIn: 'root'\r\n})\r\nexport class GregorianDateAdapter implements DateAdapter<Date> {\r\n today(): Date {\r\n return new Date();\r\n }\r\n\r\n parse(value: any, formatString: string): Date | null {\r\n if (typeof value === 'string') {\r\n // Check if it's in ISO 8601 format\r\n if (value.includes('T')) {\r\n const parsedDate = parseISO(value);\r\n return isValidGregorian(parsedDate) ? parsedDate : null;\r\n }\r\n\r\n try {\r\n let parsedDate: Date;\r\n if (formatString === \"ISO\") {\r\n parsedDate = parseISO(value);\r\n } else {\r\n parsedDate = parseGregorian(value, formatString, new Date());\r\n }\r\n return isValidGregorian(parsedDate) ? parsedDate : null;\r\n } catch (error) {\r\n console.error('Error parsing date:', error);\r\n return null;\r\n }\r\n } else if (value instanceof Date) {\r\n return isValidGregorian(value) ? value : null;\r\n }\r\n return null;\r\n }\r\n\r\n format(date: Date, formatString: string): string {\r\n return formatGregorian(date, formatString);\r\n }\r\n\r\n addDays(date: Date, amount: number): Date {\r\n return addDaysGregorian(date, amount);\r\n }\r\n\r\n addMonths(date: Date, amount: number): Date {\r\n return addMonthsGregorian(date, amount);\r\n }\r\n\r\n addYears(date: Date, amount: number): Date {\r\n return addYearsGregorian(date, amount);\r\n }\r\n\r\n addHours(date: Date, amount: number): Date {\r\n return addHoursGregorian(date, amount);\r\n }\r\n\r\n getYear(date: Date): number {\r\n return date.getFullYear();\r\n }\r\n\r\n getMonth(date: Date): number {\r\n return date.getMonth();\r\n }\r\n\r\n getDate(date: Date): number {\r\n return date.getDate();\r\n }\r\n\r\n getDayOfWeek(date: Date): number {\r\n return date.getDay();\r\n }\r\n\r\n getMonthNames(style: 'long' | 'short' | 'narrow'): string[] {\r\n const formats = {\r\n long: 'MMMM',\r\n short: 'MMM',\r\n narrow: 'MMMMM'\r\n };\r\n return Array.from({ length: 12 }, (_, i) =>\r\n formatGregorian(new Date(2000, i, 1), formats[style])\r\n );\r\n }\r\n\r\n getDateNames(): string[] {\r\n return Array.from({ length: 31 }, (_, i) => (i + 1).toString());\r\n }\r\n\r\n getDayOfWeekNames(style: 'long' | 'short' | 'narrow'): string[] {\r\n const formats = {\r\n long: 'EEEE',\r\n short: 'EEE',\r\n narrow: 'EEEEE'\r\n };\r\n return Array.from({ length: 7 }, (_, i) =>\r\n formatGregorian(addDaysGregorian(startOfWeekGregorian(new Date()), i), formats[style])\r\n );\r\n }\r\n\r\n getFirstDayOfWeek(): number {\r\n return 0; // Sunday is the first day of the week in the Gregorian calendar\r\n }\r\n\r\n getNumDaysInMonth(date: Date): number {\r\n return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();\r\n }\r\n\r\n clone(date: Date): Date {\r\n return new Date(date.getTime());\r\n }\r\n\r\n createDate(year: number, month: number, date: number): Date {\r\n return new Date(year, month, date);\r\n }\r\n\r\n isSameDay(date1: Date, date2: Date): boolean {\r\n return isSameDayGregorian(date1, date2);\r\n }\r\n\r\n isSameMonth(date1: Date, date2: Date): boolean {\r\n return isSameMonthGregorian(date1, date2);\r\n }\r\n\r\n isSameYear(date1: Date, date2: Date): boolean {\r\n return isSameYearGregorian(date1, date2);\r\n }\r\n\r\n isAfter(date1: Date, date2: Date): boolean {\r\n return isAfterGregorian(date1, date2);\r\n }\r\n\r\n isBefore(date1: Date, date2: Date): boolean {\r\n return isBeforeGregorian(date1, date2);\r\n }\r\n\r\n isEqual(date1: Date, date2: Date): boolean {\r\n return isEqual(date1, date2);\r\n }\r\n\r\n startOfMonth(date: Date): Date {\r\n return startOfMonthGregorian(date);\r\n }\r\n\r\n endOfMonth(date: Date): Date {\r\n return endOfMonthGregorian(date);\r\n }\r\n\r\n startOfWeek(date: Date): Date {\r\n return startOfWeekGregorian(date, { weekStartsOn: this.getFirstDayOfWeek() as 0 | 1 | 2 | 3 | 4 | 5 | 6 });\r\n }\r\n\r\n isValidFormat(dateString: string, formatString: string): boolean {\r\n try {\r\n const parsedDate = parseGregorian(dateString, formatString, new Date());\r\n if (!isValidGregorian(parsedDate)) {\r\n return false;\r\n }\r\n // Check if the formatted parsed date matches the original date string\r\n const formattedDate = formatGregorian(parsedDate, formatString);\r\n return formattedDate === dateString;\r\n } catch (error) {\r\n return false;\r\n }\r\n }\r\n\r\n max(dates: Date[]): Date {\r\n return maxGregorian(dates);\r\n }\r\n\r\n setYear(date: Date, year: number): Date {\r\n return setYearGregorian(date, year);\r\n }\r\n\r\n startOfDay(date: Date): Date {\r\n return startOfDay(date);\r\n }\r\n\r\n getHours(date: Date): number|null {\r\n return date? date.getHours(): null;\r\n }\r\n\r\n getMinutes(date: Date): number|null {\r\n return date? date.getMinutes(): null;\r\n }\r\n\r\n getSeconds(date: Date): number|null {\r\n return date? date.getSeconds(): null;\r\n }\r\n\r\n setHours(date: Date, hours: number): Date {\r\n const newDate = this.clone(date);\r\n newDate.setHours(hours);\r\n return newDate;\r\n }\r\n\r\n setMinutes(date: Date, minutes: number): Date {\r\n const newDate = this.clone(date);\r\n newDate.setMinutes(minutes);\r\n return newDate;\r\n }\r\n\r\n setSeconds(date: Date, seconds: number): Date {\r\n const newDate = this.clone(date);\r\n newDate.setSeconds(seconds);\r\n return newDate;\r\n }\r\n\r\n getDaysInMonth(date: Date) {\r\n return getDaysInMonthGregorian(date);\r\n }\r\n\r\n addMinutes(date: Date, amount: number) {\r\n return addMinutes(date, amount);\r\n }\r\n}","import { Injectable } from \"@angular/core\";\r\n\r\nexport interface CustomLabels {\r\n label: string,\r\n value: Array<Date> | 'custom',\r\n arrow?: boolean\r\n}\r\n\r\nexport interface YearRange {\r\n start: number,\r\n end: number\r\n}\r\n\r\nexport interface DateRange {\r\n start: Date | string,\r\n end: Date | string\r\n}\r\n\r\nexport interface RangeInputLabels {\r\n start: string,\r\n end: string,\r\n}\r\n\r\nexport interface Lang_Locale {\r\n today: string;\r\n lastDay: string;\r\n lastWeek: string;\r\n lastMonth: string;\r\n custom: string;\r\n previousMonth: string;\r\n nextMonth: string;\r\n previousYear: string;\r\n nextYear: string;\r\n selectTime: string;\r\n selectDate: string;\r\n selectMonth: string;\r\n selectYear: string;\r\n selectDateRange: string;\r\n startDate: string;\r\n endDate: string;\r\n pm: string;\r\n am: string;\r\n ok: string;\r\n cancel: string;\r\n now: string;\r\n}\r\n\r\n@Injectable({\r\n providedIn: 'root'\r\n})\r\nexport class lang_Fa implements Lang_Locale{\r\n today:string = \"امروز\";\r\n lastDay:string = \"آخرین روز\";\r\n lastWeek:string = \"آخرین هفته\";\r\n lastMonth:string = \"آخرین ماه\";\r\n custom:string = \"دلخواه\";\r\n previousMonth:string = \"ماه قبل\";\r\n nextMonth:string = \"ماه بعد\";\r\n previousYear:string = \"سال قبل\";\r\n nextYear:string = \"سال بعد\";\r\n selectTime:string = \"انتخاب زمان\";\r\n selectDate:string = \"انتخاب تاریخ\";\r\n selectMonth:string = \"انتخاب ماه\";\r\n selectYear:string = \"انتخاب سال\";\r\n selectDateRange:string = \"انتخاب محدوده تاریخ\";\r\n startDate:string = \"از تاریخ\";\r\n endDate:string = \"تا تاریخ\";\r\n pm: string = \"ب.ظ\";\r\n am: string = \"ق.ظ\";\r\n ok: string = \"تایید\";\r\n cancel: string = \"لغو\";\r\n now: string = \"اکنون\";\r\n}\r\n\r\n@Injectable({\r\n providedIn: 'root'\r\n})\r\nexport class lang_En implements Lang_Locale{\r\n today:string = \"Today\"\r\n lastDay:string = \"Last Day\";\r\n lastWeek:string = \"Last Week\";\r\n lastMonth:string = \"Last Month\";\r\n custom:string = \"Custom\";\r\n previousMonth:string = \"Previous Month\";\r\n nextMonth:string = \"Next Month\";\r\n previousYear:string = \"Previous Year\";\r\n nextYear:string = \"Next Year\";\r\n selectTime:string = \"Select time\";\r\n selectDate:string = \"Select date\";\r\n selectMonth:string = \"Select month\";\r\n selectYear:string = \"Select year\";\r\n selectDateRange:string = \"Select date range\";\r\n startDate:string = \"Start date\";\r\n endDate:string = \"End date\";\r\n pm: string = \"PM\";\r\n am: string = \"AM\";\r\n ok: string = \"Ok\";\r\n cancel: string = \"Cancel\";\r\n now: string = \"Now\";\r\n}","import { Injectable, OnDestroy } from \"@angular/core\";\r\nimport { BehaviorSubject, Subject } from \"rxjs\";\r\nimport { lang_En, lang_Fa, Lang_Locale } from \"./utils/models\";\r\n\r\nexport interface ValidTimeResult {\r\n isValid: boolean;\r\n normalizedTime: string;\r\n}\r\n\r\n@Injectable()\r\nexport class QeydarDatePickerService {\r\n activeInput$: BehaviorSubject<string> = new BehaviorSubject('');\r\n locale: Lang_Locale;\r\n /**\r\n *\r\n */\r\n constructor(public locale_fa: lang_Fa, public locale_en: lang_En) {\r\n }\r\n\r\n getActiveInputValue() {\r\n return this.activeInput$.getValue();\r\n }\r\n}\r\n\r\n@Injectable()\r\nexport class DestroyService extends Subject<void> implements OnDestroy {\r\n ngOnDestroy(): void {\r\n this.next();\r\n this.complete();\r\n }\r\n}","/**\r\n * Use of this source code is governed by an MIT-style license that can be\r\n * found in the LICENSE file at https://github.com/NG-ZORRO/../blob/master/LICENSE\r\n */\r\n\r\nimport {\r\n CdkConnectedOverlay,\r\n CdkOverlayOrigin,\r\n ConnectedOverlayPositionChange,\r\n FlexibleConnectedPositionStrategyOrigin,\r\n ConnectionPositionPair \r\n} from '@angular/cdk/overlay';\r\nimport { coerceBooleanProperty, _isNumberValue } from '@angular/cdk/coercion';\r\nimport { Directive, ElementRef, Input } from '@angular/core';\r\nimport { takeUntil } from 'rxjs/operators';\r\nimport { DestroyService } from '../../date-picker.service';\r\n\r\nexport type SafeAny = any;\r\n\r\nfunction propDecoratorFactory<T, D>(\r\n name: string,\r\n fallback: (v: T) => D\r\n): (target: SafeAny, propName: string) => void {\r\n function propDecorator(\r\n target: SafeAny,\r\n propName: string,\r\n originalDescriptor?: TypedPropertyDescriptor<SafeAny>\r\n ): SafeAny {\r\n const privatePropName = `$$__zorroPropDecorator__${propName}`;\r\n\r\n if (Object.prototype.hasOwnProperty.call(target, privatePropName)) {\r\n console.warn(`The prop \"${privatePropName}\" is already exist, it will be overrided by ${name} decorator.`);\r\n }\r\n\r\n Object.defineProperty(target, privatePropName, {\r\n configurable: true,\r\n writable: true\r\n });\r\n\r\n return {\r\n get(): string {\r\n return originalDescriptor && originalDescriptor.get\r\n ? originalDescriptor.get.bind(this)()\r\n : this[privatePropName];\r\n },\r\n set(value: T): void {\r\n if (originalDescriptor && originalDescriptor.set) {\r\n originalDescriptor.set.bind(this)(fallback(value));\r\n }\r\n this[privatePropName] = fallback(value);\r\n }\r\n };\r\n }\r\n\r\n return propDecorator;\r\n}\r\n\r\n/**\r\n * Input decorator that handle a prop to do get/set automatically with toBoolean\r\n *\r\n * Why not using @InputBoolean alone without @Input? AOT needs @Input to be visible\r\n *\r\n * @howToUse\r\n * ```\r\n * @Input() @InputBoolean() visible: boolean = false;\r\n *\r\n * // Act as below:\r\n * // @Input()\r\n * // get visible() { return this.__visible; }\r\n * // set visible(value) { this.__visible = value; }\r\n * // __visible = false;\r\n * ```\r\n */\r\nexport function InputBoolean(): any {\r\n return propDecoratorFactory('InputBoolean', toBoolean);\r\n}\r\n\r\nexport function toBoolean(value: boolean | string): boolean {\r\n return coerceBooleanProperty(value);\r\n}\r\n\r\n/** Equivalent of `ClientRect` without some of the properties we don't care about. */\r\ntype Dimensions = Omit<ClientRect, 'x' | 'y' | 'toJSON'>;\r\n\r\n@Directive({\r\n selector: '[cdkConnectedOverlay][nzConnectedOverlay]',\r\n exportAs: 'nzConnectedOverlay',\r\n standalone: true,\r\n providers: [DestroyService]\r\n})\r\nexport class NzConnectedOverlayDirective {\r\n @Input() @InputBoolean() nzArrowPointAtCenter: boolean = false;\r\n\r\n constructor(\r\n private readonly cdkConnectedOverlay: CdkConnectedOverlay,\r\n private readonly nzDestroyService: DestroyService\r\n ) {\r\n this.cdkConnectedOverlay.backdropClass = 'nz-overlay-transparent-backdrop';\r\n\r\n this.cdkConnectedOverlay.positionChange\r\n .pipe(takeUntil(this.nzDestroyService))\r\n .subscribe((position: ConnectedOverlayPositionChange) => {\r\n if (this.nzArrowPointAtCenter) {\r\n this.updateArrowPosition(position);\r\n }\r\n });\r\n }\r\n\r\n private updateArrowPosition(position: ConnectedOverlayPositionChange): void {\r\n const originRect = this.getOriginRect();\r\n const placement = getPlacementName(position);\r\n\r\n let offsetX: number | undefined = 0;\r\n let offsetY: number | undefined = 0;\r\n\r\n if (placement === 'topLeft' || placement === 'bottomLeft') {\r\n offsetX = originRect.width / 2 - 14;\r\n } else if (placement === 'topRight' || placement === 'bottomRight') {\r\n offsetX = -(originRect.width / 2 - 14);\r\n } else if (placement === 'leftTop' || placement === 'rightTop') {\r\n offsetY = originRect.height / 2 - 10;\r\n } else if (placement === 'leftBottom' || placement === 'rightBottom') {\r\n offsetY = -(originRect.height / 2 - 10);\r\n }\r\n\r\n if (this.cdkConnectedOverlay.offsetX !== offsetX || this.cdkConnectedOverlay.offsetY !== offsetY) {\r\n this.cdkConnectedOverlay.offsetY = offsetY;\r\n this.cdkConnectedOverlay.offsetX = offsetX;\r\n this.cdkConnectedOverlay.overlayRef.updatePosition();\r\n } \r\n }\r\n\r\n private getFlexibleConnectedPositionStrategyOrigin(): FlexibleConnectedPositionStrategyOrigin {\r\n if (this.cdkConnectedOverlay.origin instanceof CdkOverlayOrigin) {\r\n return this.cdkConnectedOverlay.origin.elementRef;\r\n } else {\r\n return this.cdkConnectedOverlay.origin;\r\n }\r\n }\r\n\r\n private getOriginRect(): Dimensions {\r\n const origin = this.getFlexibleConnectedPositionStrategyOrigin();\r\n\r\n if (origin instanceof ElementRef) {\r\n return origin.nativeElement.getBoundingClientRect();\r\n }\r\n\r\n // Check for Element so SVG elements are also supported.\r\n if (origin instanceof Element) {\r\n return origin.getBoundingClientRect();\r\n }\r\n\r\n const width = origin.width || 0;\r\n const height = origin.height || 0;\r\n \r\n // If the origin is a point, return a client rect as if it was a 0x0 element at the point.\r\n return {\r\n top: origin.y,\r\n bottom: origin.y + height,\r\n left: origin.x,\r\n right: origin.x + width,\r\n height,\r\n width\r\n };\r\n }\r\n}\r\n\r\n//overlay-position.ts:\r\nexport const POSITION_MAP = {\r\n top: new ConnectionPositionPair({ originX: 'center', originY: 'top' }, { overlayX: 'center', overlayY: 'bottom' }),\r\n topCenter: new ConnectionPositionPair(\r\n { originX: 'center', originY: 'top' },\r\n { overlayX: 'center', overlayY: 'bottom' }\r\n ),\r\n topLeft: new ConnectionPositionPair({ originX: 'start', originY: 'top' }, { overlayX: 'start', overlayY: 'bottom' }),\r\n topRight: new ConnectionPositionPair({ originX: 'end', originY: 'top' }, { overlayX: 'end', overlayY: 'bottom' }),\r\n right: new ConnectionPositionPair({ originX: 'end', originY: 'center' }, { overlayX: 'start', overlayY: 'center' }),\r\n rightTop: new ConnectionPositionPair({ originX: 'end', originY: 'top' }, { overlayX: 'start', overlayY: 'top' }),\r\n rightBottom: new ConnectionPositionPair(\r\n { originX: 'end', originY: 'bottom' },\r\n { overlayX: 'start', overlayY: 'bottom' }\r\n ),\r\n bottom: new ConnectionPositionPair({ originX: 'center', originY: 'bottom' }, { overlayX: 'center', overlayY: 'top' }),\r\n bottomCenter: new ConnectionPositionPair(\r\n { originX: 'center', originY: 'bottom' },\r\n { overlayX: 'center', overlayY: 'top' }\r\n ),\r\n bottomLeft: new ConnectionPositionPair(\r\n { originX: 'start', originY: 'bottom' },\r\n { overlayX: 'start', overlayY: 'top' }\r\n ),\r\n bottomRight: new ConnectionPositionPair({ originX: 'end', originY: 'bottom' }, { overlayX: 'end', overlayY: 'top' }),\r\n left: new ConnectionPositionPair({ originX: 'start', originY: 'center' }, { overlayX: 'end', overlayY: 'center' }),\r\n leftTop: new ConnectionPositionPair({ originX: 'start', originY: 'top' }, { overlayX: 'end', overlayY: 'top' }),\r\n leftBottom: new ConnectionPositionPair(\r\n { originX: 'start', originY: 'bottom' },\r\n { overlayX: 'end', overlayY: 'bottom' }\r\n )\r\n };\r\nexport type POSITION_TYPE = keyof typeof POSITION_MAP;\r\nexport type POSITION_TYPE_HORIZONTAL = Extract<\r\n POSITION_TYPE,\r\n 'bottomLeft' | 'bottomCenter' | 'bottomRight' | 'topLeft' | 'topCenter' | 'topRight'\r\n>;\r\n\r\nexport const DEFAULT_TOOLTIP_POSITIONS = [POSITION_MAP.top, POSITION_MAP.right, POSITION_MAP.bottom, POSITION_MAP.left];\r\n\r\nexport const DEFAULT_CASCADER_POSITIONS = [\r\n POSITION_MAP.bottomLeft,\r\n POSITION_MAP.bottomRight,\r\n POSITION_MAP.topLeft,\r\n POSITION_MAP.topRight,\r\n POSITION_MAP.topCenter,\r\n POSITION_MAP.bottomCenter\r\n];\r\n\r\nexport const DEFAULT_MENTION_TOP_POSITIONS = [\r\n new ConnectionPositionPair({ originX: 'start', originY: 'bottom' }, { overlayX: 'start', overlayY: 'bottom' }),\r\n new ConnectionPositionPair({ originX: 'start', originY: 'bottom' }, { overlayX: 'end', overlayY: 'bottom' })\r\n];\r\n\r\nexport const DEFAULT_MENTION_BOTTOM_POSITIONS = [\r\n POSITION_MAP.bottomLeft,\r\n new ConnectionPositionPair({ originX: 'start', originY: 'bottom' }, { overlayX: 'end', overlayY: 'top' })\r\n];\r\n\r\nexport function getPlacementName(position: ConnectedOverlayPositionChange): string | undefined {\r\n for (const placement in POSITION_MAP) {\r\n if (\r\n position.connectionPair.originX === POSITION_MAP[placement as POSITION_TYPE].originX &&\r\n position.connectionPair.originY === POSITION_MAP[placement as POSITION_TYPE].originY &&\r\n position.connectionPair.overlayX === POSITION_MAP[placement as POSITION_TYPE].overlayX &&\r\n position.connectionPair.overlayY === POSITION_MAP[placement as POSITION_TYPE].overlayY\r\n ) {\r\n return placement;\r\n }\r\n }\r\n return undefined;\r\n}\r\n\r\nexport const DATE_PICKER_POSITION_MAP = {\r\n bottomLeft: new ConnectionPositionPair(\r\n { originX: 'start', originY: 'bottom' },\r\n { overlayX: 'start', overlayY: 'top' },\r\n undefined,\r\n 2\r\n ),\r\n topLeft: new ConnectionPositionPair(\r\n { originX: 'start', originY: 'top' },\r\n { overlayX: 'start', overlayY: 'bottom' },\r\n undefined,\r\n -2\r\n ),\r\n bottomRight: new ConnectionPositionPair(\r\n { originX: 'end', originY: 'bottom' },\r\n { overlayX: 'end', overlayY: 'top' },\r\n undefined,\r\n 2\r\n ),\r\n topRight: new ConnectionPositionPair(\r\n { originX: 'end', originY: 'top' },\r\n { overlayX: 'end', overlayY: 'bottom' },\r\n undefined,\r\n -2\r\n )\r\n};\r\n\r\nexport const DEFAULT_DATE_PICKER_POSITIONS = [\r\n DATE_PICKER_POSITION_MAP.bottomLeft,\r\n DATE_PICKER_POSITION_MAP.topLeft,\r\n DATE_PICKER_POSITION_MAP.bottomRight,\r\n DATE_PICKER_POSITION_MAP.topRight\r\n];\r\n ","import { Directive, ElementRef, HostListener, Input } from '@angular/core';\r\n\r\n@Directive({\r\n selector: '[qeydar-dateMask]',\r\n standalone: true\r\n})\r\nexport class DateMaskDirective {\r\n @Input('qeydar-dateMask') dateFormat: string = 'yyyy/MM/dd';\r\n @Input() disableInputMask = false;\r\n\r\n delimiters: string[] = [];\r\n parts: string[] = [];\r\n lastValue: string = '';\r\n\r\n constructor(public el: ElementRef) {}\r\n\r\n ngOnInit() {\r\n this.parseFormat();\r\n }\r\n\r\n parseFormat() {\r\n if (this.disableInputMask)\r\n return;\r\n\r\n this.parts = [];\r\n this.delimiters = [];\r\n let currentPart = '';\r\n \r\n for (let i = 0; i < this.dateFormat.length; i++) {\r\n const char = this.dateFormat[i];\r\n \r\n if (this.isFormatChar(char)) {\r\n currentPart += char;\r\n } else {\r\n if (currentPart) {\r\n this.parts.push(currentPart);\r\n currentPart = '';\r\n }\r\n this.delimiters.push(char);\r\n }\r\n }\r\n \r\n if (currentPart) {\r\n this.parts.push(currentPart);\r\n }\r\n }\r\n\r\n isFormatChar(char: string): boolean {\r\n return /[yMdHhmsa]/i.test(char);\r\n }\r\n\r\n @HostListener('input', ['$event'])\r\n onInput(event: InputEvent) {\r\n if (this.disableInputMask)\r\n return;\r\n\r\n const input = event.target as HTMLInputElement;\r\n const cursorPosition = input.selectionStart || 0;\r\n let value = input.value.replace(/[^0-9APMapm\\s:/\\-\\.]/g, '');\r\n \r\n // Allow backspace/delete\r\n if (value.length < this.lastValue.length) {\r\n this.lastValue = value;\r\n return;\r\n }\r\n\r\n let formattedParts: string[] = [];\r\n let currentValue = value;\r\n let shouldAddDelimiter = false;\r\n let totalLength = 0;\r\n let newCursorPosition = cursorPosition;\r\n\r\n for (let i = 0; i < this.parts.length; i++) {\r\n const part = this.extractPart(currentValue, this.parts[i]);\r\n if (!part && part !== '0') break;\r\n\r\n const expectedLength = this.getPartLength(this.parts[i]);\r\n let formattedPart = part;\r\n\r\n if (formattedPart.length >= expectedLength) {\r\n formattedPart = this.validatePart(formattedPart.slice(0, expectedLength), this.parts[i]);\r\n shouldAddDelimiter = true;\r\n }\r\n\r\n formattedParts.push(formattedPart);\r\n totalLength += formattedPart.length;\r\n\r\n if (shouldAddDelimiter && i < this.parts.length - 1) {\r\n formattedParts.push(this.delimiters[i] || '');\r\n totalLength += 1;\r\n shouldAddDelimiter = false;\r\n\r\n if (cursorPosition === totalLength - 1) {\r\n newCursorPosition = totalLength;\r\n }\r\n }\r\n\r\n currentValue = this.removeProcessedPart(currentValue, part);\r\n }\r\n\r\n const formattedValue = formattedParts.join('');\r\n input.value = formattedValue;\r\n\r\n // Set cursor position\r\n newCursorPosition = Math.min(newCursorPosition, totalLength);\r\n input.setSelectionRange(newCursorPosition, newCursorPosition);\r\n\r\n this.lastValue = formattedValue;\r\n }\r\n\r\n extractPart(value: string, format: string): string {\r\n if (!value) return '';\r\n\r\n if (format[0].toLowerCase() === 'a') {\r\n // Handle AM/PM\r\n const match = value.match(/^[AaPp][Mm]?/);\r\n return match ? match[0].toUpperCase() : '';\r\n }\r\n\r\n // Handle numeric parts\r\n const match = value.match(/^\\d+/);\r\n return match ? match[0] : '';\r\n }\r\n\r\n removeProcessedPart(value: string, part: string): string {\r\n if (!part) return value;\r\n \r\n // Remove part and following delimiter if exists\r\n const remainingValue = value.slice(part.length);\r\n return remainingValue.replace(/^[:/\\s-]/, '');\r\n }\r\n\r\n @HostListener('keydown', ['$event'])\r\n onKeyDown(event: KeyboardEvent) {\r\n if (this.disableInputMask)\r\n return;\r\n\r\n const input = event.target as HTMLInputElement;\r\n const cursorPosition = input.selectionStart || 0;\r\n \r\n // Allow control keys\r\n if (event.key === 'Backspace' || event.key === 'Delete' || \r\n event.key === 'ArrowLeft' || event.key === 'ArrowRight' || \r\n event.key === 'Tab' || event.ctrlKey) {\r\n return;\r\n }\r\n\r\n const currentPartIndex = this.getCurrentPartIndex(input.value, cursorPosition);\r\n if (currentPartIndex === -1) return;\r\n\r\n const currentFormat = this.parts[currentPartIndex];\r\n const isTimeDelimiter = event.key === ':' && cursorPosition > 0 && \r\n (this.parts[currentPartIndex - 1]?.includes('H') || \r\n this.parts[currentPartIndex - 1]?.includes('h'));\r\n\r\n // Allow time delimiter after hours\r\n if (isTimeDelimiter) {\r\n if (this.delimiters[currentPartIndex - 1] === ':') {\r\n const parts = input.value.split(/[:/\\s-]/);\r\n const currentPart = this.validatePart(parts[currentPartIndex - 1], this.parts[currentPartIndex - 1]);\r\n parts[currentPartIndex - 1] = currentPart;\r\n \r\n const newValue = parts.slice(0, currentPartIndex).join(this.delimiters[currentPartIndex - 1]) + ':';\r\n input.value = newValue + parts.slice(currentPartIndex).join(this.delimiters[currentPartIndex]);\r\n \r\n input.setSelectionRange(newValue.length, newValue.length);\r\n event.preventDefault();\r\n }\r\n return;\r\n }\r\n\r\n // Handle AM/PM input\r\n if (currentFormat[0].toLowerCase() === 'a') {\r\n if (!/^[AaPpMm]$/.test(event.key)) {\r\n event.preventDefault();\r\n }\r\n return;\r\n }\r\n\r\n // Allow only digits for other parts\r\n if (!/^\\d$/.test(event.key)) {\r\n event.preventDefault();\r\n }\r\n }\r\n\r\n validatePart(value: string, format: string): string {\r\n if (value === '') return '';\r\n \r\n const type = format[0].toLowerCase();\r\n if (type === 'a') {\r\n const upperValue = value.toUpperCase();\r\n if (value.length === 1) {\r\n return upperValue === 'A' || upperValue === 'P' ? upperValue : '';\r\n }\r\n return ['AM', 'PM'].includes(upperValue) ? upperValue : upperValue[0];\r\n }\r\n\r\n const numValue = parseInt(value, 10);\r\n switch (type) {\r\n case 'h': // 12-hour format\r\n if (format[0] == 'H')\r\n return Math.min(Math.max(numValue, 0), 23).toString().padStart(2, '0');\r\n\r\n return Math.min(Math.max(numValue, 1), 12).toString().padStart(2, '0');\r\n \r\n case 'm': // month or minute\r\n if (format === 'MM') {\r\n return Math.min(Math.max(numValue, 1), 12).toString().padStart(2, '0');\r\n }\r\n return Math.min(Math.max(numValue, 0), 59).toString().padStart(2, '0');\r\n \r\n case 's': // seconds\r\n return Math.min(Math.max(numValue, 0), 59).toString().padStart(2, '0');\r\n \r\n case 'd': // day\r\n return Math.min(Math.max(numValue, 1), 31).toString().padStart(2, '0');\r\n \r\n case 'y': // year\r\n if (format.length === 2) return value.padStart(2, '0');\r\n return value.padStart(4, '0');\r\n \r\n default:\r\n return value;\r\n }\r\n }\r\n\r\n getPartLength(format: string): number {\r\n const type = format[0].toLowerCase();\r\n switch (type) {\r\n case 'y': return format.length === 2 ? 2 : 4;\r\n case 'a': return format.length === 1 ? 1 : 2;\r\n default: return 2;\r\n }\r\n }\r\n\r\n getCurrentPartIndex(value: string, cursorPosition: number): number {\r\n const parts = value.split(/[:/\\s-]/);\r\n let currentIndex = 0;\r\n let totalLength = 0;\r\n\r\n for (let i = 0; i < parts.length; i++) {\r\n totalLength += parts[i].length;\r\n if (cursorPosition <= totalLength + i) {\r\n return i;\r\n }\r\n totalLength += 1; // Add delimiter length\r\n }\r\n\r\n return parts.length - 1;\r\n }\r\n}","/**\r\n * Time Picker Component\r\n * A customizable time picker that supports 12/24 hour formats, seconds, and multiple locales.\r\n * \r\n * Features:\r\n * - 12/24 hour format\r\n * - Optional seconds\r\n * - Localization support\r\n * - String or Date value types\r\n * - Min/Max time validation\r\n * - Custom styling\r\n */\r\nimport { Component, ElementRef, forwardRef, Input, OnInit, Output, EventEmitter, ViewChild, OnDestroy, HostListener, ChangeDetectorRef, OnChanges, SimpleChanges, ChangeDetectionStrategy } from '@angular/core';\r\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR, FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';\r\nimport { CdkOverlayOrigin, ConnectedOverlayPositionChange, OverlayModule } from '@angular/cdk/overlay';\r\nimport { slideMotion } from '../utils/animation/slide';\r\nimport { Lang_Locale } from '../utils/models';\r\nimport { QeydarDatePickerService } from '../date-picker.service';\r\nimport { DateAdapter, GregorianDateAdapter, JalaliDateAdapter } from '../date-adapter';\r\nimport { TimeConfig, TimeFormat, TimeValueType } from '../utils/types';\r\nimport { DEFAULT_DATE_PICKER_POSITIONS, NzConnectedOverlayDirective } from \"../utils/overlay/overlay\"\r\nimport { NgFor, NgIf, NgTemplateOutlet } from '@angular/common';\r\nimport { DateMaskDirective } from '../utils/input-mask.directive';\r\n\r\n@Component({\r\n selector: 'qeydar-time-picker',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"time-picker-wrapper\" [formGroup]=\"form\">\r\n <!-- Regular input mode -->\r\n <ng-container *ngIf=\"!inline\">\r\n <div class=\"input-wrapper\" [class.focus]=\"isOpen\" [class.disabled]=\"disabled\">\r\n <input\r\n #timePickerInput\r\n [qeydar-dateMask]=\"displayFormat\"\r\n [disableInputMask]=\"disableInputMask\"\r\n [class.disabled]=\"disabled\"\r\n type=\"text\"\r\n class=\"time-picker-input\"\r\n [class.focus]=\"isOpen\"\r\n formControlName=\"timeInput\"\r\n (focus)=\"onFocusInput()\"\r\n [placeholder]=\"placeholder\"\r\n [readonly]=\"readOnly || readOnlyInput\"\r\n [attr.disabled]=\"disabled? 'disabled':null\"\r\n >\r\n <button *ngIf=\"showIcon\" class=\"time-button\" (click)=\"toggleTimePicker($event)\" tabindex=\"-1\">\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#999\" stroke-width=\"2\">\r\n <circle cx=\"12\" cy=\"12\" r=\"10\"/>\r\n <path d=\"M12 6v6l4 2\"/>\r\n </svg>\r\n </button>\r\n </div>\r\n\r\n <ng-template\r\n cdkConnectedOverlay\r\n nzConnectedOverlay\r\n [cdkConnectedOverlayOrigin]=\"origin\"\r\n [cdkConnectedOverlayOpen]=\"isOpen\"\r\n [cdkConnectedOverlayPositions]=\"overlayPositions\"\r\n [cdkConnectedOverlayTransformOriginOn]=\"'.time-picker-popup'\"\r\n [cdkConnectedOverlayHasBackdrop]=\"false\"\r\n (positionChange)=\"onPositionChange($event)\"\r\n (detach)=\"close()\"\r\n >\r\n <ng-container *ngTemplateOutlet=\"timePickerContent\"></ng-container>\r\n </ng-template>\r\n </ng-container>\r\n\r\n <!-- Inline mode -->\r\n <ng-container *ngIf=\"inline\">\r\n <ng-container *ngTemplateOutlet=\"timePickerContent\"></ng-container>\r\n </ng-container>\r\n\r\n <!-- Time Picker Content Template -->\r\n <ng-template #timePickerContent>\r\n <div \r\n #popupWrapper \r\n [class]=\"'time-picker-popup ' + cssClass\"\r\n [@slideMotion]=\"'enter'\" \r\n [class.inline]=\"inline\"\r\n [class.disabled]=\"disabled\"\r\n style=\"position: relative\"\r\n (click)=\"$event.stopPropagation()\"\r\n >\r\n <div class=\"time-picker-content\">\r\n <div class=\"time-columns\">\r\n <!-- Hours -->\r\n <div class=\"time-column\">\r\n <div class=\"time-scroller\">\r\n <button\r\n *ngFor=\"let hour of hours\"\r\n [id]=\"'selector_h'+hour\"\r\n [class.selected]=\"selectedTime.hour === hour\"\r\n [class.disabled]=\"isHourDisabled(hour)\"\r\n (click)=\"selectHour(hour)\"\r\n type=\"button\"\r\n >\r\n {{ hour.toString().padStart(2, '0') }}\r\n </button>\r\n </div>\r\n </div>\r\n \r\n <div class=\"time-separator\">:</div>\r\n \r\n <!-- Minutes -->\r\n <div class=\"time-column\">\r\n <div class=\"time-scroller\">\r\n <button\r\n *ngFor=\"let minute of minutes\"\r\n [id]=\"'selector_m'+minute\"\r\n [class.selected]=\"selectedTime.minute === minute\"\r\n [class.disabled]=\"isMinuteDisabled(minute)\"\r\n (click)=\"selectMinute(minute)\"\r\n type=\"button\"\r\n >\r\n {{ minute.toString().padStart(2, '0') }}\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Seconds (if format includes seconds) -->\r\n <ng-container *ngIf=\"showSeconds\">\r\n <div class=\"time-separator\">:</div>\r\n <div class=\"time-column\">\r\n <div class=\"time-scroller\">\r\n <button\r\n *ngFor=\"let second of seconds\"\r\n [id]=\"'selector_s'+second\"\r\n [class.selected]=\"selectedTime.second === second\"\r\n [class.disabled]=\"isSecondDisabled(second)\"\r\n (click)=\"selectSecond(second)\"\r\n type=\"button\"\r\n >\r\n {{ second.toString().padStart(2, '0') }}\r\n </button>\r\n </div>\r\n </div>\r\n </ng-container>\r\n \r\n <!-- AM/PM (only in 12-hour format) -->\r\n <ng-container *ngIf=\"timeFormat === '12'\">\r\n <div class=\"time-column period\">\r\n <button\r\n *ngFor=\"let period of periods\"\r\n [class.selected]=\"selectedTime.period === period\"\r\n (click)=\"selectPeriod(period)\"\r\n type=\"button\"\r\n >\r\n {{ period }}\r\n </button>\r\n