UNPKG

ng-material-date-range-picker

Version:
1,476 lines 84.9 kB
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);\">&nbsp;Apply&nbsp;</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);\">&nbsp;Apply&nbsp;</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