ng-material-date-range-picker
Version:
This library provides the date range selection with two views.
1,476 lines • 84.9 kB
JavaScript
import * as i0 from '@angular/core';
import { signal, EventEmitter, inject, ChangeDetectorRef, ElementRef, Renderer2, Input, ViewChild, Output, ChangeDetectionStrategy, Component, computed, NgModule } from '@angular/core';
import * as i2 from '@angular/forms';
import { FormGroup, FormControl, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
import * as i1 from '@angular/material/datepicker';
import { DateRange, MatDatepickerModule } from '@angular/material/datepicker';
import * as i1$1 from '@angular/common';
import { DatePipe, CommonModule } from '@angular/common';
import * as i3 from '@angular/material/input';
import { MatInputModule } from '@angular/material/input';
import * as i4 from '@angular/cdk/overlay';
import { OverlayModule } from '@angular/cdk/overlay';
import * as i5 from '@angular/material/icon';
import { MatIconModule } from '@angular/material/icon';
import * as i6 from '@angular/material/button';
import { MatButtonModule } from '@angular/material/button';
import * as i7 from '@angular/material/list';
import { MatListModule } from '@angular/material/list';
import * as i8 from '@angular/material/tooltip';
import { MatTooltipModule } from '@angular/material/tooltip';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatNativeDateModule } from '@angular/material/core';
import { MatFormFieldModule } from '@angular/material/form-field';
/**
* @(#)date-filter-enum.ts Sept 08, 2023
*
* @author Aakash Kumar
*/
const ACTIVE_DATE_DEBOUNCE = 100;
var DATE_OPTION_TYPE;
(function (DATE_OPTION_TYPE) {
DATE_OPTION_TYPE[DATE_OPTION_TYPE["DATE_DIFF"] = 1] = "DATE_DIFF";
DATE_OPTION_TYPE[DATE_OPTION_TYPE["LAST_MONTH"] = 2] = "LAST_MONTH";
DATE_OPTION_TYPE[DATE_OPTION_TYPE["THIS_MONTH"] = 3] = "THIS_MONTH";
DATE_OPTION_TYPE[DATE_OPTION_TYPE["YEAR_TO_DATE"] = 4] = "YEAR_TO_DATE";
DATE_OPTION_TYPE[DATE_OPTION_TYPE["CUSTOM"] = 5] = "CUSTOM";
DATE_OPTION_TYPE[DATE_OPTION_TYPE["MONTH_TO_DATE"] = 6] = "MONTH_TO_DATE";
DATE_OPTION_TYPE[DATE_OPTION_TYPE["WEEK_TO_DATE"] = 7] = "WEEK_TO_DATE";
})(DATE_OPTION_TYPE || (DATE_OPTION_TYPE = {}));
/**
* Resets the selection state for all options
* and marks the given option as selected if provided.
*
* @param options - List of date options
* @param selectedOption - Option to be marked as selected
*/
function resetOptionSelection(options, selectedOption) {
options.forEach((option) => (option.isSelected = false));
if (selectedOption) {
selectedOption.isSelected = true;
}
}
/**
* Marks the custom date option as selected.
*
* @param options - List of date options
*/
function selectCustomOption(options) {
const customOption = options.find((option) => option.optionType === DATE_OPTION_TYPE.CUSTOM);
if (customOption)
customOption.isSelected = true;
}
/**
* Returns a new date with the given year offset applied.
*
* @param offset - Number of years to add (negative for past years)
* @returns Date object with updated year
*/
function getDateWithOffset(offset) {
const date = new Date();
date.setFullYear(date.getFullYear() + offset);
return date;
}
/**
* Creates a deep clone of the provided object or array.
*
* @param data - Data to be cloned
* @returns A deep copy of the data
*/
function getClone(data) {
return JSON.parse(JSON.stringify(data));
}
/**
* Formats a date object into a string using Angular DatePipe.
*
* @param date - Date to be formatted
* @param dateFormat - Desired date format (e.g., 'dd/MM/yyyy')
* @returns Formatted date string
*/
function getDateString(date, dateFormat) {
const datePipe = new DatePipe('en');
return datePipe.transform(date, dateFormat) ?? '';
}
/**
* Formats a date range into a string with start and end dates.
*
* @param range - Date range with start and end
* @param dateFormat - Desired date format
* @returns Formatted range string (e.g., '01/01/2023 - 07/01/2023')
*/
function getFormattedDateString(range, dateFormat) {
if (!(range.start && range.end)) {
return '';
}
return (getDateString(range.start, dateFormat) +
' - ' +
getDateString(range.end, dateFormat));
}
/**
* Creates a standardized date option object for dropdowns.
*
* @param label - Display label for the option
* @param key - Option key from DEFAULT_DATE_OPTION_ENUM
* @param dateDiff - Offset in days from current date (default: 0)
* @param isVisible - Whether the option is visible (default: true)
* @returns ISelectDateOption object
*/
function createOption(label, key, dateDiff = 0, isVisible = true) {
return {
optionLabel: label,
optionType: key,
dateDiff,
isSelected: false,
isVisible,
};
}
/** Escapes a string so it can be used as a literal inside a RegExp. */
function escapeRegExp(value) {
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
/** Returns true when both dates fall on the same calendar day. */
function isSameDay(a, b) {
return (a.getFullYear() === b.getFullYear() &&
a.getMonth() === b.getMonth() &&
a.getDate() === b.getDate());
}
/**
* Parses a date string against an Angular-style date format (the subset of
* tokens `yyyy`, `yy`, `MM`, `M`, `dd`, `d`). Returns `null` if the string
* does not match the format or is not a real calendar date (e.g. `31/02`).
*
* @param value - The user-entered date string.
* @param format - The expected format, e.g. `dd/MM/yyyy`.
* @returns The parsed Date, or `null` if it does not match.
*/
function parseDateByFormat(value, format) {
const tokens = [];
const tokenRegex = /yyyy|yy|MM|M|dd|d/g;
let pattern = '^';
let lastIndex = 0;
let token;
while ((token = tokenRegex.exec(format)) !== null) {
pattern += escapeRegExp(format.slice(lastIndex, token.index));
switch (token[0]) {
case 'yyyy':
pattern += '(\\d{4})';
break;
case 'yy':
pattern += '(\\d{2})';
break;
case 'MM':
case 'dd':
pattern += '(\\d{2})';
break;
default: // 'M' or 'd'
pattern += '(\\d{1,2})';
break;
}
tokens.push(token[0]);
lastIndex = token.index + token[0].length;
}
pattern += escapeRegExp(format.slice(lastIndex)) + '$';
const match = new RegExp(pattern).exec(value.trim());
if (!match) {
return null;
}
let year = NaN;
let month = NaN;
let day = NaN;
tokens.forEach((token, index) => {
const part = parseInt(match[index + 1], 10);
if (token.startsWith('y')) {
year = token === 'yy' ? 2000 + part : part;
}
else if (token.startsWith('M')) {
month = part - 1;
}
else {
day = part;
}
});
if (isNaN(year) || isNaN(month) || isNaN(day)) {
return null;
}
const date = new Date(year, month, day);
// Reject overflow dates such as 31/02 that Date silently rolls over.
if (date.getFullYear() !== year ||
date.getMonth() !== month ||
date.getDate() !== day) {
return null;
}
return date;
}
/**
* Derives the relative date-math expressions (start/end) for a date option,
* for display in editable inputs. Only day-diff options have a clean relative
* form (e.g. `Last 7 Days` -> `now-7d` .. `now`); an explicit `startExpr` /
* `endExpr` on the option overrides the derived value. Returns `null` when no
* relative representation applies, so the caller falls back to absolute dates.
*
* @param option - The selected date option.
* @returns `{ start, end }` expressions, or `null`.
*/
function getRelativeExpr(option) {
if (!option) {
return null;
}
if (option.startExpr && option.endExpr) {
return { start: option.startExpr, end: option.endExpr };
}
if (option.optionType !== DATE_OPTION_TYPE.DATE_DIFF) {
return null;
}
const diff = option.dateDiff ?? 0;
const start = diff === 0 ? 'now' : `now${diff > 0 ? '+' : ''}${diff}d`;
return { start, end: 'now' };
}
/**
* Returns the date of the next month based on the given date.
*
* @param currDate - Current date
* @returns A new Date object incremented by one month
*/
function getDateOfNextMonth(currDate) {
const date = new Date(currDate);
date.setMonth(currDate.getMonth() + 1);
return date;
}
/**
* Returns the first day of the month following the given date.
*
* @param currDate - The current date
* @returns A Date object set to the first day of the next month
*/
function getFirstDateOfNextMonth(currDate) {
return new Date(currDate.getFullYear(), currDate.getMonth() + 1, 1);
}
/**
* Returns the number of days in the month of the given date.
*
* @param date The date to calculate the days for.
* @returns Number of days in the month.
*/
function getDaysInMonth(date) {
return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
}
/**
* Computes the expected DateRange for a given option, mirroring the
* logic in updateDateWithSelectedOption. Used for auto-matching a
* provided selectedDates against the available options list.
*
* Returns null for CUSTOM options and any unhandled types.
*
* @param option - The date option to compute a range for
* @returns Computed DateRange, or null if not applicable
*/
function computeOptionDateRange(option) {
if (option.optionType === DATE_OPTION_TYPE.CUSTOM) {
return null;
}
if (option.callBackFunction) {
return option.callBackFunction();
}
const currDate = new Date();
let startDate = new Date();
let lastDate = new Date();
switch (option.optionType) {
case DATE_OPTION_TYPE.DATE_DIFF:
startDate = new Date();
startDate.setDate(startDate.getDate() + (option.dateDiff ?? 0));
lastDate = new Date();
break;
case DATE_OPTION_TYPE.LAST_MONTH: {
const lastMonth = new Date(currDate);
lastMonth.setMonth(currDate.getMonth() - 1);
startDate = new Date(lastMonth.getFullYear(), lastMonth.getMonth(), 1);
lastDate = new Date(lastMonth.getFullYear(), lastMonth.getMonth(), getDaysInMonth(lastMonth));
break;
}
case DATE_OPTION_TYPE.THIS_MONTH:
startDate = new Date(currDate.getFullYear(), currDate.getMonth(), 1);
lastDate = new Date(currDate.getFullYear(), currDate.getMonth(), getDaysInMonth(currDate));
break;
case DATE_OPTION_TYPE.YEAR_TO_DATE:
startDate = new Date(currDate.getFullYear(), 0, 1);
lastDate = new Date();
break;
case DATE_OPTION_TYPE.MONTH_TO_DATE:
startDate = new Date(currDate.getFullYear(), currDate.getMonth(), 1);
lastDate = new Date();
break;
default:
return null;
}
return new DateRange(startDate, lastDate);
}
/**
* Overrides the `activeDate` setter for a MatCalendar instance, injecting custom handler logic
* while preserving the original setter behavior. Useful for reacting to internal date navigation
* events (e.g., month changes) in Angular Material's calendar.
*
* @param calendar - Instance of MatCalendar whose `activeDate` setter will be overridden.
* @param cdref - ChangeDetectorRef to trigger view updates after the setter runs.
* @param handler - Custom callback function executed whenever `activeDate` is set.
*/
function overrideActiveDateSetter(calendar, cdref, handler) {
const proto = Object.getPrototypeOf(calendar);
const descriptor = Object.getOwnPropertyDescriptor(proto, 'activeDate');
if (!(descriptor?.set && descriptor?.get)) {
console.warn('overrideActiveDateSetter: activeDate setter/getter not found on MatCalendar prototype.');
return;
}
const originalSetter = descriptor.set;
const originalGetter = descriptor.get;
Object.defineProperty(calendar, 'activeDate', {
configurable: true,
enumerable: false,
get() {
return originalGetter.call(this);
},
set(value) {
const activeDate = {
previous: originalGetter.call(this) ?? value,
current: value,
};
originalSetter.call(this, value);
handler.call(this, activeDate);
cdref.markForCheck();
},
});
}
/**
* @(#)default-date-options.ts Sept 08, 2023
*
* @author Aakash Kumar
*/
const DEFAULT_DATE_OPTIONS = [
createOption('Today', DATE_OPTION_TYPE.DATE_DIFF, 0),
createOption('Yesterday', DATE_OPTION_TYPE.DATE_DIFF, -1),
createOption('Last 7 Days', DATE_OPTION_TYPE.DATE_DIFF, -7),
createOption('Last 30 Days', DATE_OPTION_TYPE.DATE_DIFF, -30),
createOption('Last Month', DATE_OPTION_TYPE.LAST_MONTH),
createOption('This Month', DATE_OPTION_TYPE.THIS_MONTH),
createOption('Month To Date', DATE_OPTION_TYPE.MONTH_TO_DATE),
createOption('Week To Date', DATE_OPTION_TYPE.WEEK_TO_DATE, 0, false),
createOption('Year To Date', DATE_OPTION_TYPE.YEAR_TO_DATE),
createOption('Custom Range', DATE_OPTION_TYPE.CUSTOM),
];
/**
* Human-readable timedelta / date parsing utilities.
*
* Three independent parsers plus a public entry point that combines them:
* 1. {@link parseIso8601Duration} - ISO 8601 durations (`P7D`, `PT1H30M`),
* strict uppercase as required by the spec.
* 2. {@link parseDateMath} - Grafana/Elasticsearch style date math (`now-7d`).
* 3. {@link parseNaturalLanguage} - natural language (`7 days ago`) via a
* parser the host app registers with {@link setNaturalLanguageParser}
* (typically `chrono-node`). No parser registered means this step is a
* no-op, so the library never depends on `chrono-node` directly.
*
* {@link parseHumanDate} tries all three and accepts lowercase ISO durations.
*/
// ISO 8601 duration. Designators are uppercase per spec; `M` is months before
// the `T` separator and minutes after it. Fractions allow `.` or `,`.
// The `(?!$)` guards reject the degenerate `P` and `PT` strings.
const ISO_8601_DURATION = /^P(?!$)(?:(\d+(?:[.,]\d+)?)Y)?(?:(\d+(?:[.,]\d+)?)M)?(?:(\d+(?:[.,]\d+)?)W)?(?:(\d+(?:[.,]\d+)?)D)?(?:T(?!$)(?:(\d+(?:[.,]\d+)?)H)?(?:(\d+(?:[.,]\d+)?)M)?(?:(\d+(?:[.,]\d+)?)S)?)?$/;
// Date math: `now` followed by zero or more `±N<unit>` operations.
// Units: y=year, M=month, w=week, d=day, h=hour, m=minute, s=second.
const DATE_MATH = /^now((?:[+-]\d+[yMwdhms])*)$/;
const DATE_MATH_OP = /([+-])(\d+)([yMwdhms])/g;
/**
* Parses a strict, uppercase ISO 8601 duration string into a {@link Duration}.
*
* Spec-compliant: designators must be uppercase, so `P7D` parses but `p7d`
* does not. Use {@link parseHumanDate} if you need lenient (lowercase) input.
*
* @param input - Candidate ISO 8601 duration, e.g. `P1Y2M10DT2H30M`.
* @returns The parsed duration, or `null` if the string is not a valid duration.
*/
function parseIso8601Duration(input) {
const match = ISO_8601_DURATION.exec(input);
if (!match) {
return null;
}
const num = (value) => value === undefined ? 0 : parseFloat(value.replace(',', '.'));
return {
years: num(match[1]),
months: num(match[2]),
weeks: num(match[3]),
days: num(match[4]),
hours: num(match[5]),
minutes: num(match[6]),
seconds: num(match[7]),
};
}
/**
* Applies a single date-math unit to a date, returning a new Date.
* Date-level units use calendar-aware setters; the caller guarantees `unit`.
*/
function applyUnit(date, unit, amount) {
const result = new Date(date.getTime());
switch (unit) {
case 'y':
result.setFullYear(result.getFullYear() + amount);
break;
case 'M':
result.setMonth(result.getMonth() + amount);
break;
case 'w':
result.setDate(result.getDate() + amount * 7);
break;
case 'd':
result.setDate(result.getDate() + amount);
break;
case 'h':
result.setHours(result.getHours() + amount);
break;
case 'm':
result.setMinutes(result.getMinutes() + amount);
break;
case 's':
result.setSeconds(result.getSeconds() + amount);
break;
}
return result;
}
/**
* Applies a {@link Duration} to a base date, returning a new Date.
*
* Calendar units (years, months, weeks, days) use Date setters and are
* therefore truncated to integers; sub-day units (hours, minutes, seconds)
* are applied as milliseconds and preserve fractional values.
*
* @param base - The date to offset from.
* @param duration - The duration to apply.
* @param sign - `1` to add (future), `-1` to subtract (past). Defaults to `1`.
* @returns A new Date offset from `base`.
*/
function addDuration(base, duration, sign = 1) {
let result = new Date(base.getTime());
result = applyUnit(result, 'y', sign * duration.years);
result = applyUnit(result, 'M', sign * duration.months);
result.setDate(result.getDate() + sign * (duration.weeks * 7 + duration.days));
const subDayMs = (duration.hours * 3600 + duration.minutes * 60 + duration.seconds) * 1000;
result.setTime(result.getTime() + sign * subDayMs);
return result;
}
/**
* Parses a Grafana/Elasticsearch style date-math expression into a Date.
*
* Supports `now` optionally followed by `±N<unit>` operations applied left to
* right, e.g. `now`, `now-7d`, `now-1M+15d`. Units: `y M w d h m s`
* (note `M` = month, `m` = minute). Snapping (`/d`) is not supported.
*
* @param input - The date-math expression.
* @param base - Reference "now". Defaults to `new Date()`.
* @returns The resolved Date, or `null` if the expression is not date math.
*/
function parseDateMath(input, base = new Date()) {
const match = DATE_MATH.exec(input);
if (!match) {
return null;
}
let result = new Date(base.getTime());
const operations = match[1];
DATE_MATH_OP.lastIndex = 0;
let op;
while ((op = DATE_MATH_OP.exec(operations)) !== null) {
const sign = op[1] === '-' ? -1 : 1;
const amount = parseInt(op[2], 10);
result = applyUnit(result, op[3], sign * amount);
}
return result;
}
// A parser registered by the host app via `setNaturalLanguageParser`. The
// library never imports `chrono-node` itself, keeping it a truly optional
// dependency that works the same in browser, SSR, and Node builds.
let registeredParser = null;
/**
* Registers a natural-language parser (typically `chrono-node`'s `parseDate`)
* for {@link parseNaturalLanguage} / {@link parseHumanDate} to use.
*
* The consumer imports the package themselves, so their bundler resolves it
* and this library never references it directly. Call with `null` to disable.
*
* @example
* import * as chrono from 'chrono-node';
* setNaturalLanguageParser((text, ref) => chrono.parseDate(text, ref));
*
* @param parser - The parser to use, or `null` to disable.
*/
function setNaturalLanguageParser(parser) {
registeredParser = parser;
}
/**
* Parses natural language (`7 days ago`, `next friday`) using the parser
* registered via {@link setNaturalLanguageParser}.
*
* @param input - The natural-language date expression.
* @param base - Reference date the parser resolves against. Defaults to `new Date()`.
* @returns The parsed Date, or `null` if no parser is registered or the text
* could not be parsed.
*/
function parseNaturalLanguage(input, base = new Date()) {
if (!registeredParser) {
return null;
}
try {
return registeredParser(input, base) ?? null;
}
catch {
return null;
}
}
/**
* Public entry point: parses a human-readable date/duration expression into a
* concrete Date by trying, in order, date math, ISO 8601 duration, then
* natural language via the registered parser.
*
* Unlike {@link parseIso8601Duration}, this accepts lowercase ISO durations
* (`p7d`) by upper-casing the input before the ISO attempt.
*
* @param input - The expression, e.g. `now-7d`, `P7D`, `p7d`, `3 weeks ago`.
* @param options - See {@link ParseHumanDateOptions}.
* @returns The parsed Date, or `null` if nothing matched.
*/
function parseHumanDate(input, options = {}) {
const base = options.base ?? new Date();
const durationSign = options.durationSign ?? -1;
const useNaturalLanguage = options.useNaturalLanguage ?? true;
const trimmed = input?.trim();
if (!trimmed) {
return null;
}
// 1. Date math (`now-7d`) - unambiguous and cheap.
const fromDateMath = parseDateMath(trimmed, base);
if (fromDateMath) {
return fromDateMath;
}
// 2. ISO 8601 duration - upper-cased so lowercase input is accepted here.
const duration = parseIso8601Duration(trimmed.toUpperCase());
if (duration) {
return addDuration(base, duration, durationSign);
}
// 3. Natural language via the registered parser, if any.
if (useNaturalLanguage) {
return parseNaturalLanguage(trimmed, base);
}
return null;
}
/**
* @(#)calendar.component.scss Sept 07, 2023
*
* Custom Calendar Component that manages two side-by-side
* month views with support for date range selection, hover
* highlighting, and navigation controls.
*
* @author Aakash Kumar
*/
class CalendarComponent {
constructor() {
this.firstViewStartDate = signal(new Date(), ...(ngDevMode ? [{ debugName: "firstViewStartDate" }] : []));
this.secondViewStartDate = signal(getDateOfNextMonth(this.firstViewStartDate()), ...(ngDevMode ? [{ debugName: "secondViewStartDate" }] : []));
this.secondViewMinDate = signal(getFirstDateOfNextMonth(this.firstViewStartDate()), ...(ngDevMode ? [{ debugName: "secondViewMinDate" }] : []));
/** Emits when the user changes the selection by clicking dates in the views. */
this.selectedDatesChange = new EventEmitter();
this.isAllowHoverEvent = false;
this.cdref = inject(ChangeDetectorRef);
this.el = inject(ElementRef);
this.renderer = inject(Renderer2);
}
/**
* Updates the selected date range and synchronizes both calendar views.
*/
set selectedDates(selectedDates) {
this._selectedDates = selectedDates;
if (!selectedDates || !(selectedDates.start && selectedDates.end))
return;
const startDate = selectedDates.start ?? new Date();
const endDate = selectedDates.end;
this.firstViewStartDate.set(startDate);
this.secondViewMinDate.set(getFirstDateOfNextMonth(startDate));
const computedEndDate = startDate.getMonth() === endDate.getMonth()
? getDateOfNextMonth(endDate)
: endDate;
this.secondViewStartDate.set(computedEndDate);
}
get selectedDates() {
return this._selectedDates;
}
/**
* Lifecycle hook that is called after Angular has fully initialized
* the component's view (and child views).
*
* Used here to attach hover events and register active date change
* listeners once the calendar views are available in the DOM.
*/
ngAfterViewInit() {
this.attachHoverEvent('firstCalendarView');
this.attachHoverEvent('secondCalendarView');
this.registerActiveDateChangeEvents();
}
/**
* Handles month selection in the first view.
*
* @param event - Selected month date
*/
monthSelected(viewName) {
if (viewName === 'secondCalendarView') {
this.removeDefaultFocus(this);
}
this.attachHoverEvent(viewName);
}
/**
* Updates the selected date range when a date is clicked.
*
* @param date - Date clicked by the user
*/
updateDateRangeSelection(date) {
const selectedDates = this.selectedDates;
if (!selectedDates ||
(selectedDates.start && selectedDates.end) ||
(selectedDates.start && date && selectedDates.start > date)) {
this._selectedDates = new DateRange(date, null);
this.isAllowHoverEvent = true;
}
else {
this.isAllowHoverEvent = false;
this._selectedDates = new DateRange(selectedDates.start, date);
}
this.selectedDatesChange.emit(this._selectedDates);
this.cdref.markForCheck();
}
/**
* Registers event handlers for active date changes on both calendar views.
*
* This method overrides the default `activeDate` property setter of each
* calendar view to ensure custom handlers are executed whenever the
* active date changes.
*/
registerActiveDateChangeEvents() {
overrideActiveDateSetter(this.firstCalendarView, this.cdref, this.onFirstViewActiveDateChange.bind(this));
overrideActiveDateSetter(this.secondCalendarView, this.cdref, this.onSecondViewActiveDateChange.bind(this));
}
/**
* Handles the event when the active date of the first calendar view changes.
*
* @param activeDate - Object containing `previous` and `current` date values.
*/
onFirstViewActiveDateChange(activeDate) {
const handler = this.isPrevious(activeDate)
? () => this.handleFirstViewPrevEvent(activeDate)
: () => this.handleFirstViewNextEvent(activeDate.current);
// Delay execution because active date event fires before view update
setTimeout(handler, ACTIVE_DATE_DEBOUNCE);
}
/**
* Handles the event when the active date of the second calendar view changes.
*
* @param activeDate - Object containing `previous` and `current` date values.
*/
onSecondViewActiveDateChange(activeDate) {
this.attachHoverEvent('secondCalendarView');
}
/**
* Handles the "next" navigation event for the first calendar view.
*
* @param currDate - The currently active date in the first calendar view.
* @param force - Optional flag that can be used to enforce updates (not used in current logic).
*/
handleFirstViewNextEvent(currDate, force) {
if (this.firstCalendarView.currentView.toLocaleLowerCase() !== 'month') {
return;
}
this.attachHoverEvent('firstCalendarView');
const nextMonthDate = getFirstDateOfNextMonth(currDate);
let secondViewActiveDate = this.secondCalendarView.activeDate;
if (nextMonthDate < secondViewActiveDate) {
this.secondViewMinDate.set(nextMonthDate);
this.attachHoverEvent('secondCalendarView');
return;
}
secondViewActiveDate = getDateOfNextMonth(currDate);
this.secondViewMinDate.set(nextMonthDate);
this.secondCalendarView.activeDate = secondViewActiveDate;
this.cdref.detectChanges();
}
/**
* Handles the "previous" navigation event for the first calendar view.
*
* @param activeDate - Object containing `previous` and `current` date values.
*/
handleFirstViewPrevEvent(activeDate) {
if (this.firstCalendarView.currentView.toLocaleLowerCase() !== 'month') {
return;
}
this.secondViewMinDate.set(getFirstDateOfNextMonth(activeDate.current));
this.attachHoverEvent('firstCalendarView');
this.attachHoverEvent('secondCalendarView');
}
/**
* Checks whether the previous date is greater than the current date.
*
* @param activeDate - Object containing `previous` and `current` date values.
* @returns `true` if the previous date is later than the current date, otherwise `false`.
*/
isPrevious(activeDate) {
return activeDate.previous > activeDate.current;
}
/**
* Attaches hover events to all date cells in the first view.
*/
attachHoverEvent(viewId) {
const nodes = this.el.nativeElement.querySelectorAll(`#${viewId} .mat-calendar-body-cell`);
setTimeout(() => this.addHoverEvents(nodes), ACTIVE_DATE_DEBOUNCE);
}
/**
* Removes active focus from the second view.
*
* @param classRef - Reference to this component
*/
removeDefaultFocus(classRef) {
setTimeout(() => {
const btn = classRef.el.nativeElement.querySelectorAll('#secondCalendarView button.mat-calendar-body-active');
if (btn?.length) {
btn[0].blur();
}
}, 1);
}
/**
* Updates the selection range dynamically on hover.
*
* @param date - Hovered date
*/
updateSelectionOnMouseHover(date) {
const selectedDates = this.selectedDates;
if (selectedDates?.start && date && selectedDates.start < date) {
const dateRange = new DateRange(selectedDates.start, date);
this.firstCalendarView.selected = dateRange;
this.secondCalendarView.selected = dateRange;
this.firstCalendarView['_changeDetectorRef'].markForCheck();
this.secondCalendarView['_changeDetectorRef'].markForCheck();
this.isAllowHoverEvent = true;
}
}
/**
* Attaches hover events to given nodes to update range selection.
*
* @param nodes - Date cell nodes
*/
addHoverEvents(nodes) {
if (!nodes) {
return;
}
Array.from(nodes).forEach((button) => {
this.renderer.listen(button, 'mouseover', (event) => {
if (this.isAllowHoverEvent) {
const date = new Date(event.target['ariaLabel']);
this.updateSelectionOnMouseHover(date);
}
});
});
this.firstCalendarView['_changeDetectorRef'].markForCheck();
this.secondCalendarView['_changeDetectorRef'].markForCheck();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: CalendarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.8", type: CalendarComponent, isStandalone: false, selector: "lib-calendar", inputs: { minDate: "minDate", maxDate: "maxDate", selectedDates: "selectedDates" }, outputs: { selectedDatesChange: "selectedDatesChange" }, viewQueries: [{ propertyName: "firstCalendarView", first: true, predicate: ["firstCalendarView"], descendants: true }, { propertyName: "secondCalendarView", first: true, predicate: ["secondCalendarView"], descendants: true }], ngImport: i0, template: "<!--**\n * @(#)calendar.component.html Sept 07, 2023\n\n * @author Aakash Kumar\n *-->\n<div class=\"calendar-container\">\n <div class=\"first-view\">\n <mat-calendar id=\"firstCalendarView\" #firstCalendarView [startAt]=\"firstViewStartDate()\" [selected]=\"selectedDates\"\n (selectedChange)=\"updateDateRangeSelection($event)\" (monthSelected)=\"monthSelected('firstCalendarView')\" [minDate]=\"minDate\"\n [maxDate]=\"maxDate\"></mat-calendar>\n </div>\n <div class=\"second-view\">\n <mat-calendar id=\"secondCalendarView\" #secondCalendarView [startAt]=\"secondViewStartDate()\" [minDate]=\"secondViewMinDate()\"\n [maxDate]=\"maxDate\" [selected]=\"selectedDates\" (selectedChange)=\"updateDateRangeSelection($event)\"\n (monthSelected)=\"monthSelected('secondCalendarView')\"></mat-calendar>\n </div>\n</div>\n", styles: [".mat-calendar{min-width:250px}.calendar-container{width:100%;display:flex}.first-view,.second-view{flex:1 1 50%;min-width:0;margin-top:.5rem}@media(max-width:490px){.calendar-container{flex-direction:column}}\n"], dependencies: [{ kind: "component", type: i1.MatCalendar, selector: "mat-calendar", inputs: ["headerComponent", "startAt", "startView", "selected", "minDate", "maxDate", "dateFilter", "dateClass", "comparisonStart", "comparisonEnd", "startDateAccessibleName", "endDateAccessibleName"], outputs: ["selectedChange", "yearSelected", "monthSelected", "viewChanged", "_userSelection", "_userDragDrop"], exportAs: ["matCalendar"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: CalendarComponent, decorators: [{
type: Component,
args: [{ standalone: false, selector: 'lib-calendar', changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--**\n * @(#)calendar.component.html Sept 07, 2023\n\n * @author Aakash Kumar\n *-->\n<div class=\"calendar-container\">\n <div class=\"first-view\">\n <mat-calendar id=\"firstCalendarView\" #firstCalendarView [startAt]=\"firstViewStartDate()\" [selected]=\"selectedDates\"\n (selectedChange)=\"updateDateRangeSelection($event)\" (monthSelected)=\"monthSelected('firstCalendarView')\" [minDate]=\"minDate\"\n [maxDate]=\"maxDate\"></mat-calendar>\n </div>\n <div class=\"second-view\">\n <mat-calendar id=\"secondCalendarView\" #secondCalendarView [startAt]=\"secondViewStartDate()\" [minDate]=\"secondViewMinDate()\"\n [maxDate]=\"maxDate\" [selected]=\"selectedDates\" (selectedChange)=\"updateDateRangeSelection($event)\"\n (monthSelected)=\"monthSelected('secondCalendarView')\"></mat-calendar>\n </div>\n</div>\n", styles: [".mat-calendar{min-width:250px}.calendar-container{width:100%;display:flex}.first-view,.second-view{flex:1 1 50%;min-width:0;margin-top:.5rem}@media(max-width:490px){.calendar-container{flex-direction:column}}\n"] }]
}], propDecorators: { minDate: [{
type: Input
}], maxDate: [{
type: Input
}], selectedDatesChange: [{
type: Output
}], firstCalendarView: [{
type: ViewChild,
args: ['firstCalendarView']
}], secondCalendarView: [{
type: ViewChild,
args: ['secondCalendarView']
}], selectedDates: [{
type: Input
}] } });
/**
* @(#)ng-date-picker.component.ts Sept 05, 2023
*
* @author Aakash Kumar
*/
class NgDatePickerComponent {
constructor() {
this.isDateOptionList = false;
this.isCustomRange = false;
this.inputLabel = 'Date Range';
this.staticOptionId = 'static-options';
this.dynamicOptionId = 'dynamic-options';
this.calendarId = 'custom-calendar';
this.enableDefaultOptions = true;
this.dateFormat = 'dd/MM/yyyy';
this.isShowStaticDefaultOptions = false;
this.hideDefaultOptions = false;
this.cdkConnectedOverlayOffsetX = 0;
this.cdkConnectedOverlayOffsetY = 0;
this.listCdkConnectedOverlayOffsetY = 0;
this.listCdkConnectedOverlayOffsetX = 0;
this.selectedOptionIndex = 3;
this.displaySelectedLabel = false;
/**
* When true, the main input shows the human-readable expressions
* (e.g. `now-7d - now`) instead of the absolute date range. If
* `displaySelectedLabel` is also true, the label takes priority.
*/
this.displaySelectedExpression = false;
/**
* When true, the custom-range footer shows two editable Material inputs
* (start / end) that accept dates in `dateFormat`, ISO 8601 durations
* (`p7d`), or date math (`now-7d`). When false, a read-only label is shown.
*/
this.enableEditableDates = false;
this.cdkConnectedOverlayPush = true;
this.cdkConnectedOverlayPositions = [];
this.allowSingleDateSelection = true;
/**
* When true, automatically selects the preset option whose computed date range
* matches the provided selectedDates (works with both default and custom options).
* Falls back to "Custom Range" if no option matches.
*/
this.autoSelectOption = false;
// default min date is current date - 10 years.
this.minDate = getDateWithOffset(-10);
// default max date is current date + 10 years.
this.maxDate = getDateWithOffset(10);
this.onDateSelectionChanged = new EventEmitter();
this.dateListOptions = new EventEmitter();
this.cdref = inject(ChangeDetectorRef);
this.el = inject(ElementRef);
this._dateOptions = signal([], ...(ngDevMode ? [{ debugName: "_dateOptions" }] : []));
this.visibleOptions = computed(() => this._dateOptions().filter((op) => op.isVisible), ...(ngDevMode ? [{ debugName: "visibleOptions" }] : []));
/**
* Reactive form backing the editable start/end inputs. Each control accepts
* a `dateFormat` date, an ISO 8601 duration, or a date-math expression; the
* group is invalid when either value is unparseable or start is after end.
*/
this.editableForm = new FormGroup({
start: new FormControl('', {
nonNullable: true,
validators: [Validators.required, (c) => this.validateDateControl(c)],
}),
end: new FormControl('', {
nonNullable: true,
validators: [Validators.required, (c) => this.validateDateControl(c)],
}),
}, { validators: (g) => this.validateRange(g) });
// The raw expressions the user last committed via the editable inputs, kept
// so human-language input (e.g. `now-7d`) is shown again instead of being
// replaced by an absolute date - but only while it still resolves to the
// current range (see populateEditableForm).
this.editableExpr = null;
}
set dateDropDownOptions(defaultDateList) {
const options = [
...(this.enableDefaultOptions ? getClone(DEFAULT_DATE_OPTIONS) : []),
...(defaultDateList ?? []),
];
this._dateOptions.set(options);
}
get dateDropDownOptions() {
return this._dateOptions() ?? [];
}
ngOnInit() {
if (this.isDefaultInitRequired()) {
this.initDefaultOptions();
}
this.dateListOptions.emit(this.dateDropDownOptions);
}
ngAfterViewInit() {
this.updateDefaultDatesValues();
}
/**
* Toggles the visibility of the default date option list.
* If the custom range panel is open, closes it instead.
*
* @param event Optional MouseEvent triggering the toggle.
*/
toggleDateOptionSelectionList(event) {
event?.preventDefault();
event?.stopImmediatePropagation();
if (this.isCustomRange) {
this.isCustomRange = false;
return;
}
if (this.isDateOptionList) {
this.isDateOptionList = false;
return;
}
// When the active selection is a custom range, reopen straight into the
// custom-range view instead of the options list.
const selectedOption = this.dateDropDownOptions.find((o) => o.isSelected);
if (selectedOption?.optionType === DATE_OPTION_TYPE.CUSTOM) {
this.isCustomRange = true;
this.populateEditableForm();
return;
}
this.isDateOptionList = true;
}
/**
* Updates the custom date range selection from the input.
*
* @param input The HTML input element associated with the date picker.
* @param selectedDates The selected date range.
*/
updateCustomRange(input, selectedDates) {
if (this.allowSingleDateSelection && !selectedDates?.end) {
const date = selectedDates?.start ?? new Date();
selectedDates = new DateRange(date, date);
}
if (this.isCustomRange) {
resetOptionSelection(this.dateDropDownOptions);
selectCustomOption(this.dateDropDownOptions);
this.syncOptionSelection();
this.isCustomRange = false;
}
const start = selectedDates?.start ?? new Date();
const end = selectedDates?.end ?? new Date();
this.updateSelectedDates(input, start, end, null);
}
/**
* Updates the selection when a specific date option is clicked.
*
* @param option The selected date option.
* @param input The HTML input element to update with selected dates.
*/
updateSelection(option, input) {
this.isDateOptionList = false;
this.isCustomRange = option.optionType === DATE_OPTION_TYPE.CUSTOM;
if (this.isCustomRange) {
resetOptionSelection(this.dateDropDownOptions);
selectCustomOption(this.dateDropDownOptions);
this.populateEditableForm();
}
else {
resetOptionSelection(this.dateDropDownOptions, option);
this.updateDateOnOptionSelect(option, input);
}
this.syncOptionSelection();
this.cdref.markForCheck();
}
/**
* Re-emits the options signal after an in-place selection change so the
* OnPush views (bound to the `visibleOptions` computed) reliably reflect the
* new `isSelected` state - the same notification the initial signal `set`
* provides.
*/
syncOptionSelection() {
this._dateOptions.update((options) => [...options]);
}
/**
* Toggles the custom date range selection view visibility.
*/
toggleCustomDateRangeView() {
this.isCustomRange = !this.isCustomRange;
if (this.isCustomRange) {
this.populateEditableForm();
}
}
/**
* Parses a single editable input value, accepting either a `dateFormat`
* date or one of the human formats (ISO 8601 duration, date math).
*
* @param value - The raw input string.
* @returns The parsed Date, or `null` if it cannot be parsed.
*/
parseInputValue(value) {
const trimmed = value?.trim();
if (!trimmed) {
return null;
}
return parseDateByFormat(trimmed, this.dateFormat) ?? parseHumanDate(trimmed);
}
/**
* Validator for a single editable date control: valid when the value parses
* to a date. Empty values are left to the `required` validator.
*/
validateDateControl(control) {
const value = (control.value ?? '').trim();
if (!value) {
return null;
}
return this.parseInputValue(value) ? null : { invalidDate: true };
}
/**
* Group validator ensuring the parsed start date is not after the end date.
*/
validateRange(group) {
const start = this.parseInputValue(group.get('start')?.value ?? '');
const end = this.parseInputValue(group.get('end')?.value ?? '');
if (start && end && start > end) {
return { rangeOrder: true };
}
return null;
}
/**
* Commits the editable inputs to the calendar so the views and the Apply
* action reflect the typed values. No-op when editing is disabled or the
* form is invalid.
*
* @param calendar - The calendar component instance from the template.
*/
commitEditableDates(calendar) {
if (!this.enableEditableDates || this.editableForm.invalid) {
return;
}
const startRaw = this.editableForm.controls.start.value.trim();
const endRaw = this.editableForm.controls.end.value.trim();
const start = this.parseInputValue(startRaw);
const end = this.parseInputValue(endRaw);
if (!start || !end) {
return;
}
// Remember exactly what the user typed so the expression (e.g. `now-7d`)
// survives a reopen instead of being shown as a resolved absolute date.
this.editableExpr = { start: startRaw, end: endRaw };
calendar.selectedDates = new DateRange(start, end);
this.cdref.markForCheck();
}
/**
* Reflects a calendar (date-click) selection in the editable inputs as
* absolute dates. A calendar pick is an explicit absolute selection, so any
* remembered expression is cleared and the inputs show formatted dates.
*
* @param range - The range emitted by the calendar.
*/
onCalendarSelectionChange(range) {
if (!this.enableEditableDates) {
return;
}
this.editableExpr = null;
this.editableForm.setValue({
start: range.start ? getDateString(range.start, this.dateFormat) : '',
end: range.end ? getDateString(range.end, this.dateFormat) : '',
});
this.cdref.markForCheck();
}
/**
* Commits the editable inputs and applies the range, closing the panel -
* the same as clicking Apply. Used for the Enter key. No-op when editing is
* disabled or the form is invalid, so Enter never closes with bad input.
*
* @param input - The main date input element to update.
* @param calendar - The calendar component instance from the template.
*/
applyEditableDates(input, calendar) {
if (!this.enableEditableDates || this.editableForm.invalid) {
return;
}
this.commitEditableDates(calendar);
this.updateCustomRange(input, calendar.selectedDates);
}
/**
* Pre-fills the editable inputs from the current selection: relative
* expressions for a day-diff option (e.g. `now-7d` .. `now`), otherwise the
* absolute formatted dates.
*/
populateEditableForm() {
if (!this.enableEditableDates) {
return;
}
const range = this.selectedDates;
if (range?.start && range?.end) {
const option = this.dateDropDownOptions.find((o) => o.isSelected) ?? null;
this.editableForm.setValue(this.resolveDisplayExpr(range.start, range.end, option));
}
else {
this.editableForm.setValue({ start: '', end: '' });
}
}
/**
* Resolves the human-readable start/end expressions for a range. Prefers the
* user's own committed expression (when it still resolves to this range),
* then the relative form of a day-diff option, and finally the absolute
* formatted dates. Shared by the editable inputs and the emitted event.
*
* @param start - Range start date.
* @param end - Range end date.
* @param opt - The associated date option, if any.
* @returns The start and end expression strings.
*/
resolveDisplayExpr(start, end, opt) {
if (this.editableExpr && this.exprMatchesDates(this.editableExpr, start, end)) {
return { start: this.editableExpr.start, end: this.editableExpr.end };
}
const optionExpr = getRelativeExpr(opt);
if (optionExpr) {
return optionExpr;
}
return {
start: getDateString(start, this.dateFormat),
end: getDateString(end, this.dateFormat),
};
}
/**
* Checks whether a saved expression still resolves (to day precision) to the
* given dates, so a stale expression is not reused after the range changed
* by other means.
*/
exprMatchesDates(expr, start, end) {
const exprStart = this.parseInputValue(expr.start);
const exprEnd = this.parseInputValue(expr.end);
return (!!exprStart &&
!!exprEnd &&
isSameDay(exprStart, start) &&
isSameDay(exprEnd, end));
}
/**
* Clears the currently selected dates and resets all related properties.
*
* @param event The MouseEvent triggering the clear action.
*/
clearSelection(event) {
event?.stopImmediatePropagation();
this.minDate = getDateWithOffset(-10);
this.maxDate = getDateWithOffset(10);
this.selectedDates = null;
resetOptionSelection(this.dateDropDownOptions);
this.syncOptionSelection();
this.clearDateInput();
this.cdref.markForCheck();
const selectedDateEventData = {
range: null,
selectedOption: null,
startExpr: null,
endExpr: null,
};
this.onDateSelectionChanged.emit(selectedDateEventData);
}
/**
* Clears the input field value for the date picker.
*/
clearDateInput() {
const dateInputField = this.el.nativeElement.querySelector('#date-input-field');
if (dateInputField) {
dateInputField.value = '';
}
}
/**
* Updates selected dates based on a selected option and input element.
*
* @param option The selected date option.
* @param input The HTML input element to update.
*/
updateDateOnOptionSelect(option, input) {
// If there is a callback function, use it to get the date range
if (option?.callBackFunction) {
const dateRange = option.callBackFunction();
if (dateRange?.start && dateRange?.end) {
this.updateSelectedDates(input, dateRange.start, dateRange.end, option);
return;
}
}
this.updateDateWithSelectedOption(option, input);
}
/**
* Calculates and updates the start and end dates based on the selected option.
*
* @param option The selected date option.
* @param input The HTML input element to update.
*/
updateDateWithSelectedOption(option, input) {
const currDate = new Date();
let startDate = new Date();
let lastDate = new Date();
// Determine the date range based on the option key
switch (option.optionType) {
case DATE_OPTION_TYPE.DATE_DIFF:
startDate.setDate(startDate.getDate() + (option.dateDiff ?? 0));
break;
case DATE_OPTION_TYPE.LAST_MONTH:
currDate.setMonth(currDate.getMonth() - 1);
startDate = new Date(currDate.getFullYear(), currDate.getMonth(), 1);
lastDate = new Date(currDate.getFullYear(), currDate.getMonth(), getDaysInMonth(currDate));
break;
case DATE_OPTION_TYPE.THIS_MONTH:
startDate = new Date(currDate.getFullYear(), currDate.getMonth(), 1);
lastDate = new Date(currDate.getFullYear(), currDate.getMonth(), getDaysInMonth(currDate));
break;
case DATE_OPTION_TYPE.YEAR_TO_DATE:
startDate = new Date(currDate.getFullYear(), 0, 1);
break;
case DATE_OPTION_TYPE.MONTH_TO_DATE:
startDate = new Date(currDate.getFullYear(), currDate.getMonth(), 1);
break;
default:
break;
}
// Update the selected dates
this.updateSelectedDates(input, startDate, lastDate, option);
}
/**
* Updates the date range and input display.
*
* @param input The HTML input element.
* @param start Start date of the range.
* @param end End date of the range.
* @param opt Optional selected date option.
*/
updateSelectedDates(input, start, end, opt) {
const range = new DateRange(start, end);
this.selectedDates = range;
const expr = this.resolveDisplayExpr(start, end, opt);
const rangeLabel = `${getDateString(start, this.dateFormat)} - ${getDateString(end, this.dateFormat)}`;
if (this.displaySelectedLabel && opt?.optionLabel) {
input.value = opt.optionLabel;
}
else if (this.displaySelectedExpression) {
input.value = `${expr.start} - ${expr.end}`;
}
else {
input.value = rangeLabel;
}
this.onDateSelectionChanged.emit({
range,
selectedOption: this.dateDropDownOptions.find((o) => o.isSelected) ?? null,
startExpr: expr.start,
endExpr: expr.end,
});
this.cdref.markForCheck();
}
/**
* Updates the input and internal state with default dates on initialization.
* When autoSelectOption is true and selectedDates is provided, attempts to
* match against existing options before falling back to Custom Range.
*/
updateDefaultDatesValues() {
const input = this.el.nativeElement.querySelector('#date-input-field');
if (this.selectedDates?.start && this.selectedDates?.end) {
const matchedOption = this.autoSelectOption
? this.findMatchingOption(this.selectedDates)
: null;
if (matchedOption) {
resetOptionSelection(this.dateDropDownOptions, matchedOption);
const label = this.displaySelectedLabel ? matchedOption.optionLabel : null;
input.value = label ?? getFormattedDateString(this.selectedDates, this.dateFormat);
}
else {
resetOptionSelection(this.dateDropDownOptions);
selectCustomOption(this.dateDropDownOptions);
input.value = getFormattedDateString(this.selectedDates, this.dateFormat);
}
this.cdref.detectChanges();
return;
}
const selectedOptions = this._dateOptions().find((option) => option.isSelected);
if (selectedOptions &&
selectedOptions.optionType !== DATE_OPTION_TYPE.CUSTOM) {
this.updatedFromListValueSelection(selectedOptions, input);
this.cdref.detectChanges();
}
}
/**
* Iterates over all non-custom options and returns the first one whose
* computed date range matches the provided selectedDates (day-level comparison).
* Works for both default options and consumer-provided options with callBackFunction.
*
* @param selectedDates The date range to match against
* @returns The matching ISelectDateOption, or null if none found
*/
findMatchingOption(selectedDates) {
const candidates = this.dateDropDownOptions.filter((option) => option.optionType !== DATE_OPTION_TYPE.CUSTOM);
for (const option of candidates) {
const range = computeOptionDateRange(option);
if (range?.start &&
range?.end &&
isSameDay(range.start, selectedDates.start) &&
isSameDay(range.end, selectedDates.end)) {
return option;
}
}
return null;
}
/**
* Updates the input and selected dates based on a selected option from the list.
*
* @param selectedOption The selected date option.
* @param input The HTML input element to update.
*/
updatedFromListValueSelection(selectedOption, input) {
// This will update value if option is selected from default list.
if (!selectedOption['callBackFunction']) {
this.updateDateOnOptionSelect(selectedOption, input);
return;
}
// This will update value if option is selected from provided custom list.
const dateRange = selectedOption.callBackFunction();
this.updateSelectedDates(input, dateRange.start ?? new Date(), dateRange.end ?? new Date(), selectedOption);
}
/**
* Checks whether default initialization of options is required.
*
* @returns True if default options need to be initialized, otherwise false.
*/
isDefaultInitRequired() {
return this.enableDefaultOptions && !this._dateOptions.length;
}
/**
* Initializes the default date options with the selected index.
*/
initDefaultOptions() {
const options = getClone(DEFAULT_DATE_OPTIONS).map((opt, idx) => ({
...opt,
isSelected: idx === this.selectedOptionIndex,
}));
this._dateOptions.set(options);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: NgDatePickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: NgDatePickerComponent, isStandalone: false, selector: "ng-date-range-picker", inputs: { inputLabel: "inputLabel", staticOptionId: "staticOptionId", dynamicOptionId: "dynamicOptionId", calendarId: "calendarId", enableDefaultOptions: "enableDefaultOptions", selectedDates: "selectedDates", dateFormat: "dateFormat", isShowStaticDefaultOptions: "isShowStaticDefaultOptions", hideDefaultOptions: "hideDefaultOptions", cdkConnectedOverlayOffsetX: "cdkConnectedOverlayOffsetX", cdkConnectedOverlayOffsetY: "cdkConnectedOverlayOffsetY", listCdkConnectedOverlayOffsetY: "listCdkConnectedOverlayOffsetY", listCdkConnectedOverlayOffsetX: "listCdkConnectedOverlayOffsetX", selectedOptionIndex: "selectedOptionIndex", displaySelectedLabel: "displaySelectedLabel", displaySelectedExpression: "displaySelectedExpression", enableEditableDates: "enableEditableDates", cdkConnectedOverlayPush: "cdkConnectedOverlayPush", cdkConnectedOverlayPositions: "cdkConnectedOverlayPositions", allowSingleDateSelection: "allowSingleDateSelection", autoSelectOption: "autoSelectOption", minDate: "minDate", maxDate: "maxDate", dateDropDownOptions: "dateDropDownOptions" }, outputs: { onDateSelectionChanged: "onDateSelectionChanged", dateListOptions: "dateListOptions" }, ngImport: i0, template: "<!--**\n * @(#)ng-date-picker.component.html Sept 05, 2023\n\n * @author Aakash Kumar\n *-->\n<div class=\"date-picker-main ndp-main\" cdkOverlayOrigin #trigger>\n <mat-form-field class=\"w-full\" [class]=\"{'display-hidden':isShowStaticDefaultOptions}\" (click)=\"toggleDateOptionSelectionList($event)\">\n <mat-label (click)=\"toggleDateOptionSelectionList($event)\">{{inputLabel}}</mat-label>\n <input matInput readonly=\"readonly\" #dateInput class=\"cursor-pointer\" id=\"date-input-field\">\n @if (!!dateInput.value) {\n <button mat-icon-button matSuffix class=\"cursor-pointer pe-0 ps-0\" matTooltip=\"Clear\"\n (click)=\"clearSelection($event)\">\n <mat-icon>clear</mat-icon>\n </button>\n }\n <button mat-icon-button matSuffix class=\"cursor-pointer\"> <mat-icon>date_range</mat-icon></button>\n </mat-form-field>\n\n @if(dateDropDownOptions.length && isShowStaticDefaultOptions) {\n <ng-container *ngTemplateOutlet=\"dateOptionList;\n context: {\n $implicit: visibleOptions(),\n dateInput: dateInput,\n optionId: staticOptionId,\n className:'w-full custom-ckd-container ndp-cdk-container range-input',\n }\"\n ></ng-container>\n }\n\n <ng-template cdkConnectedOverlay [cdkConnectedOverlayHasBackdrop]=\"false\" [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isDateOptionList\" [cdkConnectedOverlayPush]=\"cdkConnectedOverlayPush\"\n [cdkConnectedOverlayOffsetX]=\"listCdkConnectedOverlayOffsetX\"\n [cdkConnectedOverlayOffsetY]=\"listCdkConnectedOverlayOffsetY\"\n (overlayOutsideClick)=\"!isShowStaticDefaultOptions && toggleDateOptionSelectionList()\">\n\n @if(dateDropDownOptions.length && !isShowStaticDefaultOptions) {\n <ng-container *ngTemplateOutlet=\"dateOptionList;\n context: {\n $implicit: visibleOptions(),\n dateInput: dateInput,\n optionId: dynamicOptionId,\n className:'w-full custom-ckd-container ndp-cdk-container range-input',\n }\"\n ></ng-container>\n }\n </ng-template>\n\n <ng-template cdkConnectedOverlay [cdkConnectedOverlayHasBackdrop]=\"false\" [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isCustomRange\" [cdkConnectedOverlayPush]=\"cdkConnectedOverlayPush\"\n [cdkConnectedOverlayPositions]=\"cdkConnectedOverlayPositions\"\n [cdkConnectedOverlayOffsetX]=\"cdkConnectedOverlayOffsetX\" [cdkConnectedOverlayOffsetY]=\"cdkConnectedOverlayOffsetY\"\n (overlayOutsideClick)=\"toggleCustomDateRangeView()\">\n <div class=\"custom-ckd-container ndp-cdk-container custom-calendar-container ndp-calendar-container\" [class]=\"{'without-default-opt':hideDefaultOptions}\">\n <div class=\"row-1\">\n @if (!hideDefaultOptions) {\n <div class=\"pt-custom column-1\">\n <ng-container\n *ngTemplateOutlet=\"dateOptionList;\n context: {\n $implicit: visibleOptions(),\n dateInput: dateInput,\n }\"\n ></ng-container>\n </div>\n <div class=\"ndp-column-separator\"></div>\n }\n <div class=\"mt-2 column-2\"><lib-calendar [selectedDates]=\"selectedDates\" #calendar [minDate]=\"minDate\"\n [maxDate]=\"maxDate\" (selectedDatesChange)=\"onCalendarSelectionChange($event)\"></lib-calendar></div>\n </div>\n <div class=\"row-2 br-top\">\n <div class=\"footer-content ndp-footer-content\">\n @if (enableEditableDates) {\n <form class=\"ndp-date-inputs\" [formGroup]=\"editableForm\">\n <mat-form-field class=\"ndp-date-field\" subscriptSizing=\"dynamic\">\n <mat-label>Start</mat-label>\n <input matInput formControlName=\"start\" placeholder=\"e.g. now-7d\"\n (blur)=\"commitEditableDates(calendar)\"\n (keydown.enter)=\"applyEditableDates(dateInput, calendar)\">\n @if (editableForm.controls.start.hasError('required')) {\n <mat-error>Required</mat-error>\n } @else if (editableForm.controls.start.hasError('invalidDate')) {\n <mat-error>Invalid date or expression</mat-error>\n }\n </mat-form-field>\n <mat-form-field class=\"ndp-date-field\" subscriptSizing=\"dynamic\">\n <mat-label>End</mat-label>\n <input matInput formControlName=\"end\" placeholder=\"e.g. now\"\n (blur)=\"commitEditableDates(calendar)\"\n (keydown.enter)=\"applyEditableDates(dateInput, calendar)\">\n @if (editableForm.controls.end.hasError('required')) {\n <mat-error>Required</mat-error>\n } @else if (editableForm.controls.end.hasError('invalidDate')) {\n <mat-error>Invalid date or expression</mat-error>\n } @else if (editableForm.hasError('rangeOrder')) {\n <mat-error>Start must be before end</mat-error>\n }\n </mat-form-field>\n </form>\n } @else {\n <span id=\"range-label-text\" class=\"ndp-range-label-text\">\n {{calendar?.selectedDates?.start | date: dateFormat}}\n @if (calendar?.selectedDates?.end) {\n <span> - {{calendar.selectedDates?.end | date: dateFormat}} </span>\n }\n </span>\n }\n <div class=\"buttons\">\n <button mat-button mat-raised-button (click)=\"isCustomRange=false;\">Cancel</button>\n <button mat-button mat-raised-button color=\"primary\"\n [class.disabled]=\"enableEditableDates ? editableForm.invalid : !(calendar?.selectedDates?.start && calendar?.selectedDates?.end)\"\n (click)=\"enableEditableDates && commitEditableDates(calendar); updateCustomRange(dateInput,calendar.selectedDates);\"> Apply </button>\n </div>\n </div>\n </div>\n </div>\n </ng-template>\n</div>\n\n<ng-template #dateOptionList let-options let-input=\"dateInput\" let-optionId=\"optionId\" let-className=\"className\">\n <mat-action-list [ngClass]=\"className\" [id]=\"optionId\">\n @for (option of options; track option.optionLabel) {\n <mat-list-item [activated]=\"option.isSelected\" (click)=\"updateSelection(option, input)\">\n {{option.optionLabel}}\n </mat-list-item>\n }\n </mat-action-list>\n</ng-template>\n", styles: ["::ng-deep .cdk-overlay-dark-backdrop{background:none}mat-list-item{height:35px}mat-list-item.mdc-list-item--activated{background-color:var(--ndp-selected-option-bg, var(--mat-sys-secondary-container, rgba(0, 0, 0, .08)))}::ng-deep .cdk-overlay-pane:has(.custom-ckd-container),::ng-deep .cdk-overlay-pane:has(.ndp-cdk-container){width:100%;background-color:var(--bg-color, white);max-height:100vh;overflow-y:auto;overflow-x:hidden;max-width:700px;margin-top:-22px;border:1px solid var(--border-color, #ddd)}::ng-deep .cdk-overlay-pane:has(.range-input){max-width:250px}.br-top{border-top:1px solid var(--border-color, #ddd)}.br-right{border-right:1px solid var(--border-color, #ddd)}.disabled{pointer-events:none;opacity:.5}.mat-button,.mdc-button{font-family:var(--mat-list-list-item-label-text-font);line-height:var(--mat-list-list-item-label-text-line-height);font-size:var(--mat-list-list-item-label-text-size);font-weight:var(--mat-list-list-item-label-text-weight);letter-spacing:var(--mat-list-list-item-label-text-tracking)}.w-full{width:100%}.display-hidden{display:none}.custom-calendar-container,.ndp-calendar-container{width:100%}.row-1,.row-2{width:100%;box-sizing:border-box}.row-1{display:flex;align-items:stretch}.row-2{padding:16px}.ndp-column-separator{flex:0 0 1px;background-color:var(--border-color, #ddd)}.footer-content,.ndp-footer-content{align-items:center;display:flex;text-align:right;justify-content:end;gap:16px;text-overflow:ellipsis}.footer-content .buttons,.ndp-footer-content .buttons{display:flex;gap:8px}.ndp-date-inputs{display:flex;gap:8px;margin-right:auto;align-items:flex-start}.ndp-date-field{width:150px}.column-1{flex:0 0 25%;overflow:auto}.column-2{flex:1 1 auto;min-width:0}.without-default-opt .column-1{display:none}@media(max-width:400px){.footer-content,.ndp-footer-content{display:block}.footer-content .buttons,.ndp-footer-content .buttons{justify-content:flex-end;margin-top:20px}#range-label-text{margin-right:1.5rem}}@media(max-width:650px){.row-1{flex-direction:column}.column-1{flex-basis:auto}.column-separator{display:none}}.pe-0{padding-right:0}.ps-0{padding-left:0}\n"], dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: i4.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i4.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i6.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i6.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i7.MatActionList, selector: "mat-action-list", exportAs: ["matActionList"] }, { kind: "component", type: i7.MatListItem, selector: "mat-list-item, a[mat-list-item], button[mat-list-item]", inputs: ["activated"], exportAs: ["matListItem"] }, { kind: "directive", type: i8.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: CalendarComponent, selector: "lib-calendar", inputs: ["minDate", "maxDate", "selectedDates"], outputs: ["selectedDatesChange"] }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: NgDatePickerComponent, decorators: [{
type: Component,
args: [{ standalone: false, selector: 'ng-date-range-picker', changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--**\n * @(#)ng-date-picker.component.html Sept 05, 2023\n\n * @author Aakash Kumar\n *-->\n<div class=\"date-picker-main ndp-main\" cdkOverlayOrigin #trigger>\n <mat-form-field class=\"w-full\" [class]=\"{'display-hidden':isShowStaticDefaultOptions}\" (click)=\"toggleDateOptionSelectionList($event)\">\n <mat-label (click)=\"toggleDateOptionSelectionList($event)\">{{inputLabel}}</mat-label>\n <input matInput readonly=\"readonly\" #dateInput class=\"cursor-pointer\" id=\"date-input-field\">\n @if (!!dateInput.value) {\n <button mat-icon-button matSuffix class=\"cursor-pointer pe-0 ps-0\" matTooltip=\"Clear\"\n (click)=\"clearSelection($event)\">\n <mat-icon>clear</mat-icon>\n </button>\n }\n <button mat-icon-button matSuffix class=\"cursor-pointer\"> <mat-icon>date_range</mat-icon></button>\n </mat-form-field>\n\n @if(dateDropDownOptions.length && isShowStaticDefaultOptions) {\n <ng-container *ngTemplateOutlet=\"dateOptionList;\n context: {\n $implicit: visibleOptions(),\n dateInput: dateInput,\n optionId: staticOptionId,\n className:'w-full custom-ckd-container ndp-cdk-container range-input',\n }\"\n ></ng-container>\n }\n\n <ng-template cdkConnectedOverlay [cdkConnectedOverlayHasBackdrop]=\"false\" [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isDateOptionList\" [cdkConnectedOverlayPush]=\"cdkConnectedOverlayPush\"\n [cdkConnectedOverlayOffsetX]=\"listCdkConnectedOverlayOffsetX\"\n [cdkConnectedOverlayOffsetY]=\"listCdkConnectedOverlayOffsetY\"\n (overlayOutsideClick)=\"!isShowStaticDefaultOptions && toggleDateOptionSelectionList()\">\n\n @if(dateDropDownOptions.length && !isShowStaticDefaultOptions) {\n <ng-container *ngTemplateOutlet=\"dateOptionList;\n context: {\n $implicit: visibleOptions(),\n dateInput: dateInput,\n optionId: dynamicOptionId,\n className:'w-full custom-ckd-container ndp-cdk-container range-input',\n }\"\n ></ng-container>\n }\n </ng-template>\n\n <ng-template cdkConnectedOverlay [cdkConnectedOverlayHasBackdrop]=\"false\" [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isCustomRange\" [cdkConnectedOverlayPush]=\"cdkConnectedOverlayPush\"\n [cdkConnectedOverlayPositions]=\"cdkConnectedOverlayPositions\"\n [cdkConnectedOverlayOffsetX]=\"cdkConnectedOverlayOffsetX\" [cdkConnectedOverlayOffsetY]=\"cdkConnectedOverlayOffsetY\"\n (overlayOutsideClick)=\"toggleCustomDateRangeView()\">\n <div class=\"custom-ckd-container ndp-cdk-container custom-calendar-container ndp-calendar-container\" [class]=\"{'without-default-opt':hideDefaultOptions}\">\n <div class=\"row-1\">\n @if (!hideDefaultOptions) {\n <div class=\"pt-custom column-1\">\n <ng-container\n *ngTemplateOutlet=\"dateOptionList;\n context: {\n $implicit: visibleOptions(),\n dateInput: dateInput,\n }\"\n ></ng-container>\n </div>\n <div class=\"ndp-column-separator\"></div>\n }\n <div class=\"mt-2 column-2\"><lib-calendar [selectedDates]=\"selectedDates\" #calendar [minDate]=\"minDate\"\n [maxDate]=\"maxDate\" (selectedDatesChange)=\"onCalendarSelectionChange($event)\"></lib-calendar></div>\n </div>\n <div class=\"row-2 br-top\">\n <div class=\"footer-content ndp-footer-content\">\n @if (enableEditableDates) {\n <form class=\"ndp-date-inputs\" [formGroup]=\"editableForm\">\n <mat-form-field class=\"ndp-date-field\" subscriptSizing=\"dynamic\">\n <mat-label>Start</mat-label>\n <input matInput formControlName=\"start\" placeholder=\"e.g. now-7d\"\n (blur)=\"commitEditableDates(calendar)\"\n (keydown.enter)=\"applyEditableDates(dateInput, calendar)\">\n @if (editableForm.controls.start.hasError('required')) {\n <mat-error>Required</mat-error>\n } @else if (editableForm.controls.start.hasError('invalidDate')) {\n <mat-error>Invalid date or expression</mat-error>\n }\n </mat-form-field>\n <mat-form-field class=\"ndp-date-field\" subscriptSizing=\"dynamic\">\n <mat-label>End</mat-label>\n <input matInput formControlName=\"end\" placeholder=\"e.g. now\"\n (blur)=\"commitEditableDates(calendar)\"\n (keydown.enter)=\"applyEditableDates(dateInput, calendar)\">\n @if (editableForm.controls.end.hasError('required')) {\n <mat-error>Required</mat-error>\n } @else if (editableForm.controls.end.hasError('invalidDate')) {\n <mat-error>Invalid date or expression</mat-error>\n } @else if (editableForm.hasError('rangeOrder')) {\n <mat-error>Start must be before end</mat-error>\n }\n </mat-form-field>\n </form>\n } @else {\n <span id=\"range-label-text\" class=\"ndp-range-label-text\">\n {{calendar?.selectedDates?.start | date: dateFormat}}\n @if (calendar?.selectedDates?.end) {\n <span> - {{calendar.selectedDates?.end | date: dateFormat}} </span>\n }\n </span>\n }\n <div class=\"buttons\">\n <button mat-button mat-raised-button (click)=\"isCustomRange=false;\">Cancel</button>\n <button mat-button mat-raised-button color=\"primary\"\n [class.disabled]=\"enableEditableDates ? editableForm.invalid : !(calendar?.selectedDates?.start && calendar?.selectedDates?.end)\"\n (click)=\"enableEditableDates && commitEditableDates(calendar); updateCustomRange(dateInput,calendar.selectedDates);\"> Apply </button>\n </div>\n </div>\n </div>\n </div>\n </ng-template>\n</div>\n\n<ng-template #dateOptionList let-options let-input=\"dateInput\" let-optionId=\"optionId\" let-className=\"className\">\n <mat-action-list [ngClass]=\"className\" [id]=\"optionId\">\n @for (option of options; track option.optionLabel) {\n <mat-list-item [activated]=\"option.isSelected\" (click)=\"updateSelection(option, input)\">\n {{option.optionLabel}}\n </mat-list-item>\n }\n </mat-action-list>\n</ng-template>\n", styles: ["::ng-deep .cdk-overlay-dark-backdrop{background:none}mat-list-item{height:35px}mat-list-item.mdc-list-item--activated{background-color:var(--ndp-selected-option-bg, var(--mat-sys-secondary-container, rgba(0, 0, 0, .08)))}::ng-deep .cdk-overlay-pane:has(.custom-ckd-container),::ng-deep .cdk-overlay-pane:has(.ndp-cdk-container){width:100%;background-color:var(--bg-color, white);max-height:100vh;overflow-y:auto;overflow-x:hidden;max-width:700px;margin-top:-22px;border:1px solid var(--border-color, #ddd)}::ng-deep .cdk-overlay-pane:has(.range-input){max-width:250px}.br-top{border-top:1px solid var(--border-color, #ddd)}.br-right{border-right:1px solid var(--border-color, #ddd)}.disabled{pointer-events:none;opacity:.5}.mat-button,.mdc-button{font-family:var(--mat-list-list-item-label-text-font);line-height:var(--mat-list-list-item-label-text-line-height);font-size:var(--mat-list-list-item-label-text-size);font-weight:var(--mat-list-list-item-label-text-weight);letter-spacing:var(--mat-list-list-item-label-text-tracking)}.w-full{width:100%}.display-hidden{display:none}.custom-calendar-container,.ndp-calendar-container{width:100%}.row-1,.row-2{width:100%;box-sizing:border-box}.row-1{display:flex;align-items:stretch}.row-2{padding:16px}.ndp-column-separator{flex:0 0 1px;background-color:var(--border-color, #ddd)}.footer-content,.ndp-footer-content{align-items:center;display:flex;text-align:right;justify-content:end;gap:16px;text-overflow:ellipsis}.footer-content .buttons,.ndp-footer-content .buttons{display:flex;gap:8px}.ndp-date-inputs{display:flex;gap:8px;margin-right:auto;align-items:flex-start}.ndp-date-field{width:150px}.column-1{flex:0 0 25%;overflow:auto}.column-2{flex:1 1 auto;min-width:0}.without-default-opt .column-1{display:none}@media(max-width:400px){.footer-content,.ndp-footer-content{display:block}.footer-content .buttons,.ndp-footer-content .buttons{justify-content:flex-end;margin-top:20px}#range-label-text{margin-right:1.5rem}}@media(max-width:650px){.row-1{flex-direction:column}.column-1{flex-basis:auto}.column-separator{display:none}}.pe-0{padding-right:0}.ps-0{padding-left:0}\n"] }]
}], ctorParameters: () => [], propDecorators: { inputLabel: [{
type: Input
}], staticOptionId: [{
type: Input
}], dynamicOptionId: [{
type: Input
}], calendarId: [{
type: Input
}], enableDefaultOptions: [{
type: Input
}], selectedDates: [{
type: Input
}], dateFormat: [{
type: Input
}], isShowStaticDefaultOptions: [{
type: Input
}], hideDefaultOptions: [{
type: Input
}], cdkConnectedOverlayOffsetX: [{
type: Input
}], cdkConnectedOverlayOffsetY: [{
type: Input
}], listCdkConnectedOverlayOffsetY: [{
type: Input
}], listCdkConnectedOverlayOffsetX: [{
type: Input
}], selectedOptionIndex: [{
type: Input
}], displaySelectedLabel: [{
type: Input
}], displaySelectedExpression: [{
type: Input
}], enableEditableDates: [{
type: Input
}], cdkConnectedOverlayPush: [{
type: Input
}], cdkConnectedOverlayPositions: [{
type: Input
}], allowSingleDateSelection: [{
type: Input
}], autoSelectOption: [{
type: Input
}], minDate: [{
type: Input
}], maxDate: [{
type: Input
}], onDateSelectionChanged: [{
type: Output
}], dateListOptions: [{
type: Output
}], dateDropDownOptions: [{
type: Input
}] } });
/**
* Default implementation of a selectable date option.
* Provides default values for all fields.
*/
class SelectDateOption {
constructor() {
this.optionLabel = '';
this.optionType = DATE_OPTION_TYPE.DATE_DIFF;
this.dateDiff = 0;
this.isSelected = false;
this.isVisible = false;
}
}
/**
* @(#)ng-date-picker.module.ts Sept 05, 2023
*
* @author Aakash Kumar
*/
class NgDatePickerModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: NgDatePickerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.0.8", ngImport: i0, type: NgDatePickerModule, declarations: [NgDatePickerComponent, CalendarComponent], imports: [CommonModule,
FormsModule,
ReactiveFormsModule,
MatDatepickerModule,
MatNativeDateModule,
MatInputModule,
MatAutocompleteModule,
OverlayModule,
MatIconModule,
MatButtonModule,
MatListModule,
MatFormFieldModule,
MatTooltipModule], exports: [NgDatePickerComponent] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: NgDatePickerModule, imports: [CommonModule,
FormsModule,
ReactiveFormsModule,
MatDatepickerModule,
MatNativeDateModule,
MatInputModule,
MatAutocompleteModule,
OverlayModule,
MatIconModule,
MatButtonModule,
MatListModule,
MatFormFieldModule,
MatTooltipModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: NgDatePickerModule, decorators: [{
type: NgModule,
args: [{
declarations: [NgDatePickerComponent, CalendarComponent],
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
MatDatepickerModule,
MatNativeDateModule,
MatInputModule,
MatAutocompleteModule,
OverlayModule,
MatIconModule,
MatButtonModule,
MatListModule,
MatFormFieldModule,
MatTooltipModule,
],
exports: [NgDatePickerComponent],
}]
}] });
/**
* @(#)public-api.ts Sept 05, 2023
*
* @author Aakash Kumar
*/
// Public API Surface of ng-date-picker
/**
* Generated bundle index. Do not edit.
*/
export { ACTIVE_DATE_DEBOUNCE, DATE_OPTION_TYPE, NgDatePickerComponent, NgDatePickerModule, SelectDateOption, addDuration, parseDateMath, parseHumanDate, parseIso8601Duration, parseNaturalLanguage, setNaturalLanguageParser };
//# sourceMappingURL=ng-material-date-range-picker.mjs.map