ngx-datetime-range-picker
Version:
> Ngx Date time range picker with daily, weekly, monthly, quarterly & yearly levels
1 lines • 159 kB
Source Map (JSON)
{"version":3,"file":"ngx-datetime-range-picker.mjs","sources":["../../src/lib/ngx-datetime-range-picker.constants.ts","../../src/lib/ngx-datetime-range-picker.utils.ts","../../src/lib/interfaces/index.ts","../../src/lib/ngx-datetime-range-picker.service.ts","../../src/lib/pipes/objNgFor.pipe.ts","../../src/lib/ngx-datetime-range-picker.component.ts","../../src/lib/ngx-datetime-range-picker.component.html","../../src/lib/material/material.module.ts","../../src/lib/ngx-datetime-range-picker.module.ts","../../src/public_api.ts","../../src/ngx-datetime-range-picker.ts"],"sourcesContent":["import { ActiveItemSide, DateSide, AriaLabelsOptions, Options, Settings, State } from \"./interfaces\";\n\ndeclare let require: any;\nconst moment = require(\"moment\");\nconst USA_MST_TZ_CODE = \"MST\";\nconst USA_TZ_CODE = \"PST\";\nconst EU_TZ_CODE = \"CET\";\n\nfunction getLocalTimezone(): string {\n const tz: string = /\\((.*)\\)/.exec(new Date().toString())[1];\n\n if (tz === \"Central Europe Standard Time\") {\n return EU_TZ_CODE;\n } else {\n return USA_MST_TZ_CODE;\n }\n}\n\nexport const DEFAULT_DATE_FORMAT = \"YYYY-MM-DD\";\n\nexport const NgxDatetimeRangePickerConstants = {\n DEFAULT: {\n OPTIONS: <Options>{\n dateArray: [],\n startDate: moment().format(\"YYYY-MM-DD\") as string,\n endDate: moment().format(\"YYYY-MM-DD\") as string,\n minDate: moment()\n .subtract(2, \"year\")\n .startOf(\"year\")\n .format(\"YYYY-MM-DD\") as string,\n maxDate: moment().format(\"YYYY-MM-DD\") as string,\n startTime: \"00:00\",\n endTime: \"23:59\"\n },\n SETTINGS: <Settings>{\n type: \"daily\",\n modelKeys: [\"daily\", \"weekly\", \"monthly\", \"quarterly\", \"yearly\"],\n showTimezoneSelect: false,\n useLocalTimezone: false,\n timePicker: false,\n inputClass: \"m1drp\",\n inputDateFormat: null,\n viewDateFormat: DEFAULT_DATE_FORMAT,\n outputDateFormat: DEFAULT_DATE_FORMAT,\n singleDatePicker: false,\n componentDisabled: false,\n placeholder: \"Select Date\",\n showRowNumber: false,\n availableRanges: {},\n showRanges: true,\n disableWeekends: false,\n disableWeekdays: false,\n retailCalendar: false,\n displayBeginDate: false,\n displayEndDate: false,\n ariaLabels: {\n inputField: \"Date Range Input Field\"\n } as AriaLabelsOptions\n },\n STATE: <State>{\n activeEndDate: null,\n activeItem: {\n left: {} as ActiveItemSide,\n right: {} as ActiveItemSide\n },\n activeRange: null,\n activeStartDate: null,\n calendarAvailable: {\n left: false,\n right: false\n },\n customRange: false,\n dates: {\n left: {} as DateSide,\n right: {} as DateSide\n },\n dateTitleText: {\n left: \"\",\n right: \"\"\n },\n frequencyColumnHeader: null,\n isCalendarVisible: false,\n isValidFilter: false,\n isUserModelChange: true,\n localTimezone: getLocalTimezone(),\n selectedDateText: \"\",\n selectedHour: {\n left: \"\",\n right: \"\"\n },\n selectedMeridian: {\n left: \"\",\n right: \"\"\n },\n selectedMinute: {\n left: \"\",\n right: \"\"\n },\n selectedMonth: {\n left: \"\",\n right: \"\"\n },\n selectedTimezone: undefined, // Since \"useLocalTimezone: false\" by default;\n selectedYear: {\n left: \"\",\n right: \"\"\n },\n sides: [],\n timeItems: [\"hour\", \"minute\"],\n times: {\n left: \"\",\n right: \"\"\n },\n timeZones: [USA_TZ_CODE, EU_TZ_CODE],\n todayTime: \"\",\n weekDayOptions: [\"su\", \"mo\", \"tu\", \"we\", \"th\", \"fr\", \"sa\"]\n },\n TIME_FORMAT: \"HH:mm\",\n RANGES: {\n daily: [\n { label: \"Last 7 Days\", count: 6 },\n { label: \"Last 30 Days\", count: 29 },\n { label: \"Last 90 Days\", count: 89 }\n ],\n weekly: [\n { label: \"Last 4 Weeks\", count: 3 },\n { label: \"Last 13 Weeks\", count: 12 },\n { label: \"Last 26 Weeks\", count: 25 }\n ],\n monthly: [\n { label: \"Last 3 Months\", count: 2 },\n { label: \"Last 6 Months\", count: 5 },\n { label: \"Last 9 Months\", count: 8 }\n ],\n quarterly: [\n { label: \"Last 2 Quarters\", count: 1 },\n { label: \"Last 4 Quarters\", count: 3 }\n ],\n yearly: [{ label: \"Last Year\", count: 1 }]\n }\n },\n CONSTANT: {\n MONTHS_AVAILABLE: [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"],\n TIMES_AVAILABLE: [\"hour\", \"minute\"],\n MOMENT_CONVERSION_MAP: {\n daily: \"day\",\n weekly: \"week\",\n monthly: \"month\",\n quarterly: \"quarter\",\n yearly: \"year\"\n },\n USA_MST_TZ_CODE,\n USA_TZ_CODE,\n EU_TZ_CODE,\n OFFSETS: {\n [USA_TZ_CODE]: {\n SO: -7,\n WO: -8\n },\n [EU_TZ_CODE]: {\n SO: 1,\n WO: 0\n }\n },\n TZ_NAMES: {\n [USA_MST_TZ_CODE]: \"America/Phoenix\",\n [USA_TZ_CODE]: \"America/Los_Angeles\",\n [EU_TZ_CODE]: \"Europe/Berlin\"\n }\n }\n};\n","export const getNotAvailableText = (): string => {\n return \"N/A\";\n};\n\n/**\n *\n * @param value the value to be cloned\n * @note will not work for objects containing functions\n */\nexport const cloneDeep = (value: object | string | number): object | string | number => {\n if (value) {\n return JSON.parse(JSON.stringify(value));\n }\n};\n\nexport const isEmpty = (value: object): boolean => {\n if (value) {\n return Object.keys(value).length <= 0;\n }\n};\n\nexport const mergeDeep = (...objects): object => {\n const isObject = (obj) => obj && typeof obj === \"object\";\n\n return objects.reduce((prev, obj) => {\n Object.keys(obj).forEach((key) => {\n const pVal = prev[key];\n const oVal = obj[key];\n\n if (Array.isArray(pVal) && Array.isArray(oVal)) {\n prev[key] = pVal.concat(...oVal);\n } else if (isObject(pVal) && isObject(oVal)) {\n prev[key] = mergeDeep(pVal, oVal);\n } else {\n prev[key] = oVal;\n }\n });\n\n return prev;\n }, {});\n};\n\nexport const isNil = (value) => {\n return value == null || value === undefined;\n};\n","const tuple = <T extends string[]>(...args: T) => args;\nexport const CalendarTypes = tuple(\"daily\", \"weekly\", \"monthly\", \"quarterly\", \"yearly\");\nexport type CalendarType = typeof CalendarTypes[number];\n\nexport interface AriaLabelsOptions {\n inputField?: string;\n}\n\nexport interface DateSide {\n label: string;\n months: string[];\n years: string[];\n itemRows: DateRow[];\n}\n\nexport interface TimeSide {\n hour: any[];\n minute: any[];\n meridian: any[];\n}\n\nexport interface DateCharacteristics {\n available?: boolean;\n inRange?: boolean;\n active?: boolean;\n today?: boolean;\n date?: string;\n}\n\nexport interface ActiveItemSide extends DateCharacteristics {\n rowItemText?: string;\n firstDay?: string;\n lastDay?: string;\n formattedDateString?: string;\n}\n\nexport interface CalendarSides {\n left?: DateSide | TimeSide | ActiveItemSide | string | boolean;\n right?: DateSide | TimeSide | ActiveItemSide | string | boolean;\n}\n\nexport interface DateTimeRangeChangeOutput {\n activeRange: string;\n startDate: string | number;\n endDate: string | number;\n startTime?: string;\n endTime?: string;\n}\n\nexport type DateTimeRangeModelChangeOutput = { [key in CalendarType]?: DateTimeRangeChangeOutput };\n\nexport interface Options {\n dateArray?: any[];\n startDate?: string | number;\n endDate?: string | number;\n minDate?: string | number;\n maxDate?: string | number;\n startTime?: string;\n endTime?: string;\n minTime?: string;\n maxTime?: string;\n}\n\nexport interface State {\n activeEndDate: string;\n activeItem: CalendarSides;\n activeRange: string;\n activeStartDate: string;\n calendarAvailable: CalendarSides;\n customRange: boolean;\n dates: CalendarSides;\n dateTitleText: CalendarSides;\n frequencyColumnHeader: string;\n isCalendarVisible: boolean;\n isValidFilter: boolean;\n isUserModelChange: boolean;\n localTimezone: string;\n selectedDateText: string;\n selectedHour: CalendarSides;\n selectedMeridian: CalendarSides;\n selectedMinute: CalendarSides;\n selectedMonth: CalendarSides;\n selectedTimezone: string;\n selectedYear: CalendarSides;\n sides: string[];\n timeItems: string[];\n times: CalendarSides;\n timeZones: string[];\n todayTime: string;\n weekDayOptions: string[];\n}\n\nexport interface Settings {\n type?: string;\n modelKeys?: string[];\n useLocalTimezone?: boolean;\n showTimezoneSelect?: boolean;\n timePicker?: boolean;\n timezoneSupport?: boolean;\n defaultTimezone?: string;\n inputClass?: string;\n inputDateFormat?: string;\n viewDateFormat?: string;\n outputDateFormat?: string;\n singleDatePicker?: boolean;\n componentDisabled?: boolean;\n label?: string;\n placeholder?: string;\n showRowNumber?: boolean;\n availableRanges?: Record<string, any>;\n showRanges?: boolean;\n disableWeekends?: boolean;\n disableWeekdays?: boolean;\n retailCalendar?: boolean;\n displayBeginDate?: boolean;\n displayEndDate?: boolean;\n ariaLabels?: AriaLabelsOptions;\n}\n\nexport interface Config extends Options, Settings {\n selectedTimezone?: string;\n}\n\nexport interface DateRow {\n rowNumber: string;\n rowNumberText: string;\n items: ActiveItemSide[];\n}\n\nexport interface RowVariables {\n rowNumber: string;\n columns: number;\n}\n\nexport interface RowItemVariables {\n itemCount: number;\n currentItemDate: string;\n rowItemText: string;\n firstDay: string;\n lastDay: string;\n}\n\nexport interface RowOptions {\n type: string;\n monthStartWeekNumber: number;\n dateRows: number;\n year: string;\n itemCount: number;\n}\n\nexport interface RowItemOptions {\n type: string;\n monthStartWeekNumber: number;\n dateRows: number;\n rowNumber: string;\n yearStartDate: string;\n year: number;\n rowItem: number;\n columns: number;\n}\n\nexport type DateRangeModel = { [key in CalendarType]?: Options };\n","import { Injectable } from \"@angular/core\";\nimport {\n DEFAULT_DATE_FORMAT,\n NgxDatetimeRangePickerConstants as Constants\n} from \"./ngx-datetime-range-picker.constants\";\nimport { getNotAvailableText, cloneDeep, isNil } from \"./ngx-datetime-range-picker.utils\";\nimport {\n Options,\n Settings,\n CalendarSides,\n State,\n RowItemVariables,\n RowItemOptions,\n DateCharacteristics,\n Config,\n RowOptions,\n CalendarTypes,\n RowVariables,\n CalendarType\n} from \"./interfaces\";\nimport { Moment } from \"moment\";\n\ndeclare let require: any;\nconst moment = require(\"moment\");\n\nconst DEFAULT_TIME_FORMAT = Constants.DEFAULT.TIME_FORMAT;\nconst MONTHS_AVAILABLE = Constants.CONSTANT.MONTHS_AVAILABLE;\nconst TZ_NAMES = Constants.CONSTANT.TZ_NAMES;\nconst DEFAULT_RANGES = Constants.DEFAULT.RANGES;\nconst MOMENT_CONVERSION_MAP = Constants.CONSTANT.MOMENT_CONVERSION_MAP;\n\n@Injectable({\n providedIn: \"root\"\n})\nexport class NgxDatetimeRangePickerService {\n getDefaultOptions(): Options {\n return cloneDeep(Constants.DEFAULT.OPTIONS) as Options;\n }\n\n getDefaultSettings(): Settings {\n return cloneDeep(Constants.DEFAULT.SETTINGS) as Settings;\n }\n\n getDefaultState(): State {\n return cloneDeep(Constants.DEFAULT.STATE) as State;\n }\n\n checkSettingsValidity(settings: Settings) {\n if (settings.type && !CalendarTypes.includes(settings.type as CalendarType)) {\n const errMsg = `${settings.type} is an invalid calendar type. It should one of ${[...CalendarTypes]}`;\n throw new Error(errMsg);\n }\n }\n\n formatDateToDefaultFormat(date: string | number, format: string): string {\n let formattedDate = null;\n if (!date) {\n return;\n }\n\n if (!isNaN(Number(date))) {\n formattedDate = moment(date).format(DEFAULT_DATE_FORMAT);\n } else {\n formattedDate = moment(date, format).format(DEFAULT_DATE_FORMAT);\n }\n\n return formattedDate;\n }\n\n formatTimeToDefaultFormat(time: string) {\n let formattedTime = null;\n if (!time) {\n return;\n }\n\n if (time.indexOf(\":\") > -1) {\n if (time.indexOf(\"AM\") > -1 || time.indexOf(\"PM\") > -1) {\n formattedTime = moment(time, \"h:mm A\").format(DEFAULT_TIME_FORMAT);\n } else {\n formattedTime = time;\n }\n } else {\n console.warn(\n `WARN_NGX_DATETIME_RANGE_PICKER:\n The provided time is not in correct format.\n Format: HH:mm or hh:mm A\n `\n );\n }\n return formattedTime;\n }\n\n getCalendarRowNumberText(type, number) {\n return (() => {\n switch (type) {\n case \"daily\":\n return `W${number}`;\n case \"weekly\":\n return \"\";\n case \"monthly\":\n return `Q${number}`;\n case \"quarterly\":\n return `${number}`;\n case \"yearly\":\n return \"\";\n }\n })();\n }\n\n createDefaultRanges(config: Config): object {\n const ranges = {};\n const type: string = config.type;\n const maxDate: string = cloneDeep(config.maxDate) as string;\n\n DEFAULT_RANGES[type].forEach((rangeInfo: { label: string; count: number }) => {\n ranges[rangeInfo.label] = {\n startDate: moment(maxDate, DEFAULT_DATE_FORMAT)\n .subtract(rangeInfo.count, MOMENT_CONVERSION_MAP[type])\n .format(DEFAULT_DATE_FORMAT),\n endDate: maxDate\n };\n });\n\n ranges[\"Custom Range\"] = { startDate: null, endDate: null };\n return ranges;\n }\n\n getSanitizedDateArray(config: Config): string[] {\n const sanitizedDateArray: string[] = [];\n const type = config.type;\n const dateArray = config.dateArray;\n const inputDateFormat = config.inputDateFormat;\n\n // dateArray can have nulls\n dateArray.forEach((date) => {\n if (!date) {\n return;\n }\n\n let format: string = null;\n\n if (isNaN(Number(date))) {\n if (inputDateFormat) {\n format = inputDateFormat;\n } else {\n format = moment(date)._f; // moment does not support this\n }\n }\n\n if (inputDateFormat !== moment(date)._f) {\n console.warn(\n `ERR_NGX_DATETIME_RANGE_PICKER:\n inputDateFormat !== dateFormat in dateArray.\n Converted dates might not be as expected\n `\n );\n }\n\n const value: Moment = format ? moment(date, format) : moment(date);\n\n if (value) {\n const formattedDate = value.endOf(MOMENT_CONVERSION_MAP[type as string]).format(DEFAULT_DATE_FORMAT);\n sanitizedDateArray.push(formattedDate);\n } else {\n console.warn(\n `ERR_NGX_DATETIME_RANGE_PICKER:\n dateArray values are in unknown format.\n Pass the format or pass the dates in known format\n `\n );\n }\n });\n\n return [...new Set(sanitizedDateArray)];\n }\n\n getNumberOfWeeks(date): number {\n if (!date) {\n return;\n }\n\n const monthStart: number = moment(date, DEFAULT_DATE_FORMAT)\n .startOf(\"month\")\n .day();\n const monthEnd: number = Number(\n moment(date, DEFAULT_DATE_FORMAT)\n .endOf(\"month\")\n .format(\"D\")\n );\n return Math.ceil((monthStart + monthEnd) / 7);\n }\n\n getYearlyWeekCount(year: string): number {\n if (!year) {\n return;\n }\n\n const yearStartDate: string = moment(year, \"YYYY\")\n .startOf(\"year\")\n .format(DEFAULT_DATE_FORMAT);\n const yearEndDate: string = moment(year, \"YYYY\")\n .endOf(\"year\")\n .format(DEFAULT_DATE_FORMAT);\n const yearEndWeekEndDate: string = moment(yearEndDate, DEFAULT_DATE_FORMAT)\n .startOf(\"week\")\n .format(DEFAULT_DATE_FORMAT);\n const yearStartWeekEndDate: string = moment(yearStartDate, DEFAULT_DATE_FORMAT)\n .endOf(\"week\")\n .format(DEFAULT_DATE_FORMAT);\n\n const yearStartWeekNumber: number = this.getWeekNumber(yearStartWeekEndDate) as number;\n const yearEndWeekNumber: number = this.getWeekNumber(yearEndWeekEndDate) as number;\n\n return yearEndWeekNumber - yearStartWeekNumber + 1;\n }\n\n getMonthsAvailable(minDate, maxDate, selectedYear): string[] {\n const months: string[] = [];\n\n if (!minDate || !maxDate || !selectedYear) {\n return;\n }\n\n minDate = moment(minDate, DEFAULT_DATE_FORMAT).startOf(\"month\");\n maxDate = moment(maxDate, DEFAULT_DATE_FORMAT).startOf(\"month\");\n\n let minDatems: number = moment(minDate, DEFAULT_DATE_FORMAT).valueOf();\n let maxDatems: number = moment(maxDate, DEFAULT_DATE_FORMAT).valueOf();\n const yearStartms: number = moment()\n .year(selectedYear)\n .startOf(\"year\")\n .valueOf();\n const yearEndms: number = moment()\n .year(selectedYear)\n .endOf(\"year\")\n .valueOf();\n\n if (minDatems < yearStartms) {\n minDatems = yearStartms;\n }\n if (maxDatems > yearEndms) {\n maxDatems = yearEndms;\n }\n\n let minDateMonthNumber: number = moment(minDatems).month();\n const diff: number = moment(maxDatems).diff(moment(minDatems), \"months\");\n const maxMonths: number = diff < MONTHS_AVAILABLE.length ? diff : MONTHS_AVAILABLE.length;\n\n for (let i = 0; i <= maxMonths; i++) {\n if (minDateMonthNumber >= MONTHS_AVAILABLE.length) {\n months.push(MONTHS_AVAILABLE[minDateMonthNumber - MONTHS_AVAILABLE.length]);\n } else {\n months.push(MONTHS_AVAILABLE[minDateMonthNumber]);\n }\n minDateMonthNumber++;\n }\n\n return months;\n }\n\n getYearsAvailable(config: Config): string[] {\n const minDate: string | number = config ? config.minDate : \"\";\n const maxDate: string | number = config ? config.maxDate : \"\";\n const years: string[] = [];\n\n if (minDate && maxDate) {\n const minYear: number = Number(this.getSelectedYear(minDate));\n const maxYear: number = Number(this.getSelectedYear(maxDate));\n const diff = maxYear - minYear;\n\n for (let i = 0; i <= diff; i++) {\n years.push(`${minYear + i}`);\n }\n }\n return years.reverse();\n }\n\n isDateAvailable(\n date: number,\n minDate: number,\n maxDate: number,\n startDate: number,\n endDate: number,\n monthStartDate: number,\n monthEndDate: number,\n config: Config\n ): boolean {\n let available = false;\n const type: string = config.type;\n const disableWeekends: boolean = config.disableWeekends;\n const disableWeekdays: boolean = config.disableWeekdays;\n\n if (type === \"daily\") {\n minDate = minDate > monthStartDate ? minDate : monthStartDate;\n maxDate = maxDate < monthEndDate ? maxDate : monthEndDate;\n }\n\n if (date >= minDate && date <= maxDate) {\n available = true;\n\n if (available) {\n if (disableWeekends) {\n available = !this.isWeekend(date);\n }\n if (disableWeekdays) {\n available = !this.isWeekday(date);\n }\n if (config.dateArray.length) {\n available = this.isInDateArray(date, config.dateArray, DEFAULT_DATE_FORMAT);\n }\n }\n }\n return available;\n }\n\n isDateInRange(\n date: number,\n minDate: number,\n maxDate: number,\n startDate: number,\n endDate: number,\n monthStartDate: number,\n monthEndDate: number,\n available: boolean,\n config: Config\n ): boolean {\n let inRange = false;\n const type: string = config.type;\n const singleDatePicker: boolean = config.singleDatePicker;\n\n if (!singleDatePicker) {\n if (type === \"daily\") {\n minDate = monthStartDate;\n maxDate = monthEndDate;\n }\n if (date >= startDate && date <= endDate && date >= minDate && date <= maxDate) {\n if (available) {\n inRange = true;\n }\n }\n }\n return inRange;\n }\n\n isDateActive(date: number, startDate: number, endDate: number, side: string): boolean {\n return (date === startDate && side === \"left\") || (date === endDate && side === \"right\");\n }\n\n isDateToday(dateMs: number, config): boolean {\n const todayDate: string = moment().format(DEFAULT_DATE_FORMAT);\n const type: string = config.type;\n const { firstDay, lastDay } = this.getFirstLastDay(todayDate, type);\n const firstDayMs: number = moment(firstDay, DEFAULT_DATE_FORMAT).valueOf();\n const lastDayMs: number = moment(lastDay, DEFAULT_DATE_FORMAT).valueOf();\n return dateMs >= firstDayMs && dateMs <= lastDayMs;\n }\n\n isWeekday(date: number, format?: string): boolean {\n return !this.isWeekend(date, format);\n }\n\n isWeekend(date: number, format?: string): boolean {\n if (!format) {\n format = null;\n }\n const day = moment(date, format).day();\n return day === 0 || day === 6;\n }\n\n isInDateArray(date: number, dateArray: any[], format?: string): boolean {\n if (!format) {\n format = null;\n }\n return dateArray.find((d) => moment(d, format).valueOf() === date) !== undefined;\n }\n\n getCalendarRowVariables(options: RowOptions): RowVariables {\n const variables: RowVariables = {\n rowNumber: \"\",\n columns: 0\n };\n const type: string = options.type;\n const monthStartWeekNumber: number = options.monthStartWeekNumber;\n const dateRows: number = options.dateRows;\n const year = `${options.year}`;\n\n if (type === \"daily\") {\n variables.rowNumber = `${monthStartWeekNumber + dateRows}`;\n variables.columns = 6;\n } else if (type === \"weekly\") {\n variables.rowNumber = ``;\n variables.columns = 6;\n } else if (type === \"monthly\") {\n variables.rowNumber = `${dateRows + 1}`;\n variables.columns = 2;\n } else if (type === \"quarterly\") {\n variables.rowNumber = year.charAt(dateRows);\n variables.columns = 0;\n } else if (type === \"yearly\") {\n variables.rowNumber = \"\";\n variables.columns = 0;\n }\n\n return variables;\n }\n\n getCalendarRowItemVariables(options: RowItemOptions): RowItemVariables {\n const { type, monthStartWeekNumber, yearStartDate, year, rowItem, dateRows, columns } = options;\n\n const itemCount: number = rowItem + dateRows * columns + dateRows;\n let currentItemDate = \"\";\n let rowItemText = \"\";\n\n if (type === \"daily\") {\n if (!isNil(monthStartWeekNumber) && !isNil(dateRows) && !isNil(year)) {\n const yearStartDateDaily = moment()\n .year(year)\n .startOf(\"year\")\n .format(DEFAULT_DATE_FORMAT);\n currentItemDate = moment(yearStartDateDaily, DEFAULT_DATE_FORMAT)\n .add(monthStartWeekNumber + dateRows - 1, \"week\")\n .startOf(\"week\")\n .add(rowItem, \"day\")\n .format(DEFAULT_DATE_FORMAT);\n rowItemText = moment(currentItemDate, DEFAULT_DATE_FORMAT).format(\"D\");\n }\n } else if (type === \"weekly\") {\n if (!isNil(yearStartDate) && !isNil(itemCount)) {\n currentItemDate = moment(yearStartDate, DEFAULT_DATE_FORMAT)\n .add(itemCount, \"week\")\n .endOf(\"week\")\n .format(DEFAULT_DATE_FORMAT);\n const weekNumber: any = itemCount + 1;\n rowItemText = `W${weekNumber}`;\n }\n } else if (type === \"monthly\") {\n if (!isNil(itemCount) && !isNil(year)) {\n currentItemDate = moment()\n .year(year)\n .month(itemCount)\n .endOf(\"month\")\n .format(DEFAULT_DATE_FORMAT);\n rowItemText = moment(currentItemDate, DEFAULT_DATE_FORMAT).format(\"MMM\");\n }\n } else if (type === \"quarterly\") {\n if (!isNil(itemCount) && !isNil(year)) {\n currentItemDate = moment()\n .year(year)\n .quarter(itemCount + 1)\n .endOf(\"quarter\")\n .format(DEFAULT_DATE_FORMAT);\n rowItemText = `Quarter ${itemCount + 1}`;\n }\n }\n\n const { firstDay, lastDay } = this.getFirstLastDay(currentItemDate, type);\n\n return {\n itemCount,\n currentItemDate,\n rowItemText,\n firstDay,\n lastDay\n };\n }\n\n isRowIemValid(options: RowOptions): boolean {\n let valid = false;\n const type: string = options.type;\n const year: string = options.year;\n const itemCount: number = options.itemCount;\n const validWeekCount: number = this.getYearlyWeekCount(year);\n\n if (type === \"daily\") {\n valid = true;\n } else if (type === \"weekly\") {\n if (itemCount < validWeekCount) {\n valid = true;\n }\n } else if (type === \"monthly\") {\n valid = true;\n } else if (type === \"quarterly\") {\n valid = true;\n }\n\n return valid;\n }\n\n formatStartDate(config: Config, returnFormat: string): string {\n const startDate: string | number = config ? config.startDate : null;\n const type: string = config ? config.type : \"\";\n let formattedStartDate: string = null;\n\n if (startDate) {\n formattedStartDate = moment(startDate, DEFAULT_DATE_FORMAT)\n .startOf(MOMENT_CONVERSION_MAP[type])\n .format(returnFormat);\n }\n\n return formattedStartDate;\n }\n\n getSelectedYear(date: string | number): number {\n return moment(date, DEFAULT_DATE_FORMAT).format(\"YYYY\");\n }\n\n getFirstLastDay(date: string, type: string): { firstDay: string; lastDay: string } {\n let firstDay = \"\";\n let lastDay = \"\";\n\n if (type === \"daily\") {\n firstDay = lastDay = date;\n } else if (type === \"weekly\") {\n firstDay = moment(date, DEFAULT_DATE_FORMAT)\n .startOf(\"week\")\n .format(DEFAULT_DATE_FORMAT);\n lastDay = moment(date, DEFAULT_DATE_FORMAT)\n .endOf(\"week\")\n .format(DEFAULT_DATE_FORMAT);\n } else if (type === \"monthly\") {\n firstDay = moment(date, DEFAULT_DATE_FORMAT)\n .startOf(\"month\")\n .format(DEFAULT_DATE_FORMAT);\n lastDay = moment(date, DEFAULT_DATE_FORMAT)\n .endOf(\"month\")\n .format(DEFAULT_DATE_FORMAT);\n } else if (type === \"quarterly\") {\n firstDay = moment(date, DEFAULT_DATE_FORMAT)\n .startOf(\"quarter\")\n .format(DEFAULT_DATE_FORMAT);\n lastDay = moment(date, DEFAULT_DATE_FORMAT)\n .endOf(\"quarter\")\n .format(DEFAULT_DATE_FORMAT);\n } else if (type === \"yearly\") {\n firstDay = moment(date, DEFAULT_DATE_FORMAT)\n .startOf(\"year\")\n .format(DEFAULT_DATE_FORMAT);\n lastDay = moment(date, DEFAULT_DATE_FORMAT)\n .endOf(\"year\")\n .format(DEFAULT_DATE_FORMAT);\n }\n\n return { firstDay, lastDay };\n }\n\n getZoneDate(tz: string, format: string, date?: string): Moment {\n let _date: number = moment().valueOf();\n\n if (date) {\n _date = moment(date, format)\n .startOf(\"day\")\n .valueOf();\n }\n\n const today = new Date(_date).toLocaleString(\"en-US\", {\n timeZone: TZ_NAMES[tz]\n });\n\n return moment(today, \"MM/DD/YYYY, hh:mm:ss A\");\n }\n\n getZoneToday(tz: string, viewDateFormat: string): string {\n const today: Moment = this.getZoneDate(tz, viewDateFormat);\n return moment(today).format(`${viewDateFormat} hh:mm A`);\n }\n\n formatToZoneDate(tz: string, format: string, date: string): string {\n const formattedDate: Moment = this.getZoneDate(tz, format, date);\n return moment(formattedDate).format(`${format}`);\n }\n\n convertToViewTimeItem(item: string | number): string {\n let stringified_item = item + \"\";\n if (stringified_item.length === 1) {\n stringified_item = `0${stringified_item}`;\n }\n return stringified_item;\n }\n\n getWeekNumber(date: string): string | number {\n if (date) {\n const year: number = moment(date, \"YYYY-MM-DD\").year();\n const month: number = moment(date, \"YYYY-MM-DD\").month();\n const day: number = Number(moment(date, \"YYYY-MM-DD\").format(\"D\"));\n\n const yearStartms: Date = new Date(year, 0, 1);\n const datems: Date = new Date(year, month, day);\n return Math.ceil(((datems.getTime() - yearStartms.getTime()) / 86400000 + yearStartms.getDay() + 1) / 7);\n } else {\n console.warn(`\n WARN_NGX_DATETIME_RANGE_PICKER | getWeekNumber:\n Invalid date\n `);\n return getNotAvailableText();\n }\n }\n\n iterateOverDateObj(dates: CalendarSides, func) {\n for (const side in dates) {\n if (side) {\n const sideDates = dates[side];\n sideDates.itemRows.forEach((rows) => {\n rows.items.forEach((rowItem) => {\n func(rowItem);\n });\n });\n }\n }\n }\n\n getCalendarColspan(type: string): number {\n if (type === \"daily\") {\n return 6;\n } else if (type === \"weekly\") {\n return 8;\n } else if (type === \"monthly\") {\n return 3;\n } else if (type === \"quarterly\") {\n return 1;\n } else if (type === \"yearly\") {\n return 1;\n }\n }\n\n getCalendarRowItemColspan(type: string): number {\n if (type === \"monthly\") {\n return 3;\n } else if (type === \"quarterly\") {\n return 6;\n } else if (type === \"yearly\") {\n return 6;\n }\n }\n\n getDateCharacteristics(config: Config, state: State, date: string, month: string, side: string): DateCharacteristics {\n const currentDate: number = moment(date, DEFAULT_DATE_FORMAT)\n .startOf(\"day\")\n .valueOf();\n\n let _date: string = this.formatDateToDefaultFormat(config.minDate, DEFAULT_DATE_FORMAT);\n const minDate: number = moment(_date, DEFAULT_DATE_FORMAT)\n .startOf(\"day\")\n .valueOf();\n\n _date = this.formatDateToDefaultFormat(config.maxDate, DEFAULT_DATE_FORMAT);\n const maxDate: number = moment(_date, DEFAULT_DATE_FORMAT)\n .startOf(\"day\")\n .valueOf();\n\n _date = this.formatDateToDefaultFormat(config.startDate, DEFAULT_DATE_FORMAT);\n const startDate: number = moment(_date, DEFAULT_DATE_FORMAT)\n .startOf(\"day\")\n .valueOf();\n\n _date = this.formatDateToDefaultFormat(config.endDate, DEFAULT_DATE_FORMAT);\n const endDate: number = moment(_date, DEFAULT_DATE_FORMAT)\n .startOf(\"day\")\n .valueOf();\n\n const currentMonthStartDate: number = moment(month, \"MMM YYYY\")\n .startOf(\"month\")\n .startOf(\"day\")\n .valueOf();\n const currentMonthEndDate: number = moment(month, \"MMM YYYY\")\n .endOf(\"month\")\n .startOf(\"day\")\n .valueOf();\n\n const available: boolean = this.isDateAvailable(\n currentDate,\n minDate,\n maxDate,\n startDate,\n endDate,\n currentMonthStartDate,\n currentMonthEndDate,\n config\n );\n const inRange: boolean = this.isDateInRange(\n currentDate,\n minDate,\n maxDate,\n startDate,\n endDate,\n currentMonthStartDate,\n currentMonthEndDate,\n available,\n config\n );\n const active: boolean = this.isDateActive(currentDate, startDate, endDate, side);\n const today: boolean = this.isDateToday(currentDate, config);\n\n // Active\n if (currentDate === startDate && side === \"left\") {\n state.activeStartDate = date;\n } else if (currentDate === endDate && side === \"right\") {\n state.activeEndDate = date;\n }\n\n return { available, inRange, active, today };\n }\n\n getLabelProps(\n state: State,\n calendarType: string,\n side: string\n ): { label: string; labelFormat: string; type: string } {\n let label: string, labelFormat: string, type: string;\n\n if (calendarType === \"daily\") {\n label = `${state.selectedMonth[side]} ${state.selectedYear[side]}`;\n labelFormat = \"MMM YYYY\";\n type = \"month\";\n } else {\n label = `${state.selectedYear[side]}`;\n labelFormat = \"YYYY\";\n type = \"year\";\n }\n\n return { label, labelFormat, type };\n }\n}\n","import { Pipe, PipeTransform } from \"@angular/core\";\n\n/**\n * Iterate over {key: value}\n * Returns the keys of the object\n * Usage:\n * let objKey of obj | ObjNgFor\n * Example:\n * let obj = {a: 1, b: 2};\n * *ngFor=\"let key of obj | ObjNgFor\"\n * {{keys}}: {{obj[key]}}\n */\n\n@Pipe({\n name: \"ObjNgFor\",\n pure: false,\n standalone: false\n})\nexport class ObjNgFor implements PipeTransform {\n public transform(value: any): any {\n return Object.keys(value); // .map(key => value[key]);\n }\n}\n","import {\n Component,\n ElementRef,\n EventEmitter,\n Input,\n OnChanges,\n Output,\n SimpleChanges,\n ViewChild,\n ViewEncapsulation,\n Renderer2\n} from \"@angular/core\";\nimport { Observable } from \"rxjs\";\nimport {\n DEFAULT_DATE_FORMAT,\n NgxDatetimeRangePickerConstants as Constants\n} from \"./ngx-datetime-range-picker.constants\";\nimport { NgxDatetimeRangePickerService } from \"./ngx-datetime-range-picker.service\";\nimport { cloneDeep, isEmpty, mergeDeep, isNil } from \"./ngx-datetime-range-picker.utils\";\nimport {\n Options,\n Settings,\n State,\n RowItemVariables,\n RowOptions,\n RowItemOptions,\n DateSide,\n ActiveItemSide,\n DateCharacteristics,\n DateRangeModel,\n Config,\n TimeSide,\n DateRow,\n RowVariables,\n DateTimeRangeChangeOutput,\n DateTimeRangeModelChangeOutput\n} from \"./interfaces\";\n\ndeclare let require: any;\nconst moment = require(\"moment\");\n\nenum InputFocusBlur {\n focus = 1,\n blur = 2\n}\n\nconst DEFAULT_TIME_FORMAT = Constants.DEFAULT.TIME_FORMAT;\nconst USA_TZ_CODE = Constants.CONSTANT.USA_TZ_CODE;\n\n@Component({\n selector: \"ngx-datetime-range-picker\",\n templateUrl: \"./ngx-datetime-range-picker.component.html\",\n styleUrls: [\"./ngx-datetime-range-picker.component.scss\"],\n encapsulation: ViewEncapsulation.None,\n standalone: false\n})\nexport class NgxDatetimeRangePickerComponent implements OnChanges {\n @Input() options: Options;\n @Input() settings: Settings;\n @Input() optionService: Observable<any>;\n @Input() dateRangeModel: Options | DateRangeModel;\n @Input() canBeEmpty = false;\n @Output() dateRangeModelChange: EventEmitter<Options | DateRangeModel> = new EventEmitter<Options | DateRangeModel>();\n @Output() dateRangeChanged: EventEmitter<Options> = new EventEmitter<Options>();\n @Output() inputFocusBlur: EventEmitter<object> = new EventEmitter<object>();\n @Output() selectedDate: EventEmitter<Options> = new EventEmitter<Options>();\n @ViewChild(\"filterInputBox\", { static: false }) filterInputBox: any;\n\n state: State;\n\n config: Config;\n\n constructor(public element: ElementRef, private renderer: Renderer2, private service: NgxDatetimeRangePickerService) {\n this.state = this.service.getDefaultState();\n this.options = this.service.getDefaultOptions();\n this.settings = this.service.getDefaultSettings();\n this.config = Object.assign(this.options, this.settings);\n\n this.state.todayTime = this.service.getZoneToday(this.state.selectedTimezone, this.config.viewDateFormat);\n\n this.renderer.listen(\"document\", \"click\", (event: MouseEvent) => {\n if (\n this.state.isCalendarVisible &&\n <HTMLElement>event.target &&\n !(<HTMLElement>event.target).parentElement.getElementsByClassName(\"ngx-datetime-range-picker-select-panel\")\n .length &&\n !(<HTMLElement>event.target).closest(\".mat-mdc-option\") &&\n this.element.nativeElement !== event.target &&\n !this.element.nativeElement.contains(event.target)\n ) {\n this.onCalendarClose();\n }\n });\n }\n\n public ngOnChanges(changes: SimpleChanges) {\n const { canBeEmpty, settings, dateRangeModel, optionService, options } = changes;\n\n if (canBeEmpty) {\n this.canBeEmpty = canBeEmpty.currentValue;\n }\n\n if (settings) {\n this.service.checkSettingsValidity(settings.currentValue as Settings);\n this.settings = mergeDeep(this.settings, settings.currentValue);\n }\n\n if (dateRangeModel) {\n this.dateRangeModel = dateRangeModel.currentValue;\n }\n\n if (dateRangeModel && !dateRangeModel.firstChange) {\n const previousValue = dateRangeModel.previousValue[this.config.type];\n const currentValue = dateRangeModel.currentValue[this.config.type];\n if (\n previousValue &&\n currentValue &&\n previousValue.startDate === currentValue.startDate &&\n previousValue.endDate === currentValue.endDate\n ) {\n return;\n }\n }\n\n if (optionService && optionService.currentValue) {\n optionService.currentValue.subscribe(\n (dateOptions: any) => {\n if (typeof dateOptions === \"object\" && !Array.isArray(dateOptions)) {\n this.options = dateOptions.plain ? dateOptions.plain() : dateOptions;\n }\n },\n (err) => {\n console.error(`ERR_NGX_DATETIME_RANGE_PICKER:\n Filter Call Failure:\n ${err}\n `);\n },\n () => {\n this.init();\n }\n );\n }\n\n if (options) {\n this.options = options ? options.currentValue : this.options;\n }\n\n if (!optionService) {\n this.init();\n }\n }\n\n // Events\n onDateRangeInputChange() {\n this.dateRangeSelected();\n }\n\n setDisabledState(disabled: boolean): void {\n this.config.componentDisabled = disabled;\n }\n\n onComponentClick(): void {\n this.state.isCalendarVisible = !this.state.isCalendarVisible;\n }\n\n onFocusInput(event: MouseEvent | FocusEvent): void {\n this.inputFocusBlur.emit({\n reason: InputFocusBlur.focus,\n value: (<HTMLInputElement>event.target).value\n });\n }\n\n onBlurInput(event: MouseEvent | FocusEvent): void {\n const value = (<HTMLInputElement>event.target).value;\n this.state.selectedDateText = value;\n this.inputFocusBlur.emit({\n reason: InputFocusBlur.blur,\n value\n });\n }\n\n onCalendarClose(): void {\n if (this.config.startDate && this.config.endDate) {\n if (this.filterInputBox) {\n this.filterInputBox.nativeElement.classList.remove(\"empty-filter\");\n }\n this.state.isCalendarVisible = false;\n } else {\n // this.filterInputBox.nativeElement.classList.add('empty-filter');\n }\n }\n\n isPrevAvailable(side): boolean {\n const { label, labelFormat, type } = this.service.getLabelProps(this.state, this.config.type, side);\n\n return (\n moment(label, labelFormat)\n .startOf(type)\n .valueOf() >\n moment(this.config.minDate, DEFAULT_DATE_FORMAT)\n .startOf(type)\n .valueOf()\n );\n }\n\n isNextAvailable(side): boolean {\n const { label, labelFormat, type } = this.service.getLabelProps(this.state, this.config.type, side);\n\n return (\n moment(label, labelFormat)\n .endOf(type)\n .valueOf() <\n moment(this.config.maxDate, DEFAULT_DATE_FORMAT)\n .endOf(type)\n .valueOf()\n );\n }\n\n getCalendarColspan() {\n return this.service.getCalendarColspan(this.config.type);\n }\n\n getCalendarRowItemColspan() {\n return this.service.getCalendarRowItemColspan(this.config.type);\n }\n\n onClickPrevious(side: string) {\n const { label, labelFormat, type } = this.service.getLabelProps(this.state, this.config.type, side);\n const startDate = moment(label, labelFormat)\n .subtract(1, type)\n .startOf(type)\n .format(DEFAULT_DATE_FORMAT);\n\n this.state.dates[side] = this.generateCalendar(startDate, side);\n }\n\n onClickNext(side: string) {\n const { label, labelFormat, type } = this.service.getLabelProps(this.state, this.config.type, side);\n const endDate = moment(label, labelFormat)\n .add(1, type)\n .endOf(type)\n .format(DEFAULT_DATE_FORMAT);\n\n this.state.dates[side] = this.generateCalendar(endDate, side);\n }\n\n onCellClick(item: DateCharacteristics, itemCell, side: string) {\n const date: number = moment(item.date, DEFAULT_DATE_FORMAT).valueOf();\n const startDate: number = moment(this.config.startDate, DEFAULT_DATE_FORMAT).valueOf();\n const endDate: number = moment(this.config.endDate, DEFAULT_DATE_FORMAT).valueOf();\n const minDate: number = moment(this.config.minDate, DEFAULT_DATE_FORMAT).valueOf();\n const maxDate: number = moment(this.config.maxDate, DEFAULT_DATE_FORMAT).valueOf();\n\n if (!item.available) {\n if (date < minDate || date > maxDate) {\n return;\n }\n this.state.dates[side] = this.generateCalendar(item.date, side);\n }\n\n if (endDate || date < startDate) {\n this.config.endDate = null;\n this.config.startDate = item.date;\n this.state.activeItem.left = item;\n // eslint-disable-next-line no-dupe-else-if\n } else if (!endDate && date < startDate) {\n this.config.endDate = cloneDeep(this.config.startDate) as string;\n this.state.activeItem.right = item;\n } else {\n this.config.endDate = item.date;\n this.state.activeItem.right = item;\n }\n\n if (this.config.singleDatePicker) {\n this.config.endDate = cloneDeep(this.config.startDate) as string;\n this.state.activeItem.right = this.state.activeItem.left = item;\n }\n\n this.doApply();\n }\n\n onCellMouseEnter(item: DateCharacteristics, itemCell, side: string) {\n if (!item.available) {\n return;\n }\n\n const date: number = moment(item.date, DEFAULT_DATE_FORMAT).valueOf();\n const startDate: number = moment(this.config.startDate, DEFAULT_DATE_FORMAT).valueOf();\n const endDate: number = moment(this.config.endDate, DEFAULT_DATE_FORMAT).valueOf();\n const hoverItemText: string = itemCell ? itemCell.innerText : \"\";\n let hoverItemFirstDate: string = itemCell ? itemCell.getAttribute(\"firstDay\") : \"\";\n let hoverItemLastDate: string = itemCell ? itemCell.getAttribute(\"lastDay\") : \"\";\n\n hoverItemFirstDate = moment(hoverItemFirstDate, DEFAULT_DATE_FORMAT).format(this.config.viewDateFormat);\n hoverItemLastDate = moment(hoverItemLastDate, DEFAULT_DATE_FORMAT).format(this.config.viewDateFormat);\n\n let activeItemInputFieldText = `${hoverItemText} (${hoverItemFirstDate} - ${hoverItemLastDate})`;\n\n if (this.config.type === \"daily\") {\n activeItemInputFieldText = `${hoverItemLastDate}`;\n }\n\n if (!endDate) {\n const func = (rowItem) => {\n if (rowItem.available) {\n const hoverItemDate = rowItem.date ? moment(rowItem.date, DEFAULT_DATE_FORMAT).valueOf() : rowItem.date;\n if ((hoverItemDate > startDate && hoverItemDate < date) || date === hoverItemDate) {\n rowItem.inRange = true;\n this.state.dateTitleText.right = activeItemInputFieldText;\n }\n }\n };\n\n this.service.iterateOverDateObj(this.state.dates, func.bind(this));\n } else {\n // if (this.config.singleDatePicker) {\n // this.state.dateTitleText.right = activeItemInputFieldText;\n // } else {\n // this.state.dateTitleText.left = activeItemInputFieldText;\n // }\n this.state.dateTitleText[side] = activeItemInputFieldText;\n }\n }\n\n onCellMouseLeave() {\n if (!this.config.endDate) {\n const func = (rowItem) => {\n rowItem.inRange = false;\n };\n this.service.iterateOverDateObj(this.state.dates, func.bind(this));\n } else {\n this.updateActiveItemInputField();\n }\n }\n\n onRangeClick(rangeLabel: string, dateRangeModel: Options) {\n this.state.activeRange = rangeLabel;\n if (rangeLabel === \"Custom Range\") {\n this.state.customRange = true;\n } else {\n this.state.customRange = false;\n this.config.startDate = dateRangeModel.startDate;\n this.config.endDate = dateRangeModel.endDate;\n if (this.config.timePicker) {\n this.state.times = {};\n }\n this.setActiveItemOnRangeClick();\n }\n }\n\n updateCalendar() {\n this.state.sides.length = 0;\n this.state.dates = {};\n // takes 223 milliSeconds\n // Order is important left - right\n if (!this.config.singleDatePicker) {\n this.state.sides.push(\"left\");\n this.state.dates.left = this.generateCalendar(this.config.startDate, \"left\");\n if (this.config.timePicker) {\n this.state.times.left = this.generateTimePicker(this.config.startTime, \"left\");\n }\n }\n this.state.sides.push(\"right\");\n this.state.dates.right = this.generateCalendar(this.config.endDate, \"right\");\n if (this.config.timePicker) {\n this.state.times.right = this.generateTimePicker(this.config.endTime, \"right\");\n }\n }\n\n onCalendarLabelChange(label: string, side: string, type: string) {\n this.state.isCalendarVisible = true;\n if (type === \"month\") {\n this.state.selectedMonth[side] = label;\n } else if (type === \"year\") {\n this.state.selectedYear[side] = label;\n }\n\n if (this.config.type !== \"daily\") {\n this.state.selectedMonth[side] = \"Jun\";\n }\n\n if (this.config.type !== \"yearly\") {\n const selectedMonth = `${this.state.selectedMonth[side]} ${this.state.selectedYear[side]}`;\n const date: string = moment(selectedMonth, \"MMM YYYY\")\n .startOf(\"month\")\n .format(DEFAULT_DATE_FORMAT);\n this.state.dates[side] = this.generateCalendar(date, side);\n } else {\n if (this.state.selectedYear.left <= this.state.selectedYear.right && side === \"right\") {\n this.config.startDate = moment(this.state.selectedYear.left, \"YYYY\")\n .startOf(\"year\")\n .format(DEFAULT_DATE_FORMAT);\n this.config.endDate = moment(this.state.selectedYear.right, \"YYYY\")\n .endOf(\"year\")\n .format(DEFAULT_DATE_FORMAT);\n\n this.doApply();\n }\n const config = {\n startDate: moment(this.state.selectedYear.left, \"YYYY\")\n .startOf(\"year\")\n .format(DEFAULT_DATE_FORMAT),\n type: \"yearly\"\n };\n const startDate: string = this.service.formatStartDate(config, this.config.viewDateFormat);\n const endDate: string = this.config.endDate\n ? moment(this.config.endDate, DEFAULT_DATE_FORMAT).format(this.config.viewDateFormat)\n : \"\";\n this.state.dateTitleText.left = `${startDate}`;\n this.state.dateTitleText.right = `${endDate}`;\n }\n }\n\n onTimeLabelChange(timeItem: string, side: string, item: string) {\n let time = null;\n if (side === \"left\") {\n time = this.config.startTime.split(\":\");\n if (timeItem === \"hour\") {\n this.config.startTime = `${item}:${time[1]}`;\n } else {\n this.config.startTime = `${time[0]}:${item}`;\n }\n\n const startDateEpoch: number = moment(this.config.startDate, DEFAULT_DATE_FORMAT).valueOf();\n const endDateEpoch: number = moment(this.config.endDate, DEFAULT_DATE_FORMAT).valueOf();\n if (startDateEpoch === endDateEpoch) {\n this.state.times.right = this.generateTimePicker(this.config.startTime, \"right\");\n }\n } else {\n time = this.config.endTime.split(\":\");\n if (timeItem === \"hour\") {\n this.config.endTime = `${item}:${time[1]}`;\n } else {\n this.config.endTime = `${time[0]}:${item}`;\n }\n }\n\n if (timeItem === \"hour\") {\n this.state.selectedHour[side] = this.service.convertToViewTimeItem(item);\n } else {\n this.state.selectedMinute[side] = this.service.convertToViewTimeItem(item);\n }\n }\n\n onTimeApply() {\n this.dateRangeSelected();\n this.updateInputField();\n }\n\n // Helpers\n init() {\n this.state.isValidFilter = false;\n if (!this.config) {\n this.config = Object.assign(this.service.getDefaultOptions(), this.service.getDefaultSettings());\n }\n this.initialize();\n this.parseOptions();\n this.updateInputField();\n }\n\n initialize() {\n this.state = this.service.getDefaultState();\n }\n\n parseOptions() {\n if (this.options !== undefined) {\n Object.keys(this.options).forEach((k) => {\n if (!isNil(this.options[k])) {\n this.config[k] = this.options[k];\n } else {\n console.warn(`WARN_NGX_DATETIME_RANGE_PICKER:\n 'options.${k}' is undefined or null. Setting default value.\n `);\n }\n });\n }\n if (this.settings !== undefined) {\n Object.keys(this.settings).forEach((k) => {\n if (!isNil(this.settings[k])) {\n this.config[k] = this.settings[k];\n } else {\n console.warn(`WARN_NGX_DATETIME_RANGE_PICKER:\n 'settings.${k}' is undefined or null. Setting default value.\n `);\n }\n });\n }\n\n // check if inputDateFormat is provided\n if (!this.config.inputDateFormat) {\n console.warn(`WARN_NGX_DATETIME_RANGE_PICKER:\n 'inputDateFormat' is required to convert dates.\n 'inputDateFormat' not provided. Setting it to YYYY-MM-DD.\n `);\n this.config.inputDateFormat = DEFAULT_DATE_FORMAT;\n }\n\n if (this.config.type === \"weekly\" || this.config.type === \"yearly\") {\n this.config.showRowNumber = false;\n }\n if (this.config.singleDatePicker) {\n this.config.startDate = cloneDeep(this.config